xref: /petsc/src/dm/impls/plex/plex.c (revision 40cbb1a031ea8f2be4fe2b92dc842b003ad37be3)
1 #include <petsc/private/dmpleximpl.h>   /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/isimpl.h>
3 #include <petsc/private/vecimpl.h>
4 #include <petsc/private/glvisvecimpl.h>
5 #include <petscsf.h>
6 #include <petscds.h>
7 #include <petscdraw.h>
8 #include <petscdmfield.h>
9 #include <petscdmplextransform.h>
10 
11 /* Logging support */
12 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF,DMPLEX_LocatePoints,DMPLEX_TopologyView,DMPLEX_LabelsView,DMPLEX_CoordinatesView,DMPLEX_SectionView,DMPLEX_GlobalVectorView,DMPLEX_LocalVectorView,DMPLEX_TopologyLoad,DMPLEX_LabelsLoad,DMPLEX_CoordinatesLoad,DMPLEX_SectionLoad,DMPLEX_GlobalVectorLoad,DMPLEX_LocalVectorLoad;
13 
14 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
15 
16 /*@
17   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
18 
19   Input Parameter:
20 . dm      - The DMPlex object
21 
22   Output Parameter:
23 . simplex - Flag checking for a simplex
24 
25   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
26   If the mesh has no cells, this returns PETSC_FALSE.
27 
28   Level: intermediate
29 
30 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
31 @*/
32 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
33 {
34   DMPolytopeType ct;
35   PetscInt       cStart, cEnd;
36 
37   PetscFunctionBegin;
38   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
39   if (cEnd <= cStart) {*simplex = PETSC_FALSE; PetscFunctionReturn(0);}
40   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
41   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
42   PetscFunctionReturn(0);
43 }
44 
45 /*@
46   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
47 
48   Input Parameters:
49 + dm     - The DMPlex object
50 - height - The cell height in the Plex, 0 is the default
51 
52   Output Parameters:
53 + cStart - The first "normal" cell
54 - cEnd   - The upper bound on "normal"" cells
55 
56   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
57 
58   Level: developer
59 
60 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
61 @*/
62 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
63 {
64   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
65   PetscInt       cS, cE, c;
66 
67   PetscFunctionBegin;
68   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
69   for (c = cS; c < cE; ++c) {
70     DMPolytopeType cct;
71 
72     PetscCall(DMPlexGetCellType(dm, c, &cct));
73     if ((PetscInt) cct < 0) break;
74     switch (cct) {
75       case DM_POLYTOPE_POINT:
76       case DM_POLYTOPE_SEGMENT:
77       case DM_POLYTOPE_TRIANGLE:
78       case DM_POLYTOPE_QUADRILATERAL:
79       case DM_POLYTOPE_TETRAHEDRON:
80       case DM_POLYTOPE_HEXAHEDRON:
81         ct = cct;
82         break;
83       default: break;
84     }
85     if (ct != DM_POLYTOPE_UNKNOWN) break;
86   }
87   if (ct != DM_POLYTOPE_UNKNOWN) {
88     DMLabel ctLabel;
89 
90     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
91     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
92   }
93   if (cStart) *cStart = cS;
94   if (cEnd)   *cEnd   = cE;
95   PetscFunctionReturn(0);
96 }
97 
98 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
99 {
100   PetscInt       cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
101   PetscInt       vcdof[2] = {0,0}, globalvcdof[2];
102 
103   PetscFunctionBegin;
104   *ft  = PETSC_VTK_INVALID;
105   PetscCall(DMGetCoordinateDim(dm, &cdim));
106   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
107   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
108   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
109   if (field >= 0) {
110     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
111     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
112   } else {
113     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
114     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
115   }
116   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
117   if (globalvcdof[0]) {
118     *sStart = vStart;
119     *sEnd   = vEnd;
120     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
121     else                        *ft = PETSC_VTK_POINT_FIELD;
122   } else if (globalvcdof[1]) {
123     *sStart = cStart;
124     *sEnd   = cEnd;
125     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
126     else                        *ft = PETSC_VTK_CELL_FIELD;
127   } else {
128     if (field >= 0) {
129       const char *fieldname;
130 
131       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
132       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
133     } else {
134       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section\n"));
135     }
136   }
137   PetscFunctionReturn(0);
138 }
139 
140 /*@
141   DMPlexVecView1D - Plot many 1D solutions on the same line graph
142 
143   Collective on dm
144 
145   Input Parameters:
146 + dm - The DMPlex
147 . n  - The number of vectors
148 . u  - The array of local vectors
149 - viewer - The Draw viewer
150 
151   Level: advanced
152 
153 .seealso: `VecViewFromOptions()`, `VecView()`
154 @*/
155 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
156 {
157   PetscDS            ds;
158   PetscDraw          draw = NULL;
159   PetscDrawLG        lg;
160   Vec                coordinates;
161   const PetscScalar *coords, **sol;
162   PetscReal         *vals;
163   PetscInt          *Nc;
164   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
165   char             **names;
166 
167   PetscFunctionBegin;
168   PetscCall(DMGetDS(dm, &ds));
169   PetscCall(PetscDSGetNumFields(ds, &Nf));
170   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
171   PetscCall(PetscDSGetComponents(ds, &Nc));
172 
173   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
174   if (!draw) PetscFunctionReturn(0);
175   PetscCall(PetscDrawLGCreate(draw, n*Nl, &lg));
176 
177   PetscCall(PetscMalloc3(n, &sol, n*Nl, &names, n*Nl, &vals));
178   for (i = 0, l = 0; i < n; ++i) {
179     const char *vname;
180 
181     PetscCall(PetscObjectGetName((PetscObject) u[i], &vname));
182     for (f = 0; f < Nf; ++f) {
183       PetscObject disc;
184       const char *fname;
185       char        tmpname[PETSC_MAX_PATH_LEN];
186 
187       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
188       /* TODO Create names for components */
189       for (c = 0; c < Nc[f]; ++c, ++l) {
190         PetscCall(PetscObjectGetName(disc, &fname));
191         PetscCall(PetscStrcpy(tmpname, vname));
192         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
193         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
194         PetscCall(PetscStrallocpy(tmpname, &names[l]));
195       }
196     }
197   }
198   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *) names));
199   /* Just add P_1 support for now */
200   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
201   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
202   PetscCall(VecGetArrayRead(coordinates, &coords));
203   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
204   for (v = vStart; v < vEnd; ++v) {
205     PetscScalar *x, *svals;
206 
207     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
208     for (i = 0; i < n; ++i) {
209       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
210       for (l = 0; l < Nl; ++l) vals[i*Nl + l] = PetscRealPart(svals[l]);
211     }
212     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
213   }
214   PetscCall(VecRestoreArrayRead(coordinates, &coords));
215   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
216   for (l = 0; l < n*Nl; ++l) PetscCall(PetscFree(names[l]));
217   PetscCall(PetscFree3(sol, names, vals));
218 
219   PetscCall(PetscDrawLGDraw(lg));
220   PetscCall(PetscDrawLGDestroy(&lg));
221   PetscFunctionReturn(0);
222 }
223 
224 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
225 {
226   DM             dm;
227 
228   PetscFunctionBegin;
229   PetscCall(VecGetDM(u, &dm));
230   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
231   PetscFunctionReturn(0);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
235 {
236   DM                 dm;
237   PetscSection       s;
238   PetscDraw          draw, popup;
239   DM                 cdm;
240   PetscSection       coordSection;
241   Vec                coordinates;
242   const PetscScalar *coords, *array;
243   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
244   PetscReal          vbound[2], time;
245   PetscBool          flg;
246   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
247   const char        *name;
248   char               title[PETSC_MAX_PATH_LEN];
249 
250   PetscFunctionBegin;
251   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
252   PetscCall(VecGetDM(v, &dm));
253   PetscCall(DMGetCoordinateDim(dm, &dim));
254   PetscCall(DMGetLocalSection(dm, &s));
255   PetscCall(PetscSectionGetNumFields(s, &Nf));
256   PetscCall(DMGetCoarsenLevel(dm, &level));
257   PetscCall(DMGetCoordinateDM(dm, &cdm));
258   PetscCall(DMGetLocalSection(cdm, &coordSection));
259   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
260   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
261   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
262 
263   PetscCall(PetscObjectGetName((PetscObject) v, &name));
264   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
265 
266   PetscCall(VecGetLocalSize(coordinates, &N));
267   PetscCall(VecGetArrayRead(coordinates, &coords));
268   for (c = 0; c < N; c += dim) {
269     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
270     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
271   }
272   PetscCall(VecRestoreArrayRead(coordinates, &coords));
273   PetscCall(PetscDrawClear(draw));
274 
275   /* Could implement something like DMDASelectFields() */
276   for (f = 0; f < Nf; ++f) {
277     DM   fdm = dm;
278     Vec  fv  = v;
279     IS   fis;
280     char prefix[PETSC_MAX_PATH_LEN];
281     const char *fname;
282 
283     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
284     PetscCall(PetscSectionGetFieldName(s, f, &fname));
285 
286     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix,sizeof(prefix)));
287     else               {prefix[0] = '\0';}
288     if (Nf > 1) {
289       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
290       PetscCall(VecGetSubVector(v, fis, &fv));
291       PetscCall(PetscStrlcat(prefix, fname,sizeof(prefix)));
292       PetscCall(PetscStrlcat(prefix, "_",sizeof(prefix)));
293     }
294     for (comp = 0; comp < Nc; ++comp, ++w) {
295       PetscInt nmax = 2;
296 
297       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
298       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
299       else        PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
300       PetscCall(PetscDrawSetTitle(draw, title));
301 
302       /* TODO Get max and min only for this component */
303       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
304       if (!flg) {
305         PetscCall(VecMin(fv, NULL, &vbound[0]));
306         PetscCall(VecMax(fv, NULL, &vbound[1]));
307         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
308       }
309       PetscCall(PetscDrawGetPopup(draw, &popup));
310       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
311       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
312 
313       PetscCall(VecGetArrayRead(fv, &array));
314       for (c = cStart; c < cEnd; ++c) {
315         PetscScalar *coords = NULL, *a = NULL;
316         PetscInt     numCoords, color[4] = {-1,-1,-1,-1};
317 
318         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
319         if (a) {
320           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
321           color[1] = color[2] = color[3] = color[0];
322         } else {
323           PetscScalar *vals = NULL;
324           PetscInt     numVals, va;
325 
326           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
327           PetscCheck(numVals % Nc == 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
328           switch (numVals/Nc) {
329           case 3: /* P1 Triangle */
330           case 4: /* P1 Quadrangle */
331             for (va = 0; va < numVals/Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp]), vbound[0], vbound[1]);
332             break;
333           case 6: /* P2 Triangle */
334           case 8: /* P2 Quadrangle */
335             for (va = 0; va < numVals/(Nc*2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp + numVals/(Nc*2)]), vbound[0], vbound[1]);
336             break;
337           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals/Nc);
338           }
339           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
340         }
341         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
342         switch (numCoords) {
343         case 6:
344         case 12: /* Localized triangle */
345           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
346           break;
347         case 8:
348         case 16: /* Localized quadrilateral */
349           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
350           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
351           break;
352         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
353         }
354         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
355       }
356       PetscCall(VecRestoreArrayRead(fv, &array));
357       PetscCall(PetscDrawFlush(draw));
358       PetscCall(PetscDrawPause(draw));
359       PetscCall(PetscDrawSave(draw));
360     }
361     if (Nf > 1) {
362       PetscCall(VecRestoreSubVector(v, fis, &fv));
363       PetscCall(ISDestroy(&fis));
364       PetscCall(DMDestroy(&fdm));
365     }
366   }
367   PetscFunctionReturn(0);
368 }
369 
370 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
371 {
372   DM        dm;
373   PetscDraw draw;
374   PetscInt  dim;
375   PetscBool isnull;
376 
377   PetscFunctionBegin;
378   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
379   PetscCall(PetscDrawIsNull(draw, &isnull));
380   if (isnull) PetscFunctionReturn(0);
381 
382   PetscCall(VecGetDM(v, &dm));
383   PetscCall(DMGetCoordinateDim(dm, &dim));
384   switch (dim) {
385   case 1: PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));break;
386   case 2: PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));break;
387   default: SETERRQ(PetscObjectComm((PetscObject) v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
388   }
389   PetscFunctionReturn(0);
390 }
391 
392 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
393 {
394   DM                      dm;
395   Vec                     locv;
396   const char              *name;
397   PetscSection            section;
398   PetscInt                pStart, pEnd;
399   PetscInt                numFields;
400   PetscViewerVTKFieldType ft;
401 
402   PetscFunctionBegin;
403   PetscCall(VecGetDM(v, &dm));
404   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
405   PetscCall(PetscObjectGetName((PetscObject) v, &name));
406   PetscCall(PetscObjectSetName((PetscObject) locv, name));
407   PetscCall(VecCopy(v, locv));
408   PetscCall(DMGetLocalSection(dm, &section));
409   PetscCall(PetscSectionGetNumFields(section, &numFields));
410   if (!numFields) {
411     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
412     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE,(PetscObject) locv));
413   } else {
414     PetscInt f;
415 
416     for (f = 0; f < numFields; f++) {
417       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
418       if (ft == PETSC_VTK_INVALID) continue;
419       PetscCall(PetscObjectReference((PetscObject)locv));
420       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE,(PetscObject) locv));
421     }
422     PetscCall(VecDestroy(&locv));
423   }
424   PetscFunctionReturn(0);
425 }
426 
427 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
428 {
429   DM             dm;
430   PetscBool      isvtk, ishdf5, isdraw, isglvis;
431 
432   PetscFunctionBegin;
433   PetscCall(VecGetDM(v, &dm));
434   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
435   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk));
436   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5));
437   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw));
438   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis));
439   if (isvtk || ishdf5 || isdraw || isglvis) {
440     PetscInt    i,numFields;
441     PetscObject fe;
442     PetscBool   fem = PETSC_FALSE;
443     Vec         locv = v;
444     const char  *name;
445     PetscInt    step;
446     PetscReal   time;
447 
448     PetscCall(DMGetNumFields(dm, &numFields));
449     for (i=0; i<numFields; i++) {
450       PetscCall(DMGetField(dm, i, NULL, &fe));
451       if (fe->classid == PETSCFE_CLASSID) { fem = PETSC_TRUE; break; }
452     }
453     if (fem) {
454       PetscObject isZero;
455 
456       PetscCall(DMGetLocalVector(dm, &locv));
457       PetscCall(PetscObjectGetName((PetscObject) v, &name));
458       PetscCall(PetscObjectSetName((PetscObject) locv, name));
459       PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
460       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
461       PetscCall(VecCopy(v, locv));
462       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
463       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
464     }
465     if (isvtk) {
466       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
467     } else if (ishdf5) {
468 #if defined(PETSC_HAVE_HDF5)
469       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
470 #else
471       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
472 #endif
473     } else if (isdraw) {
474       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
475     } else if (isglvis) {
476       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
477       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
478       PetscCall(VecView_GLVis(locv, viewer));
479     }
480     if (fem) {
481       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
482       PetscCall(DMRestoreLocalVector(dm, &locv));
483     }
484   } else {
485     PetscBool isseq;
486 
487     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
488     if (isseq) PetscCall(VecView_Seq(v, viewer));
489     else       PetscCall(VecView_MPI(v, viewer));
490   }
491   PetscFunctionReturn(0);
492 }
493 
494 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
495 {
496   DM        dm;
497   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii;
498 
499   PetscFunctionBegin;
500   PetscCall(VecGetDM(v, &dm));
501   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
502   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
503   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
504   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
505   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
506   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
507   if (isvtk || isdraw || isglvis) {
508     Vec         locv;
509     PetscObject isZero;
510     const char *name;
511 
512     PetscCall(DMGetLocalVector(dm, &locv));
513     PetscCall(PetscObjectGetName((PetscObject) v, &name));
514     PetscCall(PetscObjectSetName((PetscObject) locv, name));
515     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
516     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
517     PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
518     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
519     PetscCall(VecView_Plex_Local(locv, viewer));
520     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
521     PetscCall(DMRestoreLocalVector(dm, &locv));
522   } else if (ishdf5) {
523 #if defined(PETSC_HAVE_HDF5)
524     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
525 #else
526     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
527 #endif
528   } else if (isexodusii) {
529 #if defined(PETSC_HAVE_EXODUSII)
530     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
531 #else
532     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
533 #endif
534   } else {
535     PetscBool isseq;
536 
537     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
538     if (isseq) PetscCall(VecView_Seq(v, viewer));
539     else       PetscCall(VecView_MPI(v, viewer));
540   }
541   PetscFunctionReturn(0);
542 }
543 
544 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
545 {
546   DM                dm;
547   MPI_Comm          comm;
548   PetscViewerFormat format;
549   Vec               v;
550   PetscBool         isvtk, ishdf5;
551 
552   PetscFunctionBegin;
553   PetscCall(VecGetDM(originalv, &dm));
554   PetscCall(PetscObjectGetComm((PetscObject) originalv, &comm));
555   PetscCheck(dm,comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
556   PetscCall(PetscViewerGetFormat(viewer, &format));
557   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
558   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,  &isvtk));
559   if (format == PETSC_VIEWER_NATIVE) {
560     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
561     /* this need a better fix */
562     if (dm->useNatural) {
563       if (dm->sfNatural) {
564         const char *vecname;
565         PetscInt    n, nroots;
566 
567         PetscCall(VecGetLocalSize(originalv, &n));
568         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
569         if (n == nroots) {
570           PetscCall(DMGetGlobalVector(dm, &v));
571           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
572           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
573           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
574           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
575         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
576       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
577     } else v = originalv;
578   } else v = originalv;
579 
580   if (ishdf5) {
581 #if defined(PETSC_HAVE_HDF5)
582     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
583 #else
584     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
585 #endif
586   } else if (isvtk) {
587     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
588   } else {
589     PetscBool isseq;
590 
591     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
592     if (isseq) PetscCall(VecView_Seq(v, viewer));
593     else       PetscCall(VecView_MPI(v, viewer));
594   }
595   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
596   PetscFunctionReturn(0);
597 }
598 
599 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
600 {
601   DM             dm;
602   PetscBool      ishdf5;
603 
604   PetscFunctionBegin;
605   PetscCall(VecGetDM(v, &dm));
606   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
607   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
608   if (ishdf5) {
609     DM          dmBC;
610     Vec         gv;
611     const char *name;
612 
613     PetscCall(DMGetOutputDM(dm, &dmBC));
614     PetscCall(DMGetGlobalVector(dmBC, &gv));
615     PetscCall(PetscObjectGetName((PetscObject) v, &name));
616     PetscCall(PetscObjectSetName((PetscObject) gv, name));
617     PetscCall(VecLoad_Default(gv, viewer));
618     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
619     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
620     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
621   } else {
622     PetscCall(VecLoad_Default(v, viewer));
623   }
624   PetscFunctionReturn(0);
625 }
626 
627 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
628 {
629   DM             dm;
630   PetscBool      ishdf5,isexodusii;
631 
632   PetscFunctionBegin;
633   PetscCall(VecGetDM(v, &dm));
634   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
635   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
636   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
637   if (ishdf5) {
638 #if defined(PETSC_HAVE_HDF5)
639     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
640 #else
641     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
642 #endif
643   } else if (isexodusii) {
644 #if defined(PETSC_HAVE_EXODUSII)
645     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
646 #else
647     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
648 #endif
649   } else {
650     PetscCall(VecLoad_Default(v, viewer));
651   }
652   PetscFunctionReturn(0);
653 }
654 
655 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
656 {
657   DM                dm;
658   PetscViewerFormat format;
659   PetscBool         ishdf5;
660 
661   PetscFunctionBegin;
662   PetscCall(VecGetDM(originalv, &dm));
663   PetscCheck(dm,PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
664   PetscCall(PetscViewerGetFormat(viewer, &format));
665   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
666   if (format == PETSC_VIEWER_NATIVE) {
667     if (dm->useNatural) {
668       if (dm->sfNatural) {
669         if (ishdf5) {
670 #if defined(PETSC_HAVE_HDF5)
671           Vec         v;
672           const char *vecname;
673 
674           PetscCall(DMGetGlobalVector(dm, &v));
675           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
676           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
677           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
678           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
679           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
680           PetscCall(DMRestoreGlobalVector(dm, &v));
681 #else
682           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
683 #endif
684         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
685       }
686     } else {
687       PetscCall(VecLoad_Default(originalv, viewer));
688     }
689   }
690   PetscFunctionReturn(0);
691 }
692 
693 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
694 {
695   PetscSection       coordSection;
696   Vec                coordinates;
697   DMLabel            depthLabel, celltypeLabel;
698   const char        *name[4];
699   const PetscScalar *a;
700   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
701 
702   PetscFunctionBegin;
703   PetscCall(DMGetDimension(dm, &dim));
704   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
705   PetscCall(DMGetCoordinateSection(dm, &coordSection));
706   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
707   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
708   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
709   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
710   PetscCall(VecGetArrayRead(coordinates, &a));
711   name[0]     = "vertex";
712   name[1]     = "edge";
713   name[dim-1] = "face";
714   name[dim]   = "cell";
715   for (c = cStart; c < cEnd; ++c) {
716     PetscInt *closure = NULL;
717     PetscInt  closureSize, cl, ct;
718 
719     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
720     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
721     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
722     PetscCall(PetscViewerASCIIPushTab(viewer));
723     for (cl = 0; cl < closureSize*2; cl += 2) {
724       PetscInt point = closure[cl], depth, dof, off, d, p;
725 
726       if ((point < pStart) || (point >= pEnd)) continue;
727       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
728       if (!dof) continue;
729       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
730       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
731       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
732       for (p = 0; p < dof/dim; ++p) {
733         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
734         for (d = 0; d < dim; ++d) {
735           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
736           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d])));
737         }
738         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
739       }
740       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
741     }
742     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
743     PetscCall(PetscViewerASCIIPopTab(viewer));
744   }
745   PetscCall(VecRestoreArrayRead(coordinates, &a));
746   PetscFunctionReturn(0);
747 }
748 
749 typedef enum {CS_CARTESIAN, CS_POLAR, CS_CYLINDRICAL, CS_SPHERICAL} CoordSystem;
750 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
751 
752 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
753 {
754   PetscInt       i;
755 
756   PetscFunctionBegin;
757   if (dim > 3) {
758     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) PetscRealPart(x[i])));
759   } else {
760     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
761 
762     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
763     switch (cs) {
764       case CS_CARTESIAN: for (i = 0; i < dim; ++i) trcoords[i] = coords[i];break;
765       case CS_POLAR:
766         PetscCheck(dim == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
767         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
768         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
769         break;
770       case CS_CYLINDRICAL:
771         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
772         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
773         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
774         trcoords[2] = coords[2];
775         break;
776       case CS_SPHERICAL:
777         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
778         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
779         trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
780         trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
781         break;
782     }
783     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) trcoords[i]));
784   }
785   PetscFunctionReturn(0);
786 }
787 
788 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
789 {
790   DM_Plex          *mesh = (DM_Plex*) dm->data;
791   DM                cdm;
792   PetscSection      coordSection;
793   Vec               coordinates;
794   PetscViewerFormat format;
795 
796   PetscFunctionBegin;
797   PetscCall(DMGetCoordinateDM(dm, &cdm));
798   PetscCall(DMGetLocalSection(cdm, &coordSection));
799   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
800   PetscCall(PetscViewerGetFormat(viewer, &format));
801   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
802     const char *name;
803     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
804     PetscInt    pStart, pEnd, p, numLabels, l;
805     PetscMPIInt rank, size;
806 
807     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
808     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
809     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
810     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
811     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
812     PetscCall(DMGetDimension(dm, &dim));
813     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
814     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
815     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
816     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
817     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
818     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
819     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
820     for (p = pStart; p < pEnd; ++p) {
821       PetscInt dof, off, s;
822 
823       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
824       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
825       for (s = off; s < off+dof; ++s) {
826         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
827       }
828     }
829     PetscCall(PetscViewerFlush(viewer));
830     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
831     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
832     for (p = pStart; p < pEnd; ++p) {
833       PetscInt dof, off, c;
834 
835       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
836       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
837       for (c = off; c < off+dof; ++c) {
838         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
839       }
840     }
841     PetscCall(PetscViewerFlush(viewer));
842     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
843     if (coordSection && coordinates) {
844       CoordSystem        cs = CS_CARTESIAN;
845       const PetscScalar *array;
846       PetscInt           Nf, Nc, pStart, pEnd, p;
847       PetscMPIInt        rank;
848       const char        *name;
849 
850       PetscCall(PetscOptionsGetEnum(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *) &cs, NULL));
851       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
852       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
853       PetscCheck(Nf == 1,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
854       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
855       PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
856       PetscCall(PetscObjectGetName((PetscObject) coordinates, &name));
857       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
858       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
859       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
860 
861       PetscCall(VecGetArrayRead(coordinates, &array));
862       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
863       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
864       for (p = pStart; p < pEnd; ++p) {
865         PetscInt dof, off;
866 
867         PetscCall(PetscSectionGetDof(coordSection, p, &dof));
868         PetscCall(PetscSectionGetOffset(coordSection, p, &off));
869         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
870         PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
871         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
872       }
873       PetscCall(PetscViewerFlush(viewer));
874       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
875       PetscCall(VecRestoreArrayRead(coordinates, &array));
876     }
877     PetscCall(DMGetNumLabels(dm, &numLabels));
878     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
879     for (l = 0; l < numLabels; ++l) {
880       DMLabel     label;
881       PetscBool   isdepth;
882       const char *name;
883 
884       PetscCall(DMGetLabelName(dm, l, &name));
885       PetscCall(PetscStrcmp(name, "depth", &isdepth));
886       if (isdepth) continue;
887       PetscCall(DMGetLabel(dm, name, &label));
888       PetscCall(DMLabelView(label, viewer));
889     }
890     if (size > 1) {
891       PetscSF sf;
892 
893       PetscCall(DMGetPointSF(dm, &sf));
894       PetscCall(PetscSFView(sf, viewer));
895     }
896     PetscCall(PetscViewerFlush(viewer));
897   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
898     const char  *name, *color;
899     const char  *defcolors[3]  = {"gray", "orange", "green"};
900     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
901     char         lname[PETSC_MAX_PATH_LEN];
902     PetscReal    scale         = 2.0;
903     PetscReal    tikzscale     = 1.0;
904     PetscBool    useNumbers    = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
905     double       tcoords[3];
906     PetscScalar *coords;
907     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
908     PetscMPIInt  rank, size;
909     char         **names, **colors, **lcolors;
910     PetscBool    flg, lflg;
911     PetscBT      wp = NULL;
912     PetscInt     pEnd, pStart;
913 
914     PetscCall(DMGetDimension(dm, &dim));
915     PetscCall(DMPlexGetDepth(dm, &depth));
916     PetscCall(DMGetNumLabels(dm, &numLabels));
917     numLabels  = PetscMax(numLabels, 10);
918     numColors  = 10;
919     numLColors = 10;
920     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
921     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
922     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
923     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
924     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
925     for (d = 0; d < 4; ++d) drawColors[d]  = PETSC_TRUE;
926     n = 4;
927     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
928     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
929     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
930     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
931     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
932     if (!useLabels) numLabels = 0;
933     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
934     if (!useColors) {
935       numColors = 3;
936       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
937     }
938     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
939     if (!useColors) {
940       numLColors = 4;
941       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
942     }
943     PetscCall(PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
944     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
945     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
946     PetscCheck(!flg || !plotEdges || depth >= dim,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
947     if (depth < dim) plotEdges = PETSC_FALSE;
948     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
949 
950     /* filter points with labelvalue != labeldefaultvalue */
951     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
952     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
953     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
954     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
955     if (lflg) {
956       DMLabel lbl;
957 
958       PetscCall(DMGetLabel(dm, lname, &lbl));
959       if (lbl) {
960         PetscInt val, defval;
961 
962         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
963         PetscCall(PetscBTCreate(pEnd-pStart, &wp));
964         for (c = pStart;  c < pEnd; c++) {
965           PetscInt *closure = NULL;
966           PetscInt  closureSize;
967 
968           PetscCall(DMLabelGetValue(lbl, c, &val));
969           if (val == defval) continue;
970 
971           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
972           for (p = 0; p < closureSize*2; p += 2) {
973             PetscCall(PetscBTSet(wp, closure[p] - pStart));
974           }
975           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
976         }
977       }
978     }
979 
980     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
981     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
982     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
983     PetscCall(PetscViewerASCIIPrintf(viewer, "\
984 \\documentclass[tikz]{standalone}\n\n\
985 \\usepackage{pgflibraryshapes}\n\
986 \\usetikzlibrary{backgrounds}\n\
987 \\usetikzlibrary{arrows}\n\
988 \\begin{document}\n"));
989     if (size > 1) {
990       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
991       for (p = 0; p < size; ++p) {
992         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size-1) ? ", and " :  ", "));
993         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p%numColors], p));
994       }
995       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
996     }
997     if (drawHasse) {
998       PetscInt maxStratum = PetscMax(vEnd-vStart, PetscMax(eEnd-eStart, cEnd-cStart));
999 
1000       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1001       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd-1));
1002       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd-vStart));
1003       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum-(vEnd-vStart))/2.));
1004       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1005       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd-1));
1006       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum-(eEnd-eStart))/2.));
1007       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd-eStart));
1008       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1009       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd-1));
1010       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd-cStart));
1011       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum-(cEnd-cStart))/2.));
1012     }
1013     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale));
1014 
1015     /* Plot vertices */
1016     PetscCall(VecGetArray(coordinates, &coords));
1017     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1018     for (v = vStart; v < vEnd; ++v) {
1019       PetscInt  off, dof, d;
1020       PetscBool isLabeled = PETSC_FALSE;
1021 
1022       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
1023       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1024       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1025       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1026       PetscCheck(dof <= 3,PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3",v,dof);
1027       for (d = 0; d < dof; ++d) {
1028         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1029         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1030       }
1031       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1032       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1033       for (d = 0; d < dof; ++d) {
1034         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1035         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]));
1036       }
1037       if (drawHasse) color = colors[0%numColors];
1038       else           color = colors[rank%numColors];
1039       for (l = 0; l < numLabels; ++l) {
1040         PetscInt val;
1041         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1042         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1043       }
1044       if (drawNumbers[0]) {
1045         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1046       } else if (drawColors[0]) {
1047         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1048       } else {
1049         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1050       }
1051     }
1052     PetscCall(VecRestoreArray(coordinates, &coords));
1053     PetscCall(PetscViewerFlush(viewer));
1054     /* Plot edges */
1055     if (plotEdges) {
1056       PetscCall(VecGetArray(coordinates, &coords));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1058       for (e = eStart; e < eEnd; ++e) {
1059         const PetscInt *cone;
1060         PetscInt        coneSize, offA, offB, dof, d;
1061 
1062         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1063         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1064         PetscCheck(coneSize == 2,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1065         PetscCall(DMPlexGetCone(dm, e, &cone));
1066         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1067         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1068         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1069         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1070         for (d = 0; d < dof; ++d) {
1071           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
1072           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1073         }
1074         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1075         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1076         for (d = 0; d < dof; ++d) {
1077           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1078           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1079         }
1080         if (drawHasse) color = colors[1%numColors];
1081         else           color = colors[rank%numColors];
1082         for (l = 0; l < numLabels; ++l) {
1083           PetscInt val;
1084           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1085           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1086         }
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1088       }
1089       PetscCall(VecRestoreArray(coordinates, &coords));
1090       PetscCall(PetscViewerFlush(viewer));
1091       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1092     }
1093     /* Plot cells */
1094     if (dim == 3 || !drawNumbers[1]) {
1095       for (e = eStart; e < eEnd; ++e) {
1096         const PetscInt *cone;
1097 
1098         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1099         color = colors[rank%numColors];
1100         for (l = 0; l < numLabels; ++l) {
1101           PetscInt val;
1102           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1103           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1104         }
1105         PetscCall(DMPlexGetCone(dm, e, &cone));
1106         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1107       }
1108     } else {
1109        DMPolytopeType ct;
1110 
1111       /* Drawing a 2D polygon */
1112       for (c = cStart; c < cEnd; ++c) {
1113         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1114         PetscCall(DMPlexGetCellType(dm, c, &ct));
1115         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR ||
1116             ct == DM_POLYTOPE_TRI_PRISM_TENSOR ||
1117             ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1118           const PetscInt *cone;
1119           PetscInt        coneSize, e;
1120 
1121           PetscCall(DMPlexGetCone(dm, c, &cone));
1122           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1123           for (e = 0; e < coneSize; ++e) {
1124             const PetscInt *econe;
1125 
1126             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1127             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank%numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1128           }
1129         } else {
1130           PetscInt *closure = NULL;
1131           PetscInt  closureSize, Nv = 0, v;
1132 
1133           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1134           for (p = 0; p < closureSize*2; p += 2) {
1135             const PetscInt point = closure[p];
1136 
1137             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1138           }
1139           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]));
1140           for (v = 0; v <= Nv; ++v) {
1141             const PetscInt vertex = closure[v%Nv];
1142 
1143             if (v > 0) {
1144               if (plotEdges) {
1145                 const PetscInt *edge;
1146                 PetscInt        endpoints[2], ne;
1147 
1148                 endpoints[0] = closure[v-1]; endpoints[1] = vertex;
1149                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1150                 PetscCheck(ne == 1,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1151                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1152                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1153               } else {
1154                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1155               }
1156             }
1157             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1158           }
1159           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1160           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1161         }
1162       }
1163     }
1164     PetscCall(VecGetArray(coordinates, &coords));
1165     for (c = cStart; c < cEnd; ++c) {
1166       double    ccoords[3] = {0.0, 0.0, 0.0};
1167       PetscBool isLabeled  = PETSC_FALSE;
1168       PetscInt *closure    = NULL;
1169       PetscInt  closureSize, dof, d, n = 0;
1170 
1171       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
1172       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1173       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1174       for (p = 0; p < closureSize*2; p += 2) {
1175         const PetscInt point = closure[p];
1176         PetscInt       off;
1177 
1178         if ((point < vStart) || (point >= vEnd)) continue;
1179         PetscCall(PetscSectionGetDof(coordSection, point, &dof));
1180         PetscCall(PetscSectionGetOffset(coordSection, point, &off));
1181         for (d = 0; d < dof; ++d) {
1182           tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1183           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1184         }
1185         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1186         if (dof == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1187         for (d = 0; d < dof; ++d) {ccoords[d] += tcoords[d];}
1188         ++n;
1189       }
1190       for (d = 0; d < dof; ++d) {ccoords[d] /= n;}
1191       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1192       for (d = 0; d < dof; ++d) {
1193         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1194         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]));
1195       }
1196       if (drawHasse) color = colors[depth%numColors];
1197       else           color = colors[rank%numColors];
1198       for (l = 0; l < numLabels; ++l) {
1199         PetscInt val;
1200         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1201         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1202       }
1203       if (drawNumbers[dim]) {
1204         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1205       } else if (drawColors[dim]) {
1206         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1207       } else {
1208         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1209       }
1210     }
1211     PetscCall(VecRestoreArray(coordinates, &coords));
1212     if (drawHasse) {
1213       color = colors[depth%numColors];
1214       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1215       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1216       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1217       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1218       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1219 
1220       color = colors[1%numColors];
1221       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1222       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1223       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1224       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1225       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1226 
1227       color = colors[0%numColors];
1228       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1229       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1230       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1231       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1232       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1233 
1234       for (p = pStart; p < pEnd; ++p) {
1235         const PetscInt *cone;
1236         PetscInt        coneSize, cp;
1237 
1238         PetscCall(DMPlexGetCone(dm, p, &cone));
1239         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1240         for (cp = 0; cp < coneSize; ++cp) {
1241           PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1242         }
1243       }
1244     }
1245     PetscCall(PetscViewerFlush(viewer));
1246     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1247     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1248     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1249     for (l = 0; l < numLabels;  ++l) PetscCall(PetscFree(names[l]));
1250     for (c = 0; c < numColors;  ++c) PetscCall(PetscFree(colors[c]));
1251     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1252     PetscCall(PetscFree3(names, colors, lcolors));
1253     PetscCall(PetscBTDestroy(&wp));
1254   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1255     Vec                    cown,acown;
1256     VecScatter             sct;
1257     ISLocalToGlobalMapping g2l;
1258     IS                     gid,acis;
1259     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
1260     MPI_Group              ggroup,ngroup;
1261     PetscScalar            *array,nid;
1262     const PetscInt         *idxs;
1263     PetscInt               *idxs2,*start,*adjacency,*work;
1264     PetscInt64             lm[3],gm[3];
1265     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
1266     PetscMPIInt            d1,d2,rank;
1267 
1268     PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
1269     PetscCallMPI(MPI_Comm_rank(comm,&rank));
1270 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1271     PetscCallMPI(MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm));
1272 #endif
1273     if (ncomm != MPI_COMM_NULL) {
1274       PetscCallMPI(MPI_Comm_group(comm,&ggroup));
1275       PetscCallMPI(MPI_Comm_group(ncomm,&ngroup));
1276       d1   = 0;
1277       PetscCallMPI(MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2));
1278       nid  = d2;
1279       PetscCallMPI(MPI_Group_free(&ggroup));
1280       PetscCallMPI(MPI_Group_free(&ngroup));
1281       PetscCallMPI(MPI_Comm_free(&ncomm));
1282     } else nid = 0.0;
1283 
1284     /* Get connectivity */
1285     PetscCall(DMPlexGetVTKCellHeight(dm,&cellHeight));
1286     PetscCall(DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid));
1287 
1288     /* filter overlapped local cells */
1289     PetscCall(DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd));
1290     PetscCall(ISGetIndices(gid,&idxs));
1291     PetscCall(ISGetLocalSize(gid,&cum));
1292     PetscCall(PetscMalloc1(cum,&idxs2));
1293     for (c = cStart, cum = 0; c < cEnd; c++) {
1294       if (idxs[c-cStart] < 0) continue;
1295       idxs2[cum++] = idxs[c-cStart];
1296     }
1297     PetscCall(ISRestoreIndices(gid,&idxs));
1298     PetscCheck(numVertices == cum,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %" PetscInt_FMT " != %" PetscInt_FMT,numVertices,cum);
1299     PetscCall(ISDestroy(&gid));
1300     PetscCall(ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid));
1301 
1302     /* support for node-aware cell locality */
1303     PetscCall(ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis));
1304     PetscCall(VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown));
1305     PetscCall(VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown));
1306     PetscCall(VecGetArray(cown,&array));
1307     for (c = 0; c < numVertices; c++) array[c] = nid;
1308     PetscCall(VecRestoreArray(cown,&array));
1309     PetscCall(VecScatterCreate(cown,acis,acown,NULL,&sct));
1310     PetscCall(VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1311     PetscCall(VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1312     PetscCall(ISDestroy(&acis));
1313     PetscCall(VecScatterDestroy(&sct));
1314     PetscCall(VecDestroy(&cown));
1315 
1316     /* compute edgeCut */
1317     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
1318     PetscCall(PetscMalloc1(cum,&work));
1319     PetscCall(ISLocalToGlobalMappingCreateIS(gid,&g2l));
1320     PetscCall(ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH));
1321     PetscCall(ISDestroy(&gid));
1322     PetscCall(VecGetArray(acown,&array));
1323     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1324       PetscInt totl;
1325 
1326       totl = start[c+1]-start[c];
1327       PetscCall(ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work));
1328       for (i = 0; i < totl; i++) {
1329         if (work[i] < 0) {
1330           ect  += 1;
1331           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1332         }
1333       }
1334     }
1335     PetscCall(PetscFree(work));
1336     PetscCall(VecRestoreArray(acown,&array));
1337     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1338     lm[1] = -numVertices;
1339     PetscCall(MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm));
1340     PetscCall(PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT,-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]));
1341     lm[0] = ect; /* edgeCut */
1342     lm[1] = ectn; /* node-aware edgeCut */
1343     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1344     PetscCall(MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm));
1345     PetscCall(PetscViewerASCIIPrintf(viewer,", empty %" PetscInt_FMT ")\n",(PetscInt)gm[2]));
1346 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1347     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),gm[0] ? ((double)(gm[1]))/((double)gm[0]) : 1.));
1348 #else
1349     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0));
1350 #endif
1351     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1352     PetscCall(PetscFree(start));
1353     PetscCall(PetscFree(adjacency));
1354     PetscCall(VecDestroy(&acown));
1355   } else {
1356     const char    *name;
1357     PetscInt      *sizes, *hybsizes, *ghostsizes;
1358     PetscInt       locDepth, depth, cellHeight, dim, d;
1359     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1360     PetscInt       numLabels, l, maxSize = 17;
1361     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1362     MPI_Comm       comm;
1363     PetscMPIInt    size, rank;
1364 
1365     PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
1366     PetscCallMPI(MPI_Comm_size(comm, &size));
1367     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1368     PetscCall(DMGetDimension(dm, &dim));
1369     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1370     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1371     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1372     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1373     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1374     PetscCall(DMPlexGetDepth(dm, &locDepth));
1375     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1376     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1377     gcNum = gcEnd - gcStart;
1378     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1379     else                PetscCall(PetscCalloc3(3,    &sizes, 3,    &hybsizes, 3,    &ghostsizes));
1380     for (d = 0; d <= depth; d++) {
1381       PetscInt Nc[2] = {0, 0}, ict;
1382 
1383       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1384       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1385       ict  = ct0;
1386       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1387       ct0  = (DMPolytopeType) ict;
1388       for (p = pStart; p < pEnd; ++p) {
1389         DMPolytopeType ct;
1390 
1391         PetscCall(DMPlexGetCellType(dm, p, &ct));
1392         if (ct == ct0) ++Nc[0];
1393         else           ++Nc[1];
1394       }
1395       if (size < maxSize) {
1396         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm));
1397         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1398         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1399         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1400         for (p = 0; p < size; ++p) {
1401           if (rank == 0) {
1402             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p]+hybsizes[p]));
1403             if (hybsizes[p]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1404             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1405           }
1406         }
1407       } else {
1408         PetscInt locMinMax[2];
1409 
1410         locMinMax[0] = Nc[0]+Nc[1]; locMinMax[1] = Nc[0]+Nc[1];
1411         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1412         locMinMax[0] = Nc[1]; locMinMax[1] = Nc[1];
1413         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1414         if (d == depth) {
1415           locMinMax[0] = gcNum; locMinMax[1] = gcNum;
1416           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1417         }
1418         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1419         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1420         if (hybsizes[0]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1421         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1422       }
1423       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1424     }
1425     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1426     {
1427       const PetscReal      *maxCell;
1428       const PetscReal      *L;
1429       const DMBoundaryType *bd;
1430       PetscBool             per, localized;
1431 
1432       PetscCall(DMGetPeriodicity(dm, &per, &maxCell, &L, &bd));
1433       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1434       if (per) {
1435         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh ("));
1436         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1437         for (d = 0; d < dim; ++d) {
1438           if (bd && d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1439           if (bd)    PetscCall(PetscViewerASCIIPrintf(viewer, "%s", DMBoundaryTypes[bd[d]]));
1440         }
1441         PetscCall(PetscViewerASCIIPrintf(viewer, ") coordinates %s\n", localized ? "localized" : "not localized"));
1442         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1443       }
1444     }
1445     PetscCall(DMGetNumLabels(dm, &numLabels));
1446     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1447     for (l = 0; l < numLabels; ++l) {
1448       DMLabel         label;
1449       const char     *name;
1450       IS              valueIS;
1451       const PetscInt *values;
1452       PetscInt        numValues, v;
1453 
1454       PetscCall(DMGetLabelName(dm, l, &name));
1455       PetscCall(DMGetLabel(dm, name, &label));
1456       PetscCall(DMLabelGetNumValues(label, &numValues));
1457       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1458       PetscCall(DMLabelGetValueIS(label, &valueIS));
1459       PetscCall(ISGetIndices(valueIS, &values));
1460       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1461       for (v = 0; v < numValues; ++v) {
1462         PetscInt size;
1463 
1464         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1465         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1466         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1467       }
1468       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1469       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1470       PetscCall(ISRestoreIndices(valueIS, &values));
1471       PetscCall(ISDestroy(&valueIS));
1472     }
1473     {
1474       char    **labelNames;
1475       PetscInt  Nl = numLabels;
1476       PetscBool flg;
1477 
1478       PetscCall(PetscMalloc1(Nl, &labelNames));
1479       PetscCall(PetscOptionsGetStringArray(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1480       for (l = 0; l < Nl; ++l) {
1481         DMLabel label;
1482 
1483         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1484         if (flg) {
1485           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1486           PetscCall(DMLabelView(label, viewer));
1487         }
1488         PetscCall(PetscFree(labelNames[l]));
1489       }
1490       PetscCall(PetscFree(labelNames));
1491     }
1492     /* If no fields are specified, people do not want to see adjacency */
1493     if (dm->Nf) {
1494       PetscInt f;
1495 
1496       for (f = 0; f < dm->Nf; ++f) {
1497         const char *name;
1498 
1499         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1500         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1501         PetscCall(PetscViewerASCIIPushTab(viewer));
1502         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1503         if (dm->fields[f].adjacency[0]) {
1504           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1505           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1506         } else {
1507           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1508           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1509         }
1510         PetscCall(PetscViewerASCIIPopTab(viewer));
1511       }
1512     }
1513     PetscCall(DMGetCoarseDM(dm, &cdm));
1514     if (cdm) {
1515       PetscCall(PetscViewerASCIIPushTab(viewer));
1516       PetscCall(DMPlexView_Ascii(cdm, viewer));
1517       PetscCall(PetscViewerASCIIPopTab(viewer));
1518     }
1519   }
1520   PetscFunctionReturn(0);
1521 }
1522 
1523 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1524 {
1525   DMPolytopeType ct;
1526   PetscMPIInt    rank;
1527   PetscInt       cdim;
1528 
1529   PetscFunctionBegin;
1530   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1531   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1532   PetscCall(DMGetCoordinateDim(dm, &cdim));
1533   switch (ct) {
1534   case DM_POLYTOPE_SEGMENT:
1535   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1536     switch (cdim) {
1537     case 1:
1538     {
1539       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1540       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1541 
1542       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y,    PetscRealPart(coords[1]), y,    PETSC_DRAW_BLACK));
1543       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y+dy, PetscRealPart(coords[0]), y-dy, PETSC_DRAW_BLACK));
1544       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y+dy, PetscRealPart(coords[1]), y-dy, PETSC_DRAW_BLACK));
1545     }
1546     break;
1547     case 2:
1548     {
1549       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1550       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1551       const PetscReal l  = 0.1/PetscSqrtReal(dx*dx + dy*dy);
1552 
1553       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1554       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0])+l*dx, PetscRealPart(coords[1])+l*dy, PetscRealPart(coords[0])-l*dx, PetscRealPart(coords[1])-l*dy, PETSC_DRAW_BLACK));
1555       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2])+l*dx, PetscRealPart(coords[3])+l*dy, PetscRealPart(coords[2])-l*dx, PetscRealPart(coords[3])-l*dy, PETSC_DRAW_BLACK));
1556     }
1557     break;
1558     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1559     }
1560     break;
1561   case DM_POLYTOPE_TRIANGLE:
1562     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1563                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1564                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1565                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1566     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1567     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1568     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1569     break;
1570   case DM_POLYTOPE_QUADRILATERAL:
1571     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1572                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1573                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1574                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1575     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1576                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1577                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1578                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1579     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1580     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1581     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1582     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1583     break;
1584   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1585   }
1586   PetscFunctionReturn(0);
1587 }
1588 
1589 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1590 {
1591   DMPolytopeType ct;
1592   PetscReal      centroid[2] = {0., 0.};
1593   PetscMPIInt    rank;
1594   PetscInt       fillColor, v, e, d;
1595 
1596   PetscFunctionBegin;
1597   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1598   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1599   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2;
1600   switch (ct) {
1601   case DM_POLYTOPE_TRIANGLE:
1602     {
1603       PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1604 
1605       for (v = 0; v < 3; ++v) {centroid[0] += PetscRealPart(coords[v*2+0])/3.;centroid[1] += PetscRealPart(coords[v*2+1])/3.;}
1606       for (e = 0; e < 3; ++e) {
1607         refCoords[0] = refVertices[e*2+0];
1608         refCoords[1] = refVertices[e*2+1];
1609         for (d = 1; d <= edgeDiv; ++d) {
1610           refCoords[d*2+0] = refCoords[0] + (refVertices[(e+1)%3 * 2 + 0] - refCoords[0])*d/edgeDiv;
1611           refCoords[d*2+1] = refCoords[1] + (refVertices[(e+1)%3 * 2 + 1] - refCoords[1])*d/edgeDiv;
1612         }
1613         PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv+1, refCoords, edgeCoords));
1614         for (d = 0; d < edgeDiv; ++d) {
1615           PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], fillColor, fillColor, fillColor));
1616           PetscCall(PetscDrawLine(draw, edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], PETSC_DRAW_BLACK));
1617         }
1618       }
1619     }
1620     break;
1621   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1622   }
1623   PetscFunctionReturn(0);
1624 }
1625 
1626 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1627 {
1628   PetscDraw          draw;
1629   DM                 cdm;
1630   PetscSection       coordSection;
1631   Vec                coordinates;
1632   const PetscScalar *coords;
1633   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1634   PetscReal         *refCoords, *edgeCoords;
1635   PetscBool          isnull, drawAffine = PETSC_TRUE;
1636   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1637 
1638   PetscFunctionBegin;
1639   PetscCall(DMGetCoordinateDim(dm, &dim));
1640   PetscCheck(dim <= 2,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1641   PetscCall(PetscOptionsGetBool(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1642   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv+1)*dim, &refCoords, (edgeDiv+1)*dim, &edgeCoords));
1643   PetscCall(DMGetCoordinateDM(dm, &cdm));
1644   PetscCall(DMGetLocalSection(cdm, &coordSection));
1645   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1646   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1647   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1648 
1649   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1650   PetscCall(PetscDrawIsNull(draw, &isnull));
1651   if (isnull) PetscFunctionReturn(0);
1652   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1653 
1654   PetscCall(VecGetLocalSize(coordinates, &N));
1655   PetscCall(VecGetArrayRead(coordinates, &coords));
1656   for (c = 0; c < N; c += dim) {
1657     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1658     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1659   }
1660   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1661   PetscCall(MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm)));
1662   PetscCall(MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm)));
1663   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1664   PetscCall(PetscDrawClear(draw));
1665 
1666   for (c = cStart; c < cEnd; ++c) {
1667     PetscScalar *coords = NULL;
1668     PetscInt     numCoords;
1669 
1670     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1671     if (drawAffine) {
1672       PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1673     } else {
1674       PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1675     }
1676     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1677   }
1678   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1679   PetscCall(PetscDrawFlush(draw));
1680   PetscCall(PetscDrawPause(draw));
1681   PetscCall(PetscDrawSave(draw));
1682   PetscFunctionReturn(0);
1683 }
1684 
1685 #if defined(PETSC_HAVE_EXODUSII)
1686 #include <exodusII.h>
1687 #include <petscviewerexodusii.h>
1688 #endif
1689 
1690 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1691 {
1692   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus;
1693   char           name[PETSC_MAX_PATH_LEN];
1694 
1695   PetscFunctionBegin;
1696   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1697   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1698   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii));
1699   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
1700   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
1701   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
1702   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
1703   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus));
1704   if (iascii) {
1705     PetscViewerFormat format;
1706     PetscCall(PetscViewerGetFormat(viewer, &format));
1707     if (format == PETSC_VIEWER_ASCII_GLVIS) {
1708       PetscCall(DMPlexView_GLVis(dm, viewer));
1709     } else {
1710       PetscCall(DMPlexView_Ascii(dm, viewer));
1711     }
1712   } else if (ishdf5) {
1713 #if defined(PETSC_HAVE_HDF5)
1714     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1715 #else
1716     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1717 #endif
1718   } else if (isvtk) {
1719     PetscCall(DMPlexVTKWriteAll((PetscObject) dm,viewer));
1720   } else if (isdraw) {
1721     PetscCall(DMPlexView_Draw(dm, viewer));
1722   } else if (isglvis) {
1723     PetscCall(DMPlexView_GLVis(dm, viewer));
1724 #if defined(PETSC_HAVE_EXODUSII)
1725   } else if (isexodus) {
1726 /*
1727       exodusII requires that all sets be part of exactly one cell set.
1728       If the dm does not have a "Cell Sets" label defined, we create one
1729       with ID 1, containig all cells.
1730       Note that if the Cell Sets label is defined but does not cover all cells,
1731       we may still have a problem. This should probably be checked here or in the viewer;
1732     */
1733     PetscInt numCS;
1734     PetscCall(DMGetLabelSize(dm,"Cell Sets",&numCS));
1735     if (!numCS) {
1736       PetscInt cStart, cEnd, c;
1737       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1738       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1739       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1740     }
1741     PetscCall(DMView_PlexExodusII(dm, viewer));
1742 #endif
1743   } else {
1744     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1745   }
1746   /* Optionally view the partition */
1747   PetscCall(PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg));
1748   if (flg) {
1749     Vec ranks;
1750     PetscCall(DMPlexCreateRankField(dm, &ranks));
1751     PetscCall(VecView(ranks, viewer));
1752     PetscCall(VecDestroy(&ranks));
1753   }
1754   /* Optionally view a label */
1755   PetscCall(PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1756   if (flg) {
1757     DMLabel label;
1758     Vec     val;
1759 
1760     PetscCall(DMGetLabel(dm, name, &label));
1761     PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1762     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1763     PetscCall(VecView(val, viewer));
1764     PetscCall(VecDestroy(&val));
1765   }
1766   PetscFunctionReturn(0);
1767 }
1768 
1769 /*@
1770   DMPlexTopologyView - Saves a DMPlex topology into a file
1771 
1772   Collective on DM
1773 
1774   Input Parameters:
1775 + dm     - The DM whose topology is to be saved
1776 - viewer - The PetscViewer for saving
1777 
1778   Level: advanced
1779 
1780 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1781 @*/
1782 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1783 {
1784   PetscBool      ishdf5;
1785 
1786   PetscFunctionBegin;
1787   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1788   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1789   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1790   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView,viewer,0,0,0));
1791   if (ishdf5) {
1792 #if defined(PETSC_HAVE_HDF5)
1793     PetscViewerFormat format;
1794     PetscCall(PetscViewerGetFormat(viewer, &format));
1795     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1796       IS globalPointNumbering;
1797 
1798       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1799       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1800       PetscCall(ISDestroy(&globalPointNumbering));
1801     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1802 #else
1803     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1804 #endif
1805   }
1806   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView,viewer,0,0,0));
1807   PetscFunctionReturn(0);
1808 }
1809 
1810 /*@
1811   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1812 
1813   Collective on DM
1814 
1815   Input Parameters:
1816 + dm     - The DM whose coordinates are to be saved
1817 - viewer - The PetscViewer for saving
1818 
1819   Level: advanced
1820 
1821 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1822 @*/
1823 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1824 {
1825   PetscBool      ishdf5;
1826 
1827   PetscFunctionBegin;
1828   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1829   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1830   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1831   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView,viewer,0,0,0));
1832   if (ishdf5) {
1833 #if defined(PETSC_HAVE_HDF5)
1834     PetscViewerFormat format;
1835     PetscCall(PetscViewerGetFormat(viewer, &format));
1836     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1837       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1838     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1839 #else
1840     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1841 #endif
1842   }
1843   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView,viewer,0,0,0));
1844   PetscFunctionReturn(0);
1845 }
1846 
1847 /*@
1848   DMPlexLabelsView - Saves DMPlex labels into a file
1849 
1850   Collective on DM
1851 
1852   Input Parameters:
1853 + dm     - The DM whose labels are to be saved
1854 - viewer - The PetscViewer for saving
1855 
1856   Level: advanced
1857 
1858 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1859 @*/
1860 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1861 {
1862   PetscBool      ishdf5;
1863 
1864   PetscFunctionBegin;
1865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1866   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1867   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1868   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView,viewer,0,0,0));
1869   if (ishdf5) {
1870 #if defined(PETSC_HAVE_HDF5)
1871     IS                globalPointNumbering;
1872     PetscViewerFormat format;
1873 
1874     PetscCall(PetscViewerGetFormat(viewer, &format));
1875     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1876       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1877       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1878       PetscCall(ISDestroy(&globalPointNumbering));
1879     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1880 #else
1881     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1882 #endif
1883   }
1884   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView,viewer,0,0,0));
1885   PetscFunctionReturn(0);
1886 }
1887 
1888 /*@
1889   DMPlexSectionView - Saves a section associated with a DMPlex
1890 
1891   Collective on DM
1892 
1893   Input Parameters:
1894 + dm         - The DM that contains the topology on which the section to be saved is defined
1895 . viewer     - The PetscViewer for saving
1896 - sectiondm  - The DM that contains the section to be saved
1897 
1898   Level: advanced
1899 
1900   Notes:
1901   This function is a wrapper around PetscSectionView(); in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with DMPlexTopologyLoad() and DMPlexSectionLoad(), respectively, this information is used to match section points with topology points.
1902 
1903   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1904 
1905 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1906 @*/
1907 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1908 {
1909   PetscBool      ishdf5;
1910 
1911   PetscFunctionBegin;
1912   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1913   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1914   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1915   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
1916   PetscCall(PetscLogEventBegin(DMPLEX_SectionView,viewer,0,0,0));
1917   if (ishdf5) {
1918 #if defined(PETSC_HAVE_HDF5)
1919     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1920 #else
1921     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1922 #endif
1923   }
1924   PetscCall(PetscLogEventEnd(DMPLEX_SectionView,viewer,0,0,0));
1925   PetscFunctionReturn(0);
1926 }
1927 
1928 /*@
1929   DMPlexGlobalVectorView - Saves a global vector
1930 
1931   Collective on DM
1932 
1933   Input Parameters:
1934 + dm        - The DM that represents the topology
1935 . viewer    - The PetscViewer to save data with
1936 . sectiondm - The DM that contains the global section on which vec is defined
1937 - vec       - The global vector to be saved
1938 
1939   Level: advanced
1940 
1941   Notes:
1942   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1943 
1944   Typical calling sequence
1945 $       DMCreate(PETSC_COMM_WORLD, &dm);
1946 $       DMSetType(dm, DMPLEX);
1947 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1948 $       DMClone(dm, &sectiondm);
1949 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1950 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1951 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1952 $       PetscSectionSetChart(section, pStart, pEnd);
1953 $       PetscSectionSetUp(section);
1954 $       DMSetLocalSection(sectiondm, section);
1955 $       PetscSectionDestroy(&section);
1956 $       DMGetGlobalVector(sectiondm, &vec);
1957 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1958 $       DMPlexTopologyView(dm, viewer);
1959 $       DMPlexSectionView(dm, viewer, sectiondm);
1960 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1961 $       DMRestoreGlobalVector(sectiondm, &vec);
1962 $       DMDestroy(&sectiondm);
1963 $       DMDestroy(&dm);
1964 
1965 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1966 @*/
1967 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
1968 {
1969   PetscBool       ishdf5;
1970 
1971   PetscFunctionBegin;
1972   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1973   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1974   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1975   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1976   /* Check consistency */
1977   {
1978     PetscSection  section;
1979     PetscBool     includesConstraints;
1980     PetscInt      m, m1;
1981 
1982     PetscCall(VecGetLocalSize(vec, &m1));
1983     PetscCall(DMGetGlobalSection(sectiondm, &section));
1984     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1985     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
1986     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
1987     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
1988   }
1989   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1990   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView,viewer,0,0,0));
1991   if (ishdf5) {
1992 #if defined(PETSC_HAVE_HDF5)
1993     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
1994 #else
1995     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1996 #endif
1997   }
1998   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView,viewer,0,0,0));
1999   PetscFunctionReturn(0);
2000 }
2001 
2002 /*@
2003   DMPlexLocalVectorView - Saves a local vector
2004 
2005   Collective on DM
2006 
2007   Input Parameters:
2008 + dm        - The DM that represents the topology
2009 . viewer    - The PetscViewer to save data with
2010 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2011 - vec       - The local vector to be saved
2012 
2013   Level: advanced
2014 
2015   Notes:
2016   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2017 
2018   Typical calling sequence
2019 $       DMCreate(PETSC_COMM_WORLD, &dm);
2020 $       DMSetType(dm, DMPLEX);
2021 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2022 $       DMClone(dm, &sectiondm);
2023 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2024 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2025 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2026 $       PetscSectionSetChart(section, pStart, pEnd);
2027 $       PetscSectionSetUp(section);
2028 $       DMSetLocalSection(sectiondm, section);
2029 $       DMGetLocalVector(sectiondm, &vec);
2030 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2031 $       DMPlexTopologyView(dm, viewer);
2032 $       DMPlexSectionView(dm, viewer, sectiondm);
2033 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2034 $       DMRestoreLocalVector(sectiondm, &vec);
2035 $       DMDestroy(&sectiondm);
2036 $       DMDestroy(&dm);
2037 
2038 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2039 @*/
2040 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2041 {
2042   PetscBool       ishdf5;
2043 
2044   PetscFunctionBegin;
2045   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2046   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2047   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2048   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2049   /* Check consistency */
2050   {
2051     PetscSection  section;
2052     PetscBool     includesConstraints;
2053     PetscInt      m, m1;
2054 
2055     PetscCall(VecGetLocalSize(vec, &m1));
2056     PetscCall(DMGetLocalSection(sectiondm, &section));
2057     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2058     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2059     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2060     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2061   }
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2063   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView,viewer,0,0,0));
2064   if (ishdf5) {
2065 #if defined(PETSC_HAVE_HDF5)
2066     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2067 #else
2068     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2069 #endif
2070   }
2071   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView,viewer,0,0,0));
2072   PetscFunctionReturn(0);
2073 }
2074 
2075 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2076 {
2077   PetscBool      ishdf5;
2078 
2079   PetscFunctionBegin;
2080   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2081   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2082   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5));
2083   if (ishdf5) {
2084 #if defined(PETSC_HAVE_HDF5)
2085     PetscViewerFormat format;
2086     PetscCall(PetscViewerGetFormat(viewer, &format));
2087     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2088       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2089     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2090       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2091     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2092     PetscFunctionReturn(0);
2093 #else
2094     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2095 #endif
2096   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2097 }
2098 
2099 /*@
2100   DMPlexTopologyLoad - Loads a topology into a DMPlex
2101 
2102   Collective on DM
2103 
2104   Input Parameters:
2105 + dm     - The DM into which the topology is loaded
2106 - viewer - The PetscViewer for the saved topology
2107 
2108   Output Parameters:
2109 . globalToLocalPointSF - The PetscSF that pushes points in [0, N) to the associated points in the loaded plex, where N is the global number of points; NULL if unneeded
2110 
2111   Level: advanced
2112 
2113 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2114 @*/
2115 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2116 {
2117   PetscBool      ishdf5;
2118 
2119   PetscFunctionBegin;
2120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2121   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2122   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2123   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2124   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad,viewer,0,0,0));
2125   if (ishdf5) {
2126 #if defined(PETSC_HAVE_HDF5)
2127     PetscViewerFormat format;
2128     PetscCall(PetscViewerGetFormat(viewer, &format));
2129     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2130       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2131     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2132 #else
2133     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2134 #endif
2135   }
2136   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad,viewer,0,0,0));
2137   PetscFunctionReturn(0);
2138 }
2139 
2140 /*@
2141   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2142 
2143   Collective on DM
2144 
2145   Input Parameters:
2146 + dm     - The DM into which the coordinates are loaded
2147 . viewer - The PetscViewer for the saved coordinates
2148 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2149 
2150   Level: advanced
2151 
2152 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2153 @*/
2154 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2155 {
2156   PetscBool      ishdf5;
2157 
2158   PetscFunctionBegin;
2159   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2160   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2161   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2162   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2163   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2164   if (ishdf5) {
2165 #if defined(PETSC_HAVE_HDF5)
2166     PetscViewerFormat format;
2167     PetscCall(PetscViewerGetFormat(viewer, &format));
2168     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2169       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2170     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2171 #else
2172     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2173 #endif
2174   }
2175   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2176   PetscFunctionReturn(0);
2177 }
2178 
2179 /*@
2180   DMPlexLabelsLoad - Loads labels into a DMPlex
2181 
2182   Collective on DM
2183 
2184   Input Parameters:
2185 + dm     - The DM into which the labels are loaded
2186 . viewer - The PetscViewer for the saved labels
2187 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2188 
2189   Level: advanced
2190 
2191   Notes:
2192   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2193 
2194 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2195 @*/
2196 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2197 {
2198   PetscBool      ishdf5;
2199 
2200   PetscFunctionBegin;
2201   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2202   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2203   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2204   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2205   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad,viewer,0,0,0));
2206   if (ishdf5) {
2207 #if defined(PETSC_HAVE_HDF5)
2208     PetscViewerFormat format;
2209 
2210     PetscCall(PetscViewerGetFormat(viewer, &format));
2211     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2212       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2213     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2214 #else
2215     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2216 #endif
2217   }
2218   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad,viewer,0,0,0));
2219   PetscFunctionReturn(0);
2220 }
2221 
2222 /*@
2223   DMPlexSectionLoad - Loads section into a DMPlex
2224 
2225   Collective on DM
2226 
2227   Input Parameters:
2228 + dm          - The DM that represents the topology
2229 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2230 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2231 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2232 
2233   Output Parameters
2234 + globalDofSF - The SF that migrates any on-disk Vec data associated with sectionA into a global Vec associated with the sectiondm's global section (NULL if not needed)
2235 - localDofSF  - The SF that migrates any on-disk Vec data associated with sectionA into a local Vec associated with the sectiondm's local section (NULL if not needed)
2236 
2237   Level: advanced
2238 
2239   Notes:
2240   This function is a wrapper around PetscSectionLoad(); it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in dm. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2241 
2242   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2243 
2244   The output parameter, globalDofSF (localDofSF), can later be used with DMPlexGlobalVectorLoad() (DMPlexLocalVectorLoad()) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2245 
2246   Example using 2 processes:
2247 $  NX (number of points on dm): 4
2248 $  sectionA                   : the on-disk section
2249 $  vecA                       : a vector associated with sectionA
2250 $  sectionB                   : sectiondm's local section constructed in this function
2251 $  vecB (local)               : a vector associated with sectiondm's local section
2252 $  vecB (global)              : a vector associated with sectiondm's global section
2253 $
2254 $                                     rank 0    rank 1
2255 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2256 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2257 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2258 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2259 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2260 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2261 $  sectionB->atlasDof             :     1 0 1 | 1 3
2262 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2263 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2264 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2265 $
2266 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2267 
2268 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2269 @*/
2270 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2271 {
2272   PetscBool      ishdf5;
2273 
2274   PetscFunctionBegin;
2275   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2276   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2277   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2278   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2279   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2280   if (localDofSF) PetscValidPointer(localDofSF, 6);
2281   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2282   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad,viewer,0,0,0));
2283   if (ishdf5) {
2284 #if defined(PETSC_HAVE_HDF5)
2285     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2286 #else
2287     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2288 #endif
2289   }
2290   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad,viewer,0,0,0));
2291   PetscFunctionReturn(0);
2292 }
2293 
2294 /*@
2295   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2296 
2297   Collective on DM
2298 
2299   Input Parameters:
2300 + dm        - The DM that represents the topology
2301 . viewer    - The PetscViewer that represents the on-disk vector data
2302 . sectiondm - The DM that contains the global section on which vec is defined
2303 . sf        - The SF that migrates the on-disk vector data into vec
2304 - vec       - The global vector to set values of
2305 
2306   Level: advanced
2307 
2308   Notes:
2309   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2310 
2311   Typical calling sequence
2312 $       DMCreate(PETSC_COMM_WORLD, &dm);
2313 $       DMSetType(dm, DMPLEX);
2314 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2315 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2316 $       DMClone(dm, &sectiondm);
2317 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2318 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2319 $       DMGetGlobalVector(sectiondm, &vec);
2320 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2321 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2322 $       DMRestoreGlobalVector(sectiondm, &vec);
2323 $       PetscSFDestroy(&gsf);
2324 $       PetscSFDestroy(&sfX);
2325 $       DMDestroy(&sectiondm);
2326 $       DMDestroy(&dm);
2327 
2328 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2329 @*/
2330 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2331 {
2332   PetscBool       ishdf5;
2333 
2334   PetscFunctionBegin;
2335   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2336   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2337   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2338   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2339   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2340   /* Check consistency */
2341   {
2342     PetscSection  section;
2343     PetscBool     includesConstraints;
2344     PetscInt      m, m1;
2345 
2346     PetscCall(VecGetLocalSize(vec, &m1));
2347     PetscCall(DMGetGlobalSection(sectiondm, &section));
2348     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2349     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2350     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2351     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2352   }
2353   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2354   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2355   if (ishdf5) {
2356 #if defined(PETSC_HAVE_HDF5)
2357     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2358 #else
2359     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2360 #endif
2361   }
2362   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2363   PetscFunctionReturn(0);
2364 }
2365 
2366 /*@
2367   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2368 
2369   Collective on DM
2370 
2371   Input Parameters:
2372 + dm        - The DM that represents the topology
2373 . viewer    - The PetscViewer that represents the on-disk vector data
2374 . sectiondm - The DM that contains the local section on which vec is defined
2375 . sf        - The SF that migrates the on-disk vector data into vec
2376 - vec       - The local vector to set values of
2377 
2378   Level: advanced
2379 
2380   Notes:
2381   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2382 
2383   Typical calling sequence
2384 $       DMCreate(PETSC_COMM_WORLD, &dm);
2385 $       DMSetType(dm, DMPLEX);
2386 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2387 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2388 $       DMClone(dm, &sectiondm);
2389 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2390 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2391 $       DMGetLocalVector(sectiondm, &vec);
2392 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2393 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2394 $       DMRestoreLocalVector(sectiondm, &vec);
2395 $       PetscSFDestroy(&lsf);
2396 $       PetscSFDestroy(&sfX);
2397 $       DMDestroy(&sectiondm);
2398 $       DMDestroy(&dm);
2399 
2400 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2401 @*/
2402 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2403 {
2404   PetscBool       ishdf5;
2405 
2406   PetscFunctionBegin;
2407   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2408   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2409   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2410   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2411   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2412   /* Check consistency */
2413   {
2414     PetscSection  section;
2415     PetscBool     includesConstraints;
2416     PetscInt      m, m1;
2417 
2418     PetscCall(VecGetLocalSize(vec, &m1));
2419     PetscCall(DMGetLocalSection(sectiondm, &section));
2420     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2421     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2422     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2423     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2424   }
2425   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2426   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2427   if (ishdf5) {
2428 #if defined(PETSC_HAVE_HDF5)
2429     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2430 #else
2431     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2432 #endif
2433   }
2434   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2435   PetscFunctionReturn(0);
2436 }
2437 
2438 PetscErrorCode DMDestroy_Plex(DM dm)
2439 {
2440   DM_Plex       *mesh = (DM_Plex*) dm->data;
2441 
2442   PetscFunctionBegin;
2443   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL));
2444   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL));
2445   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL));
2446   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMInterpolateSolution_C", NULL));
2447   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2448   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C", NULL));
2449   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeGetDefault_C", NULL));
2450   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeSetDefault_C", NULL));
2451   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"MatComputeNeumannOverlap_C",NULL));
2452   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderGetDefault_C", NULL));
2453   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderSetDefault_C", NULL));
2454   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C",NULL));
2455   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexSetOverlap_C",NULL));
2456   if (--mesh->refct > 0) PetscFunctionReturn(0);
2457   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2458   PetscCall(PetscFree(mesh->cones));
2459   PetscCall(PetscFree(mesh->coneOrientations));
2460   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2461   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2462   PetscCall(PetscFree(mesh->supports));
2463   PetscCall(PetscFree(mesh->facesTmp));
2464   PetscCall(PetscFree(mesh->tetgenOpts));
2465   PetscCall(PetscFree(mesh->triangleOpts));
2466   PetscCall(PetscFree(mesh->transformType));
2467   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2468   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2469   PetscCall(ISDestroy(&mesh->subpointIS));
2470   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2471   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2472   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2473   PetscCall(ISDestroy(&mesh->anchorIS));
2474   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2475   PetscCall(PetscFree(mesh->parents));
2476   PetscCall(PetscFree(mesh->childIDs));
2477   PetscCall(PetscSectionDestroy(&mesh->childSection));
2478   PetscCall(PetscFree(mesh->children));
2479   PetscCall(DMDestroy(&mesh->referenceTree));
2480   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2481   PetscCall(PetscFree(mesh->neighbors));
2482   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2483   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2484   PetscCall(PetscFree(mesh));
2485   PetscFunctionReturn(0);
2486 }
2487 
2488 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2489 {
2490   PetscSection           sectionGlobal;
2491   PetscInt               bs = -1, mbs;
2492   PetscInt               localSize, localStart = 0;
2493   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2494   MatType                mtype;
2495   ISLocalToGlobalMapping ltog;
2496 
2497   PetscFunctionBegin;
2498   PetscCall(MatInitializePackage());
2499   mtype = dm->mattype;
2500   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2501   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2502   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2503   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject) dm)));
2504   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2505   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2506   PetscCall(MatSetType(*J, mtype));
2507   PetscCall(MatSetFromOptions(*J));
2508   PetscCall(MatGetBlockSize(*J, &mbs));
2509   if (mbs > 1) bs = mbs;
2510   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2511   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2512   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2513   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2514   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2515   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2516   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2517   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2518   if (!isShell) {
2519     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2520     PetscInt  *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2521     PetscInt  pStart, pEnd, p, dof, cdof;
2522 
2523     PetscCall(DMGetLocalToGlobalMapping(dm,&ltog));
2524 
2525     PetscCall(PetscCalloc1(localSize, &pblocks));
2526     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2527     for (p = pStart; p < pEnd; ++p) {
2528       PetscInt bdof, offset;
2529 
2530       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2531       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2532       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2533       for (PetscInt i=0; i < dof - cdof; i++)
2534         pblocks[offset - localStart + i] = dof - cdof;
2535       dof  = dof < 0 ? -(dof+1) : dof;
2536       bdof = cdof && (dof-cdof) ? 1 : dof;
2537       if (dof) {
2538         if (bs < 0)          {bs = bdof;}
2539         else if (bs != bdof) {bs = 1;}
2540       }
2541     }
2542     /* Must have same blocksize on all procs (some might have no points) */
2543     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2544     bsLocal[1] = bs;
2545     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
2546     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2547     else bs = bsMinMax[0];
2548     bs = PetscMax(1,bs);
2549     PetscCall(MatSetLocalToGlobalMapping(*J,ltog,ltog));
2550     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2551       PetscCall(MatSetBlockSize(*J, bs));
2552       PetscCall(MatSetUp(*J));
2553     } else {
2554       PetscCall(PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu));
2555       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2556       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2557     }
2558     { // Consolidate blocks
2559       PetscInt nblocks = 0;
2560       for (PetscInt i=0; i<localSize; i += PetscMax(1, pblocks[i])) {
2561         if (pblocks[i] == 0) continue;
2562         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2563         for (PetscInt j=1; j<pblocks[i]; j++) {
2564            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]);
2565         }
2566       }
2567       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2568     }
2569     PetscCall(PetscFree(pblocks));
2570   }
2571   PetscCall(MatSetDM(*J, dm));
2572   PetscFunctionReturn(0);
2573 }
2574 
2575 /*@
2576   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2577 
2578   Not collective
2579 
2580   Input Parameter:
2581 . mesh - The DMPlex
2582 
2583   Output Parameters:
2584 . subsection - The subdomain section
2585 
2586   Level: developer
2587 
2588 .seealso:
2589 @*/
2590 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2591 {
2592   DM_Plex       *mesh = (DM_Plex*) dm->data;
2593 
2594   PetscFunctionBegin;
2595   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2596   if (!mesh->subdomainSection) {
2597     PetscSection section;
2598     PetscSF      sf;
2599 
2600     PetscCall(PetscSFCreate(PETSC_COMM_SELF,&sf));
2601     PetscCall(DMGetLocalSection(dm,&section));
2602     PetscCall(PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection));
2603     PetscCall(PetscSFDestroy(&sf));
2604   }
2605   *subsection = mesh->subdomainSection;
2606   PetscFunctionReturn(0);
2607 }
2608 
2609 /*@
2610   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2611 
2612   Not collective
2613 
2614   Input Parameter:
2615 . mesh - The DMPlex
2616 
2617   Output Parameters:
2618 + pStart - The first mesh point
2619 - pEnd   - The upper bound for mesh points
2620 
2621   Level: beginner
2622 
2623 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2624 @*/
2625 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2626 {
2627   DM_Plex       *mesh = (DM_Plex*) dm->data;
2628 
2629   PetscFunctionBegin;
2630   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2631   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2632   PetscFunctionReturn(0);
2633 }
2634 
2635 /*@
2636   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2637 
2638   Not collective
2639 
2640   Input Parameters:
2641 + mesh - The DMPlex
2642 . pStart - The first mesh point
2643 - pEnd   - The upper bound for mesh points
2644 
2645   Output Parameters:
2646 
2647   Level: beginner
2648 
2649 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2650 @*/
2651 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2652 {
2653   DM_Plex       *mesh = (DM_Plex*) dm->data;
2654 
2655   PetscFunctionBegin;
2656   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2657   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2658   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2659   PetscFunctionReturn(0);
2660 }
2661 
2662 /*@
2663   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2664 
2665   Not collective
2666 
2667   Input Parameters:
2668 + mesh - The DMPlex
2669 - p - The point, which must lie in the chart set with DMPlexSetChart()
2670 
2671   Output Parameter:
2672 . size - The cone size for point p
2673 
2674   Level: beginner
2675 
2676 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2677 @*/
2678 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2679 {
2680   DM_Plex       *mesh = (DM_Plex*) dm->data;
2681 
2682   PetscFunctionBegin;
2683   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2684   PetscValidIntPointer(size, 3);
2685   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2686   PetscFunctionReturn(0);
2687 }
2688 
2689 /*@
2690   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2691 
2692   Not collective
2693 
2694   Input Parameters:
2695 + mesh - The DMPlex
2696 . p - The point, which must lie in the chart set with DMPlexSetChart()
2697 - size - The cone size for point p
2698 
2699   Output Parameter:
2700 
2701   Note:
2702   This should be called after DMPlexSetChart().
2703 
2704   Level: beginner
2705 
2706 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2707 @*/
2708 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2709 {
2710   DM_Plex       *mesh = (DM_Plex*) dm->data;
2711 
2712   PetscFunctionBegin;
2713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2714   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2715   PetscFunctionReturn(0);
2716 }
2717 
2718 /*@
2719   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2720 
2721   Not collective
2722 
2723   Input Parameters:
2724 + mesh - The DMPlex
2725 . p - The point, which must lie in the chart set with DMPlexSetChart()
2726 - size - The additional cone size for point p
2727 
2728   Output Parameter:
2729 
2730   Note:
2731   This should be called after DMPlexSetChart().
2732 
2733   Level: beginner
2734 
2735 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2736 @*/
2737 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2738 {
2739   DM_Plex       *mesh = (DM_Plex*) dm->data;
2740   PetscFunctionBegin;
2741   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2742   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2743   PetscFunctionReturn(0);
2744 }
2745 
2746 /*@C
2747   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2748 
2749   Not collective
2750 
2751   Input Parameters:
2752 + dm - The DMPlex
2753 - p - The point, which must lie in the chart set with DMPlexSetChart()
2754 
2755   Output Parameter:
2756 . cone - An array of points which are on the in-edges for point p
2757 
2758   Level: beginner
2759 
2760   Fortran Notes:
2761   Since it returns an array, this routine is only available in Fortran 90, and you must
2762   include petsc.h90 in your code.
2763   You must also call DMPlexRestoreCone() after you finish using the returned array.
2764   DMPlexRestoreCone() is not needed/available in C.
2765 
2766 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2767 @*/
2768 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2769 {
2770   DM_Plex       *mesh = (DM_Plex*) dm->data;
2771   PetscInt       off;
2772 
2773   PetscFunctionBegin;
2774   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2775   PetscValidPointer(cone, 3);
2776   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2777   *cone = &mesh->cones[off];
2778   PetscFunctionReturn(0);
2779 }
2780 
2781 /*@C
2782   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2783 
2784   Not collective
2785 
2786   Input Parameters:
2787 + dm - The DMPlex
2788 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2789 
2790   Output Parameters:
2791 + pConesSection - PetscSection describing the layout of pCones
2792 - pCones - An array of points which are on the in-edges for the point set p
2793 
2794   Level: intermediate
2795 
2796 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2797 @*/
2798 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2799 {
2800   PetscSection        cs, newcs;
2801   PetscInt            *cones;
2802   PetscInt            *newarr=NULL;
2803   PetscInt            n;
2804 
2805   PetscFunctionBegin;
2806   PetscCall(DMPlexGetCones(dm, &cones));
2807   PetscCall(DMPlexGetConeSection(dm, &cs));
2808   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL));
2809   if (pConesSection) *pConesSection = newcs;
2810   if (pCones) {
2811     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2812     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2813   }
2814   PetscFunctionReturn(0);
2815 }
2816 
2817 /*@
2818   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2819 
2820   Not collective
2821 
2822   Input Parameters:
2823 + dm - The DMPlex
2824 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2825 
2826   Output Parameter:
2827 . expandedPoints - An array of vertices recursively expanded from input points
2828 
2829   Level: advanced
2830 
2831   Notes:
2832   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2833   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2834 
2835 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2836 @*/
2837 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2838 {
2839   IS                  *expandedPointsAll;
2840   PetscInt            depth;
2841 
2842   PetscFunctionBegin;
2843   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2844   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2845   PetscValidPointer(expandedPoints, 3);
2846   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2847   *expandedPoints = expandedPointsAll[0];
2848   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2849   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2850   PetscFunctionReturn(0);
2851 }
2852 
2853 /*@
2854   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).
2855 
2856   Not collective
2857 
2858   Input Parameters:
2859 + dm - The DMPlex
2860 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2861 
2862   Output Parameters:
2863 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2864 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2865 - sections - (optional) An array of sections which describe mappings from points to their cone points
2866 
2867   Level: advanced
2868 
2869   Notes:
2870   Like DMPlexGetConeTuple() but recursive.
2871 
2872   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.
2873   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2874 
2875   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:
2876   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2877   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2878 
2879 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2880 @*/
2881 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2882 {
2883   const PetscInt      *arr0=NULL, *cone=NULL;
2884   PetscInt            *arr=NULL, *newarr=NULL;
2885   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
2886   IS                  *expandedPoints_;
2887   PetscSection        *sections_;
2888 
2889   PetscFunctionBegin;
2890   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2891   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2892   if (depth) PetscValidIntPointer(depth, 3);
2893   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2894   if (sections) PetscValidPointer(sections, 5);
2895   PetscCall(ISGetLocalSize(points, &n));
2896   PetscCall(ISGetIndices(points, &arr0));
2897   PetscCall(DMPlexGetDepth(dm, &depth_));
2898   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2899   PetscCall(PetscCalloc1(depth_, &sections_));
2900   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
2901   for (d=depth_-1; d>=0; d--) {
2902     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2903     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2904     for (i=0; i<n; i++) {
2905       PetscCall(DMPlexGetDepthStratum(dm, d+1, &start, &end));
2906       if (arr[i] >= start && arr[i] < end) {
2907         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2908         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2909       } else {
2910         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2911       }
2912     }
2913     PetscCall(PetscSectionSetUp(sections_[d]));
2914     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2915     PetscCall(PetscMalloc1(newn, &newarr));
2916     for (i=0; i<n; i++) {
2917       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2918       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2919       if (cn > 1) {
2920         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2921         PetscCall(PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt)));
2922       } else {
2923         newarr[co] = arr[i];
2924       }
2925     }
2926     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2927     arr = newarr;
2928     n = newn;
2929   }
2930   PetscCall(ISRestoreIndices(points, &arr0));
2931   *depth = depth_;
2932   if (expandedPoints) *expandedPoints = expandedPoints_;
2933   else {
2934     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2935     PetscCall(PetscFree(expandedPoints_));
2936   }
2937   if (sections) *sections = sections_;
2938   else {
2939     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2940     PetscCall(PetscFree(sections_));
2941   }
2942   PetscFunctionReturn(0);
2943 }
2944 
2945 /*@
2946   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2947 
2948   Not collective
2949 
2950   Input Parameters:
2951 + dm - The DMPlex
2952 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2953 
2954   Output Parameters:
2955 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2956 . expandedPoints - (optional) An array of recursively expanded cones
2957 - sections - (optional) An array of sections which describe mappings from points to their cone points
2958 
2959   Level: advanced
2960 
2961   Notes:
2962   See DMPlexGetConeRecursive() for details.
2963 
2964 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2965 @*/
2966 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2967 {
2968   PetscInt            d, depth_;
2969 
2970   PetscFunctionBegin;
2971   PetscCall(DMPlexGetDepth(dm, &depth_));
2972   PetscCheck(!depth || *depth == depth_,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2973   if (depth) *depth = 0;
2974   if (expandedPoints) {
2975     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2976     PetscCall(PetscFree(*expandedPoints));
2977   }
2978   if (sections)  {
2979     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2980     PetscCall(PetscFree(*sections));
2981   }
2982   PetscFunctionReturn(0);
2983 }
2984 
2985 /*@
2986   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
2987 
2988   Not collective
2989 
2990   Input Parameters:
2991 + mesh - The DMPlex
2992 . p - The point, which must lie in the chart set with DMPlexSetChart()
2993 - cone - An array of points which are on the in-edges for point p
2994 
2995   Output Parameter:
2996 
2997   Note:
2998   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
2999 
3000   Level: beginner
3001 
3002 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3003 @*/
3004 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3005 {
3006   DM_Plex       *mesh = (DM_Plex*) dm->data;
3007   PetscInt       pStart, pEnd;
3008   PetscInt       dof, off, c;
3009 
3010   PetscFunctionBegin;
3011   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3012   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3013   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3014   if (dof) PetscValidIntPointer(cone, 3);
3015   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3016   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);
3017   for (c = 0; c < dof; ++c) {
3018     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);
3019     mesh->cones[off+c] = cone[c];
3020   }
3021   PetscFunctionReturn(0);
3022 }
3023 
3024 /*@C
3025   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3026 
3027   Not collective
3028 
3029   Input Parameters:
3030 + mesh - The DMPlex
3031 - p - The point, which must lie in the chart set with DMPlexSetChart()
3032 
3033   Output Parameter:
3034 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3035                     integer giving the prescription for cone traversal.
3036 
3037   Level: beginner
3038 
3039   Notes:
3040   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3041   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3042   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3043   with the identity.
3044 
3045   Fortran Notes:
3046   Since it returns an array, this routine is only available in Fortran 90, and you must
3047   include petsc.h90 in your code.
3048   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3049   DMPlexRestoreConeOrientation() is not needed/available in C.
3050 
3051 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3052 @*/
3053 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3054 {
3055   DM_Plex       *mesh = (DM_Plex*) dm->data;
3056   PetscInt       off;
3057 
3058   PetscFunctionBegin;
3059   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3060   if (PetscDefined(USE_DEBUG)) {
3061     PetscInt dof;
3062     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3063     if (dof) PetscValidPointer(coneOrientation, 3);
3064   }
3065   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3066 
3067   *coneOrientation = &mesh->coneOrientations[off];
3068   PetscFunctionReturn(0);
3069 }
3070 
3071 /*@
3072   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3073 
3074   Not collective
3075 
3076   Input Parameters:
3077 + mesh - The DMPlex
3078 . p - The point, which must lie in the chart set with DMPlexSetChart()
3079 - coneOrientation - An array of orientations
3080   Output Parameter:
3081 
3082   Notes:
3083   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3084 
3085   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3086 
3087   Level: beginner
3088 
3089 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3090 @*/
3091 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3092 {
3093   DM_Plex       *mesh = (DM_Plex*) dm->data;
3094   PetscInt       pStart, pEnd;
3095   PetscInt       dof, off, c;
3096 
3097   PetscFunctionBegin;
3098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3099   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3100   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3101   if (dof) PetscValidIntPointer(coneOrientation, 3);
3102   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3103   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);
3104   for (c = 0; c < dof; ++c) {
3105     PetscInt cdof, o = coneOrientation[c];
3106 
3107     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof));
3108     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);
3109     mesh->coneOrientations[off+c] = o;
3110   }
3111   PetscFunctionReturn(0);
3112 }
3113 
3114 /*@
3115   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3116 
3117   Not collective
3118 
3119   Input Parameters:
3120 + mesh - The DMPlex
3121 . p - The point, which must lie in the chart set with DMPlexSetChart()
3122 . conePos - The local index in the cone where the point should be put
3123 - conePoint - The mesh point to insert
3124 
3125   Level: beginner
3126 
3127 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3128 @*/
3129 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3130 {
3131   DM_Plex       *mesh = (DM_Plex*) dm->data;
3132   PetscInt       pStart, pEnd;
3133   PetscInt       dof, off;
3134 
3135   PetscFunctionBegin;
3136   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3137   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3138   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);
3139   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);
3140   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3141   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3142   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);
3143   mesh->cones[off+conePos] = conePoint;
3144   PetscFunctionReturn(0);
3145 }
3146 
3147 /*@
3148   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3149 
3150   Not collective
3151 
3152   Input Parameters:
3153 + mesh - The DMPlex
3154 . p - The point, which must lie in the chart set with DMPlexSetChart()
3155 . conePos - The local index in the cone where the point should be put
3156 - coneOrientation - The point orientation to insert
3157 
3158   Level: beginner
3159 
3160   Notes:
3161   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3162 
3163 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3164 @*/
3165 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3166 {
3167   DM_Plex       *mesh = (DM_Plex*) dm->data;
3168   PetscInt       pStart, pEnd;
3169   PetscInt       dof, off;
3170 
3171   PetscFunctionBegin;
3172   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3173   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3174   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);
3175   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3176   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3177   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);
3178   mesh->coneOrientations[off+conePos] = coneOrientation;
3179   PetscFunctionReturn(0);
3180 }
3181 
3182 /*@
3183   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3184 
3185   Not collective
3186 
3187   Input Parameters:
3188 + mesh - The DMPlex
3189 - p - The point, which must lie in the chart set with DMPlexSetChart()
3190 
3191   Output Parameter:
3192 . size - The support size for point p
3193 
3194   Level: beginner
3195 
3196 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3197 @*/
3198 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3199 {
3200   DM_Plex       *mesh = (DM_Plex*) dm->data;
3201 
3202   PetscFunctionBegin;
3203   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3204   PetscValidIntPointer(size, 3);
3205   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3206   PetscFunctionReturn(0);
3207 }
3208 
3209 /*@
3210   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3211 
3212   Not collective
3213 
3214   Input Parameters:
3215 + mesh - The DMPlex
3216 . p - The point, which must lie in the chart set with DMPlexSetChart()
3217 - size - The support size for point p
3218 
3219   Output Parameter:
3220 
3221   Note:
3222   This should be called after DMPlexSetChart().
3223 
3224   Level: beginner
3225 
3226 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3227 @*/
3228 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3229 {
3230   DM_Plex       *mesh = (DM_Plex*) dm->data;
3231 
3232   PetscFunctionBegin;
3233   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3234   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3235   PetscFunctionReturn(0);
3236 }
3237 
3238 /*@C
3239   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3240 
3241   Not collective
3242 
3243   Input Parameters:
3244 + mesh - The DMPlex
3245 - p - The point, which must lie in the chart set with DMPlexSetChart()
3246 
3247   Output Parameter:
3248 . support - An array of points which are on the out-edges for point p
3249 
3250   Level: beginner
3251 
3252   Fortran Notes:
3253   Since it returns an array, this routine is only available in Fortran 90, and you must
3254   include petsc.h90 in your code.
3255   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3256   DMPlexRestoreSupport() is not needed/available in C.
3257 
3258 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3259 @*/
3260 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3261 {
3262   DM_Plex       *mesh = (DM_Plex*) dm->data;
3263   PetscInt       off;
3264 
3265   PetscFunctionBegin;
3266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3267   PetscValidPointer(support, 3);
3268   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3269   *support = &mesh->supports[off];
3270   PetscFunctionReturn(0);
3271 }
3272 
3273 /*@
3274   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3275 
3276   Not collective
3277 
3278   Input Parameters:
3279 + mesh - The DMPlex
3280 . p - The point, which must lie in the chart set with DMPlexSetChart()
3281 - support - An array of points which are on the out-edges for point p
3282 
3283   Output Parameter:
3284 
3285   Note:
3286   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3287 
3288   Level: beginner
3289 
3290 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3291 @*/
3292 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3293 {
3294   DM_Plex       *mesh = (DM_Plex*) dm->data;
3295   PetscInt       pStart, pEnd;
3296   PetscInt       dof, off, c;
3297 
3298   PetscFunctionBegin;
3299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3300   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3301   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3302   if (dof) PetscValidIntPointer(support, 3);
3303   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3304   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);
3305   for (c = 0; c < dof; ++c) {
3306     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);
3307     mesh->supports[off+c] = support[c];
3308   }
3309   PetscFunctionReturn(0);
3310 }
3311 
3312 /*@
3313   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3314 
3315   Not collective
3316 
3317   Input Parameters:
3318 + mesh - The DMPlex
3319 . p - The point, which must lie in the chart set with DMPlexSetChart()
3320 . supportPos - The local index in the cone where the point should be put
3321 - supportPoint - The mesh point to insert
3322 
3323   Level: beginner
3324 
3325 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3326 @*/
3327 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3328 {
3329   DM_Plex       *mesh = (DM_Plex*) dm->data;
3330   PetscInt       pStart, pEnd;
3331   PetscInt       dof, off;
3332 
3333   PetscFunctionBegin;
3334   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3335   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3336   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3337   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3338   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);
3339   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);
3340   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);
3341   mesh->supports[off+supportPos] = supportPoint;
3342   PetscFunctionReturn(0);
3343 }
3344 
3345 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3346 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3347 {
3348   switch (ct) {
3349     case DM_POLYTOPE_SEGMENT:
3350       if (o == -1) return -2;
3351       break;
3352     case DM_POLYTOPE_TRIANGLE:
3353       if (o == -3) return -1;
3354       if (o == -2) return -3;
3355       if (o == -1) return -2;
3356       break;
3357     case DM_POLYTOPE_QUADRILATERAL:
3358       if (o == -4) return -2;
3359       if (o == -3) return -1;
3360       if (o == -2) return -4;
3361       if (o == -1) return -3;
3362       break;
3363     default: return o;
3364   }
3365   return o;
3366 }
3367 
3368 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3369 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3370 {
3371   switch (ct) {
3372     case DM_POLYTOPE_SEGMENT:
3373       if ((o == -2) || (o == 1)) return -1;
3374       if (o == -1) return 0;
3375       break;
3376     case DM_POLYTOPE_TRIANGLE:
3377       if (o == -3) return -2;
3378       if (o == -2) return -1;
3379       if (o == -1) return -3;
3380       break;
3381     case DM_POLYTOPE_QUADRILATERAL:
3382       if (o == -4) return -2;
3383       if (o == -3) return -1;
3384       if (o == -2) return -4;
3385       if (o == -1) return -3;
3386       break;
3387     default: return o;
3388   }
3389   return o;
3390 }
3391 
3392 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3393 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3394 {
3395   PetscInt       pStart, pEnd, p;
3396 
3397   PetscFunctionBegin;
3398   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3399   for (p = pStart; p < pEnd; ++p) {
3400     const PetscInt *cone, *ornt;
3401     PetscInt        coneSize, c;
3402 
3403     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3404     PetscCall(DMPlexGetCone(dm, p, &cone));
3405     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3406     for (c = 0; c < coneSize; ++c) {
3407       DMPolytopeType ct;
3408       const PetscInt o = ornt[c];
3409 
3410       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3411       switch (ct) {
3412         case DM_POLYTOPE_SEGMENT:
3413           if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3414           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3415           break;
3416         case DM_POLYTOPE_TRIANGLE:
3417           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3418           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3419           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3420           break;
3421         case DM_POLYTOPE_QUADRILATERAL:
3422           if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3423           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3424           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3425           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3426           break;
3427         default: break;
3428       }
3429     }
3430   }
3431   PetscFunctionReturn(0);
3432 }
3433 
3434 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3435 {
3436   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3437   PetscInt       *closure;
3438   const PetscInt *tmp = NULL, *tmpO = NULL;
3439   PetscInt        off = 0, tmpSize, t;
3440 
3441   PetscFunctionBeginHot;
3442   if (ornt) {
3443     PetscCall(DMPlexGetCellType(dm, p, &ct));
3444     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3445   }
3446   if (*points) {
3447     closure = *points;
3448   } else {
3449     PetscInt maxConeSize, maxSupportSize;
3450     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3451     PetscCall(DMGetWorkArray(dm, 2*(PetscMax(maxConeSize, maxSupportSize)+1), MPIU_INT, &closure));
3452   }
3453   if (useCone) {
3454     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3455     PetscCall(DMPlexGetCone(dm, p, &tmp));
3456     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3457   } else {
3458     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3459     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3460   }
3461   if (ct == DM_POLYTOPE_UNKNOWN) {
3462     closure[off++] = p;
3463     closure[off++] = 0;
3464     for (t = 0; t < tmpSize; ++t) {
3465       closure[off++] = tmp[t];
3466       closure[off++] = tmpO ? tmpO[t] : 0;
3467     }
3468   } else {
3469     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3470 
3471     /* We assume that cells with a valid type have faces with a valid type */
3472     closure[off++] = p;
3473     closure[off++] = ornt;
3474     for (t = 0; t < tmpSize; ++t) {
3475       DMPolytopeType ft;
3476 
3477       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3478       closure[off++] = tmp[arr[t]];
3479       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3480     }
3481   }
3482   if (numPoints) *numPoints = tmpSize+1;
3483   if (points)    *points    = closure;
3484   PetscFunctionReturn(0);
3485 }
3486 
3487 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3488 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3489 {
3490   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3491   const PetscInt *cone, *ornt;
3492   PetscInt       *pts,  *closure = NULL;
3493   DMPolytopeType  ft;
3494   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3495   PetscInt        dim, coneSize, c, d, clSize, cl;
3496 
3497   PetscFunctionBeginHot;
3498   PetscCall(DMGetDimension(dm, &dim));
3499   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3500   PetscCall(DMPlexGetCone(dm, point, &cone));
3501   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3502   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3503   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    dim+1)-1)/(maxConeSize-1))    : dim+1;
3504   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim+1)-1)/(maxSupportSize-1)) : dim+1;
3505   maxSize       = PetscMax(coneSeries, supportSeries);
3506   if (*points) {pts  = *points;}
3507   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &pts));
3508   c    = 0;
3509   pts[c++] = point;
3510   pts[c++] = o;
3511   PetscCall(DMPlexGetCellType(dm, cone[arr[0*2+0]], &ft));
3512   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[0*2+1], ornt[0]), useCone, &clSize, &closure));
3513   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3514   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[1*2+1], ornt[1]), useCone, &clSize, &closure));
3515   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3516   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3517   for (d = 2; d < coneSize; ++d) {
3518     PetscCall(DMPlexGetCellType(dm, cone[arr[d*2+0]], &ft));
3519     pts[c++] = cone[arr[d*2+0]];
3520     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]);
3521   }
3522   if (dim >= 3) {
3523     for (d = 2; d < coneSize; ++d) {
3524       const PetscInt  fpoint = cone[arr[d*2+0]];
3525       const PetscInt *fcone, *fornt;
3526       PetscInt        fconeSize, fc, i;
3527 
3528       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3529       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]));
3530       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3531       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3532       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3533       for (fc = 0; fc < fconeSize; ++fc) {
3534         const PetscInt cp = fcone[farr[fc*2+0]];
3535         const PetscInt co = farr[fc*2+1];
3536 
3537         for (i = 0; i < c; i += 2) if (pts[i] == cp) break;
3538         if (i == c) {
3539           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3540           pts[c++] = cp;
3541           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc*2+0]]);
3542         }
3543       }
3544     }
3545   }
3546   *numPoints = c/2;
3547   *points    = pts;
3548   PetscFunctionReturn(0);
3549 }
3550 
3551 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3552 {
3553   DMPolytopeType ct;
3554   PetscInt      *closure, *fifo;
3555   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3556   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3557   PetscInt       depth, maxSize;
3558 
3559   PetscFunctionBeginHot;
3560   PetscCall(DMPlexGetDepth(dm, &depth));
3561   if (depth == 1) {
3562     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3563     PetscFunctionReturn(0);
3564   }
3565   PetscCall(DMPlexGetCellType(dm, p, &ct));
3566   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3567   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3568     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3569     PetscFunctionReturn(0);
3570   }
3571   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3572   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    depth+1)-1)/(maxConeSize-1))    : depth+1;
3573   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth+1)-1)/(maxSupportSize-1)) : depth+1;
3574   maxSize       = PetscMax(coneSeries, supportSeries);
3575   PetscCall(DMGetWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3576   if (*points) {closure = *points;}
3577   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &closure));
3578   closure[closureSize++] = p;
3579   closure[closureSize++] = ornt;
3580   fifo[fifoSize++]       = p;
3581   fifo[fifoSize++]       = ornt;
3582   fifo[fifoSize++]       = ct;
3583   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3584   while (fifoSize - fifoStart) {
3585     const PetscInt       q    = fifo[fifoStart++];
3586     const PetscInt       o    = fifo[fifoStart++];
3587     const DMPolytopeType qt   = (DMPolytopeType) fifo[fifoStart++];
3588     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3589     const PetscInt      *tmp, *tmpO;
3590     PetscInt             tmpSize, t;
3591 
3592     if (PetscDefined(USE_DEBUG)) {
3593       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt)/2;
3594       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);
3595     }
3596     if (useCone) {
3597       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3598       PetscCall(DMPlexGetCone(dm, q, &tmp));
3599       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3600     } else {
3601       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3602       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3603       tmpO = NULL;
3604     }
3605     for (t = 0; t < tmpSize; ++t) {
3606       const PetscInt ip = useCone && qarr ? qarr[t*2]   : t;
3607       const PetscInt io = useCone && qarr ? qarr[t*2+1] : 0;
3608       const PetscInt cp = tmp[ip];
3609       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3610       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3611       PetscInt       c;
3612 
3613       /* Check for duplicate */
3614       for (c = 0; c < closureSize; c += 2) {
3615         if (closure[c] == cp) break;
3616       }
3617       if (c == closureSize) {
3618         closure[closureSize++] = cp;
3619         closure[closureSize++] = co;
3620         fifo[fifoSize++]       = cp;
3621         fifo[fifoSize++]       = co;
3622         fifo[fifoSize++]       = ct;
3623       }
3624     }
3625   }
3626   PetscCall(DMRestoreWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3627   if (numPoints) *numPoints = closureSize/2;
3628   if (points)    *points    = closure;
3629   PetscFunctionReturn(0);
3630 }
3631 
3632 /*@C
3633   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3634 
3635   Not collective
3636 
3637   Input Parameters:
3638 + dm      - The DMPlex
3639 . p       - The mesh point
3640 - useCone - PETSC_TRUE for the closure, otherwise return the star
3641 
3642   Input/Output Parameter:
3643 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3644            if NULL on input, internal storage will be returned, otherwise the provided array is used
3645 
3646   Output Parameter:
3647 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3648 
3649   Note:
3650   If using internal storage (points is NULL on input), each call overwrites the last output.
3651 
3652   Fortran Notes:
3653   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3654 
3655   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3656 
3657   Level: beginner
3658 
3659 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3660 @*/
3661 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3662 {
3663   PetscFunctionBeginHot;
3664   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3665   if (numPoints) PetscValidIntPointer(numPoints, 4);
3666   if (points)    PetscValidPointer(points, 5);
3667   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3668   PetscFunctionReturn(0);
3669 }
3670 
3671 /*@C
3672   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3673 
3674   Not collective
3675 
3676   Input Parameters:
3677 + dm        - The DMPlex
3678 . p         - The mesh point
3679 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3680 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3681 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3682 
3683   Note:
3684   If not using internal storage (points is not NULL on input), this call is unnecessary
3685 
3686   Fortran Notes:
3687   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3688 
3689   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3690 
3691   Level: beginner
3692 
3693 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3694 @*/
3695 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3696 {
3697   PetscFunctionBeginHot;
3698   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3699   if (numPoints) *numPoints = 0;
3700   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3701   PetscFunctionReturn(0);
3702 }
3703 
3704 /*@
3705   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3706 
3707   Not collective
3708 
3709   Input Parameter:
3710 . mesh - The DMPlex
3711 
3712   Output Parameters:
3713 + maxConeSize - The maximum number of in-edges
3714 - maxSupportSize - The maximum number of out-edges
3715 
3716   Level: beginner
3717 
3718 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3719 @*/
3720 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3721 {
3722   DM_Plex *mesh = (DM_Plex*) dm->data;
3723 
3724   PetscFunctionBegin;
3725   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3726   if (maxConeSize) {
3727     PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3728   }
3729   if (maxSupportSize) {
3730     PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3731   }
3732   PetscFunctionReturn(0);
3733 }
3734 
3735 PetscErrorCode DMSetUp_Plex(DM dm)
3736 {
3737   DM_Plex       *mesh = (DM_Plex*) dm->data;
3738   PetscInt       size, maxSupportSize;
3739 
3740   PetscFunctionBegin;
3741   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3742   PetscCall(PetscSectionSetUp(mesh->coneSection));
3743   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3744   PetscCall(PetscMalloc1(size, &mesh->cones));
3745   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3746   PetscCall(PetscLogObjectMemory((PetscObject) dm, size*2*sizeof(PetscInt)));
3747   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3748   if (maxSupportSize) {
3749     PetscCall(PetscSectionSetUp(mesh->supportSection));
3750     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3751     PetscCall(PetscMalloc1(size, &mesh->supports));
3752     PetscCall(PetscLogObjectMemory((PetscObject) dm, size*sizeof(PetscInt)));
3753   }
3754   PetscFunctionReturn(0);
3755 }
3756 
3757 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3758 {
3759   PetscFunctionBegin;
3760   if (subdm) PetscCall(DMClone(dm, subdm));
3761   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3762   if (subdm) {(*subdm)->useNatural = dm->useNatural;}
3763   if (dm->useNatural && dm->sfMigration) {
3764     PetscSF        sfMigrationInv,sfNatural;
3765     PetscSection   section, sectionSeq;
3766 
3767     (*subdm)->sfMigration = dm->sfMigration;
3768     PetscCall(PetscObjectReference((PetscObject) dm->sfMigration));
3769     PetscCall(DMGetLocalSection((*subdm), &section));
3770     PetscCall(PetscSFCreateInverseSF((*subdm)->sfMigration, &sfMigrationInv));
3771     PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*subdm)), &sectionSeq));
3772     PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3773 
3774     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, sectionSeq, (*subdm)->sfMigration, &sfNatural));
3775     (*subdm)->sfNatural = sfNatural;
3776     PetscCall(PetscSectionDestroy(&sectionSeq));
3777     PetscCall(PetscSFDestroy(&sfMigrationInv));
3778   }
3779   PetscFunctionReturn(0);
3780 }
3781 
3782 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3783 {
3784   PetscInt       i = 0;
3785 
3786   PetscFunctionBegin;
3787   PetscCall(DMClone(dms[0], superdm));
3788   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3789   (*superdm)->useNatural = PETSC_FALSE;
3790   for (i = 0; i < len; i++) {
3791     if (dms[i]->useNatural && dms[i]->sfMigration) {
3792       PetscSF        sfMigrationInv,sfNatural;
3793       PetscSection   section, sectionSeq;
3794 
3795       (*superdm)->sfMigration = dms[i]->sfMigration;
3796       PetscCall(PetscObjectReference((PetscObject) dms[i]->sfMigration));
3797       (*superdm)->useNatural = PETSC_TRUE;
3798       PetscCall(DMGetLocalSection((*superdm), &section));
3799       PetscCall(PetscSFCreateInverseSF((*superdm)->sfMigration, &sfMigrationInv));
3800       PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*superdm)), &sectionSeq));
3801       PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3802 
3803       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, sectionSeq, (*superdm)->sfMigration, &sfNatural));
3804       (*superdm)->sfNatural = sfNatural;
3805       PetscCall(PetscSectionDestroy(&sectionSeq));
3806       PetscCall(PetscSFDestroy(&sfMigrationInv));
3807       break;
3808     }
3809   }
3810   PetscFunctionReturn(0);
3811 }
3812 
3813 /*@
3814   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3815 
3816   Not collective
3817 
3818   Input Parameter:
3819 . mesh - The DMPlex
3820 
3821   Output Parameter:
3822 
3823   Note:
3824   This should be called after all calls to DMPlexSetCone()
3825 
3826   Level: beginner
3827 
3828 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3829 @*/
3830 PetscErrorCode DMPlexSymmetrize(DM dm)
3831 {
3832   DM_Plex       *mesh = (DM_Plex*) dm->data;
3833   PetscInt      *offsets;
3834   PetscInt       supportSize;
3835   PetscInt       pStart, pEnd, p;
3836 
3837   PetscFunctionBegin;
3838   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3839   PetscCheck(!mesh->supports,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3840   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0));
3841   /* Calculate support sizes */
3842   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3843   for (p = pStart; p < pEnd; ++p) {
3844     PetscInt dof, off, c;
3845 
3846     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3847     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3848     for (c = off; c < off+dof; ++c) {
3849       PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3850     }
3851   }
3852   PetscCall(PetscSectionSetUp(mesh->supportSection));
3853   /* Calculate supports */
3854   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3855   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3856   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3857   for (p = pStart; p < pEnd; ++p) {
3858     PetscInt dof, off, c;
3859 
3860     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3861     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3862     for (c = off; c < off+dof; ++c) {
3863       const PetscInt q = mesh->cones[c];
3864       PetscInt       offS;
3865 
3866       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3867 
3868       mesh->supports[offS+offsets[q]] = p;
3869       ++offsets[q];
3870     }
3871   }
3872   PetscCall(PetscFree(offsets));
3873   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0));
3874   PetscFunctionReturn(0);
3875 }
3876 
3877 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3878 {
3879   IS             stratumIS;
3880 
3881   PetscFunctionBegin;
3882   if (pStart >= pEnd) PetscFunctionReturn(0);
3883   if (PetscDefined(USE_DEBUG)) {
3884     PetscInt  qStart, qEnd, numLevels, level;
3885     PetscBool overlap = PETSC_FALSE;
3886     PetscCall(DMLabelGetNumValues(label, &numLevels));
3887     for (level = 0; level < numLevels; level++) {
3888       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3889       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
3890     }
3891     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);
3892   }
3893   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS));
3894   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3895   PetscCall(ISDestroy(&stratumIS));
3896   PetscFunctionReturn(0);
3897 }
3898 
3899 /*@
3900   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3901   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3902   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3903   the DAG.
3904 
3905   Collective on dm
3906 
3907   Input Parameter:
3908 . mesh - The DMPlex
3909 
3910   Output Parameter:
3911 
3912   Notes:
3913   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3914   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3915   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3916   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3917   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3918 
3919   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3920   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3921   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
3922   to interpolate only that one (e0), so that
3923 $  cone(c0) = {e0, v2}
3924 $  cone(e0) = {v0, v1}
3925   If DMPlexStratify() is run on this mesh, it will give depths
3926 $  depth 0 = {v0, v1, v2}
3927 $  depth 1 = {e0, c0}
3928   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3929 
3930   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3931 
3932   Level: beginner
3933 
3934 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3935 @*/
3936 PetscErrorCode DMPlexStratify(DM dm)
3937 {
3938   DM_Plex       *mesh = (DM_Plex*) dm->data;
3939   DMLabel        label;
3940   PetscInt       pStart, pEnd, p;
3941   PetscInt       numRoots = 0, numLeaves = 0;
3942 
3943   PetscFunctionBegin;
3944   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3945   PetscCall(PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0));
3946 
3947   /* Create depth label */
3948   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3949   PetscCall(DMCreateLabel(dm, "depth"));
3950   PetscCall(DMPlexGetDepthLabel(dm, &label));
3951 
3952   {
3953     /* Initialize roots and count leaves */
3954     PetscInt sMin = PETSC_MAX_INT;
3955     PetscInt sMax = PETSC_MIN_INT;
3956     PetscInt coneSize, supportSize;
3957 
3958     for (p = pStart; p < pEnd; ++p) {
3959       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3960       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3961       if (!coneSize && supportSize) {
3962         sMin = PetscMin(p, sMin);
3963         sMax = PetscMax(p, sMax);
3964         ++numRoots;
3965       } else if (!supportSize && coneSize) {
3966         ++numLeaves;
3967       } else if (!supportSize && !coneSize) {
3968         /* Isolated points */
3969         sMin = PetscMin(p, sMin);
3970         sMax = PetscMax(p, sMax);
3971       }
3972     }
3973     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1));
3974   }
3975 
3976   if (numRoots + numLeaves == (pEnd - pStart)) {
3977     PetscInt sMin = PETSC_MAX_INT;
3978     PetscInt sMax = PETSC_MIN_INT;
3979     PetscInt coneSize, supportSize;
3980 
3981     for (p = pStart; p < pEnd; ++p) {
3982       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3983       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3984       if (!supportSize && coneSize) {
3985         sMin = PetscMin(p, sMin);
3986         sMax = PetscMax(p, sMax);
3987       }
3988     }
3989     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1));
3990   } else {
3991     PetscInt level = 0;
3992     PetscInt qStart, qEnd, q;
3993 
3994     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3995     while (qEnd > qStart) {
3996       PetscInt sMin = PETSC_MAX_INT;
3997       PetscInt sMax = PETSC_MIN_INT;
3998 
3999       for (q = qStart; q < qEnd; ++q) {
4000         const PetscInt *support;
4001         PetscInt        supportSize, s;
4002 
4003         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4004         PetscCall(DMPlexGetSupport(dm, q, &support));
4005         for (s = 0; s < supportSize; ++s) {
4006           sMin = PetscMin(support[s], sMin);
4007           sMax = PetscMax(support[s], sMax);
4008         }
4009       }
4010       PetscCall(DMLabelGetNumValues(label, &level));
4011       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1));
4012       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4013     }
4014   }
4015   { /* just in case there is an empty process */
4016     PetscInt numValues, maxValues = 0, v;
4017 
4018     PetscCall(DMLabelGetNumValues(label, &numValues));
4019     PetscCallMPI(MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm)));
4020     for (v = numValues; v < maxValues; v++) {
4021       PetscCall(DMLabelAddStratum(label, v));
4022     }
4023   }
4024   PetscCall(PetscObjectStateGet((PetscObject) label, &mesh->depthState));
4025   PetscCall(PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0));
4026   PetscFunctionReturn(0);
4027 }
4028 
4029 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4030 {
4031   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4032   PetscInt       dim, depth, pheight, coneSize;
4033 
4034   PetscFunctionBeginHot;
4035   PetscCall(DMGetDimension(dm, &dim));
4036   PetscCall(DMPlexGetDepth(dm, &depth));
4037   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4038   pheight = depth - pdepth;
4039   if (depth <= 1) {
4040     switch (pdepth) {
4041       case 0: ct = DM_POLYTOPE_POINT;break;
4042       case 1:
4043         switch (coneSize) {
4044           case 2: ct = DM_POLYTOPE_SEGMENT;break;
4045           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4046           case 4:
4047           switch (dim) {
4048             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
4049             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
4050             default: break;
4051           }
4052           break;
4053         case 5: ct = DM_POLYTOPE_PYRAMID;break;
4054         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4055         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
4056         default: break;
4057       }
4058     }
4059   } else {
4060     if (pdepth == 0) {
4061       ct = DM_POLYTOPE_POINT;
4062     } else if (pheight == 0) {
4063       switch (dim) {
4064         case 1:
4065           switch (coneSize) {
4066             case 2: ct = DM_POLYTOPE_SEGMENT;break;
4067             default: break;
4068           }
4069           break;
4070         case 2:
4071           switch (coneSize) {
4072             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4073             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4074             default: break;
4075           }
4076           break;
4077         case 3:
4078           switch (coneSize) {
4079             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
4080             case 5:
4081             {
4082               const PetscInt *cone;
4083               PetscInt        faceConeSize;
4084 
4085               PetscCall(DMPlexGetCone(dm, p, &cone));
4086               PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4087               switch (faceConeSize) {
4088                 case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4089                 case 4: ct = DM_POLYTOPE_PYRAMID;break;
4090               }
4091             }
4092             break;
4093             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
4094             default: break;
4095           }
4096           break;
4097         default: break;
4098       }
4099     } else if (pheight > 0) {
4100       switch (coneSize) {
4101         case 2: ct = DM_POLYTOPE_SEGMENT;break;
4102         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4103         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4104         default: break;
4105       }
4106     }
4107   }
4108   *pt = ct;
4109   PetscFunctionReturn(0);
4110 }
4111 
4112 /*@
4113   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4114 
4115   Collective on dm
4116 
4117   Input Parameter:
4118 . mesh - The DMPlex
4119 
4120   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4121 
4122   Level: developer
4123 
4124   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4125   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4126   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4127 
4128 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4129 @*/
4130 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4131 {
4132   DM_Plex       *mesh;
4133   DMLabel        ctLabel;
4134   PetscInt       pStart, pEnd, p;
4135 
4136   PetscFunctionBegin;
4137   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4138   mesh = (DM_Plex *) dm->data;
4139   PetscCall(DMCreateLabel(dm, "celltype"));
4140   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4141   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4142   for (p = pStart; p < pEnd; ++p) {
4143     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4144     PetscInt       pdepth;
4145 
4146     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4147     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4148     PetscCheck(ct != DM_POLYTOPE_UNKNOWN,PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4149     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4150   }
4151   PetscCall(PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState));
4152   PetscCall(PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view"));
4153   PetscFunctionReturn(0);
4154 }
4155 
4156 /*@C
4157   DMPlexGetJoin - Get an array for the join of the set of points
4158 
4159   Not Collective
4160 
4161   Input Parameters:
4162 + dm - The DMPlex object
4163 . numPoints - The number of input points for the join
4164 - points - The input points
4165 
4166   Output Parameters:
4167 + numCoveredPoints - The number of points in the join
4168 - coveredPoints - The points in the join
4169 
4170   Level: intermediate
4171 
4172   Note: Currently, this is restricted to a single level join
4173 
4174   Fortran Notes:
4175   Since it returns an array, this routine is only available in Fortran 90, and you must
4176   include petsc.h90 in your code.
4177 
4178   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4179 
4180 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4181 @*/
4182 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4183 {
4184   DM_Plex       *mesh = (DM_Plex*) dm->data;
4185   PetscInt      *join[2];
4186   PetscInt       joinSize, i = 0;
4187   PetscInt       dof, off, p, c, m;
4188   PetscInt       maxSupportSize;
4189 
4190   PetscFunctionBegin;
4191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4192   PetscValidIntPointer(points, 3);
4193   PetscValidIntPointer(numCoveredPoints, 4);
4194   PetscValidPointer(coveredPoints, 5);
4195   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4196   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4197   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4198   /* Copy in support of first point */
4199   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4200   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4201   for (joinSize = 0; joinSize < dof; ++joinSize) {
4202     join[i][joinSize] = mesh->supports[off+joinSize];
4203   }
4204   /* Check each successive support */
4205   for (p = 1; p < numPoints; ++p) {
4206     PetscInt newJoinSize = 0;
4207 
4208     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4209     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4210     for (c = 0; c < dof; ++c) {
4211       const PetscInt point = mesh->supports[off+c];
4212 
4213       for (m = 0; m < joinSize; ++m) {
4214         if (point == join[i][m]) {
4215           join[1-i][newJoinSize++] = point;
4216           break;
4217         }
4218       }
4219     }
4220     joinSize = newJoinSize;
4221     i        = 1-i;
4222   }
4223   *numCoveredPoints = joinSize;
4224   *coveredPoints    = join[i];
4225   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1-i]));
4226   PetscFunctionReturn(0);
4227 }
4228 
4229 /*@C
4230   DMPlexRestoreJoin - Restore an array for the join of the set of points
4231 
4232   Not Collective
4233 
4234   Input Parameters:
4235 + dm - The DMPlex object
4236 . numPoints - The number of input points for the join
4237 - points - The input points
4238 
4239   Output Parameters:
4240 + numCoveredPoints - The number of points in the join
4241 - coveredPoints - The points in the join
4242 
4243   Fortran Notes:
4244   Since it returns an array, this routine is only available in Fortran 90, and you must
4245   include petsc.h90 in your code.
4246 
4247   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4248 
4249   Level: intermediate
4250 
4251 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4252 @*/
4253 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4254 {
4255   PetscFunctionBegin;
4256   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4257   if (points) PetscValidIntPointer(points,3);
4258   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4259   PetscValidPointer(coveredPoints, 5);
4260   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4261   if (numCoveredPoints) *numCoveredPoints = 0;
4262   PetscFunctionReturn(0);
4263 }
4264 
4265 /*@C
4266   DMPlexGetFullJoin - Get an array for the join of the set of points
4267 
4268   Not Collective
4269 
4270   Input Parameters:
4271 + dm - The DMPlex object
4272 . numPoints - The number of input points for the join
4273 - points - The input points
4274 
4275   Output Parameters:
4276 + numCoveredPoints - The number of points in the join
4277 - coveredPoints - The points in the join
4278 
4279   Fortran Notes:
4280   Since it returns an array, this routine is only available in Fortran 90, and you must
4281   include petsc.h90 in your code.
4282 
4283   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4284 
4285   Level: intermediate
4286 
4287 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4288 @*/
4289 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4290 {
4291   PetscInt      *offsets, **closures;
4292   PetscInt      *join[2];
4293   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
4294   PetscInt       p, d, c, m, ms;
4295 
4296   PetscFunctionBegin;
4297   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4298   PetscValidIntPointer(points, 3);
4299   PetscValidIntPointer(numCoveredPoints, 4);
4300   PetscValidPointer(coveredPoints, 5);
4301 
4302   PetscCall(DMPlexGetDepth(dm, &depth));
4303   PetscCall(PetscCalloc1(numPoints, &closures));
4304   PetscCall(DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4305   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4306   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
4307   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4308   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4309 
4310   for (p = 0; p < numPoints; ++p) {
4311     PetscInt closureSize;
4312 
4313     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4314 
4315     offsets[p*(depth+2)+0] = 0;
4316     for (d = 0; d < depth+1; ++d) {
4317       PetscInt pStart, pEnd, i;
4318 
4319       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4320       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
4321         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4322           offsets[p*(depth+2)+d+1] = i;
4323           break;
4324         }
4325       }
4326       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
4327     }
4328     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);
4329   }
4330   for (d = 0; d < depth+1; ++d) {
4331     PetscInt dof;
4332 
4333     /* Copy in support of first point */
4334     dof = offsets[d+1] - offsets[d];
4335     for (joinSize = 0; joinSize < dof; ++joinSize) {
4336       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
4337     }
4338     /* Check each successive cone */
4339     for (p = 1; p < numPoints && joinSize; ++p) {
4340       PetscInt newJoinSize = 0;
4341 
4342       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
4343       for (c = 0; c < dof; ++c) {
4344         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
4345 
4346         for (m = 0; m < joinSize; ++m) {
4347           if (point == join[i][m]) {
4348             join[1-i][newJoinSize++] = point;
4349             break;
4350           }
4351         }
4352       }
4353       joinSize = newJoinSize;
4354       i        = 1-i;
4355     }
4356     if (joinSize) break;
4357   }
4358   *numCoveredPoints = joinSize;
4359   *coveredPoints    = join[i];
4360   for (p = 0; p < numPoints; ++p) {
4361     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4362   }
4363   PetscCall(PetscFree(closures));
4364   PetscCall(DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4365   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1-i]));
4366   PetscFunctionReturn(0);
4367 }
4368 
4369 /*@C
4370   DMPlexGetMeet - Get an array for the meet of the set of points
4371 
4372   Not Collective
4373 
4374   Input Parameters:
4375 + dm - The DMPlex object
4376 . numPoints - The number of input points for the meet
4377 - points - The input points
4378 
4379   Output Parameters:
4380 + numCoveredPoints - The number of points in the meet
4381 - coveredPoints - The points in the meet
4382 
4383   Level: intermediate
4384 
4385   Note: Currently, this is restricted to a single level meet
4386 
4387   Fortran Notes:
4388   Since it returns an array, this routine is only available in Fortran 90, and you must
4389   include petsc.h90 in your code.
4390 
4391   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4392 
4393 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4394 @*/
4395 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4396 {
4397   DM_Plex       *mesh = (DM_Plex*) dm->data;
4398   PetscInt      *meet[2];
4399   PetscInt       meetSize, i = 0;
4400   PetscInt       dof, off, p, c, m;
4401   PetscInt       maxConeSize;
4402 
4403   PetscFunctionBegin;
4404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4405   PetscValidIntPointer(points, 3);
4406   PetscValidIntPointer(numCoveringPoints, 4);
4407   PetscValidPointer(coveringPoints, 5);
4408   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4409   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4410   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4411   /* Copy in cone of first point */
4412   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4413   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4414   for (meetSize = 0; meetSize < dof; ++meetSize) {
4415     meet[i][meetSize] = mesh->cones[off+meetSize];
4416   }
4417   /* Check each successive cone */
4418   for (p = 1; p < numPoints; ++p) {
4419     PetscInt newMeetSize = 0;
4420 
4421     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4422     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4423     for (c = 0; c < dof; ++c) {
4424       const PetscInt point = mesh->cones[off+c];
4425 
4426       for (m = 0; m < meetSize; ++m) {
4427         if (point == meet[i][m]) {
4428           meet[1-i][newMeetSize++] = point;
4429           break;
4430         }
4431       }
4432     }
4433     meetSize = newMeetSize;
4434     i        = 1-i;
4435   }
4436   *numCoveringPoints = meetSize;
4437   *coveringPoints    = meet[i];
4438   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1-i]));
4439   PetscFunctionReturn(0);
4440 }
4441 
4442 /*@C
4443   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4444 
4445   Not Collective
4446 
4447   Input Parameters:
4448 + dm - The DMPlex object
4449 . numPoints - The number of input points for the meet
4450 - points - The input points
4451 
4452   Output Parameters:
4453 + numCoveredPoints - The number of points in the meet
4454 - coveredPoints - The points in the meet
4455 
4456   Level: intermediate
4457 
4458   Fortran Notes:
4459   Since it returns an array, this routine is only available in Fortran 90, and you must
4460   include petsc.h90 in your code.
4461 
4462   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4463 
4464 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4465 @*/
4466 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4467 {
4468   PetscFunctionBegin;
4469   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4470   if (points) PetscValidIntPointer(points,3);
4471   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4472   PetscValidPointer(coveredPoints,5);
4473   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4474   if (numCoveredPoints) *numCoveredPoints = 0;
4475   PetscFunctionReturn(0);
4476 }
4477 
4478 /*@C
4479   DMPlexGetFullMeet - Get an array for the meet of the set of points
4480 
4481   Not Collective
4482 
4483   Input Parameters:
4484 + dm - The DMPlex object
4485 . numPoints - The number of input points for the meet
4486 - points - The input points
4487 
4488   Output Parameters:
4489 + numCoveredPoints - The number of points in the meet
4490 - coveredPoints - The points in the meet
4491 
4492   Level: intermediate
4493 
4494   Fortran Notes:
4495   Since it returns an array, this routine is only available in Fortran 90, and you must
4496   include petsc.h90 in your code.
4497 
4498   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4499 
4500 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4501 @*/
4502 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4503 {
4504   PetscInt      *offsets, **closures;
4505   PetscInt      *meet[2];
4506   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
4507   PetscInt       p, h, c, m, mc;
4508 
4509   PetscFunctionBegin;
4510   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4511   PetscValidIntPointer(points, 3);
4512   PetscValidIntPointer(numCoveredPoints, 4);
4513   PetscValidPointer(coveredPoints, 5);
4514 
4515   PetscCall(DMPlexGetDepth(dm, &height));
4516   PetscCall(PetscMalloc1(numPoints, &closures));
4517   PetscCall(DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4518   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4519   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
4520   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4521   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4522 
4523   for (p = 0; p < numPoints; ++p) {
4524     PetscInt closureSize;
4525 
4526     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4527 
4528     offsets[p*(height+2)+0] = 0;
4529     for (h = 0; h < height+1; ++h) {
4530       PetscInt pStart, pEnd, i;
4531 
4532       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4533       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
4534         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4535           offsets[p*(height+2)+h+1] = i;
4536           break;
4537         }
4538       }
4539       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
4540     }
4541     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);
4542   }
4543   for (h = 0; h < height+1; ++h) {
4544     PetscInt dof;
4545 
4546     /* Copy in cone of first point */
4547     dof = offsets[h+1] - offsets[h];
4548     for (meetSize = 0; meetSize < dof; ++meetSize) {
4549       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
4550     }
4551     /* Check each successive cone */
4552     for (p = 1; p < numPoints && meetSize; ++p) {
4553       PetscInt newMeetSize = 0;
4554 
4555       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
4556       for (c = 0; c < dof; ++c) {
4557         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
4558 
4559         for (m = 0; m < meetSize; ++m) {
4560           if (point == meet[i][m]) {
4561             meet[1-i][newMeetSize++] = point;
4562             break;
4563           }
4564         }
4565       }
4566       meetSize = newMeetSize;
4567       i        = 1-i;
4568     }
4569     if (meetSize) break;
4570   }
4571   *numCoveredPoints = meetSize;
4572   *coveredPoints    = meet[i];
4573   for (p = 0; p < numPoints; ++p) {
4574     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4575   }
4576   PetscCall(PetscFree(closures));
4577   PetscCall(DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4578   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1-i]));
4579   PetscFunctionReturn(0);
4580 }
4581 
4582 /*@C
4583   DMPlexEqual - Determine if two DMs have the same topology
4584 
4585   Not Collective
4586 
4587   Input Parameters:
4588 + dmA - A DMPlex object
4589 - dmB - A DMPlex object
4590 
4591   Output Parameters:
4592 . equal - PETSC_TRUE if the topologies are identical
4593 
4594   Level: intermediate
4595 
4596   Notes:
4597   We are not solving graph isomorphism, so we do not permutation.
4598 
4599 .seealso: `DMPlexGetCone()`
4600 @*/
4601 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4602 {
4603   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4604 
4605   PetscFunctionBegin;
4606   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4607   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4608   PetscValidBoolPointer(equal, 3);
4609 
4610   *equal = PETSC_FALSE;
4611   PetscCall(DMPlexGetDepth(dmA, &depth));
4612   PetscCall(DMPlexGetDepth(dmB, &depthB));
4613   if (depth != depthB) PetscFunctionReturn(0);
4614   PetscCall(DMPlexGetChart(dmA, &pStart,  &pEnd));
4615   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4616   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4617   for (p = pStart; p < pEnd; ++p) {
4618     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4619     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4620 
4621     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4622     PetscCall(DMPlexGetCone(dmA, p, &cone));
4623     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4624     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4625     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4626     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4627     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4628     for (c = 0; c < coneSize; ++c) {
4629       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4630       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4631     }
4632     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4633     PetscCall(DMPlexGetSupport(dmA, p, &support));
4634     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4635     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4636     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4637     for (s = 0; s < supportSize; ++s) {
4638       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4639     }
4640   }
4641   *equal = PETSC_TRUE;
4642   PetscFunctionReturn(0);
4643 }
4644 
4645 /*@C
4646   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4647 
4648   Not Collective
4649 
4650   Input Parameters:
4651 + dm         - The DMPlex
4652 . cellDim    - The cell dimension
4653 - numCorners - The number of vertices on a cell
4654 
4655   Output Parameters:
4656 . numFaceVertices - The number of vertices on a face
4657 
4658   Level: developer
4659 
4660   Notes:
4661   Of course this can only work for a restricted set of symmetric shapes
4662 
4663 .seealso: `DMPlexGetCone()`
4664 @*/
4665 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4666 {
4667   MPI_Comm       comm;
4668 
4669   PetscFunctionBegin;
4670   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
4671   PetscValidIntPointer(numFaceVertices,4);
4672   switch (cellDim) {
4673   case 0:
4674     *numFaceVertices = 0;
4675     break;
4676   case 1:
4677     *numFaceVertices = 1;
4678     break;
4679   case 2:
4680     switch (numCorners) {
4681     case 3: /* triangle */
4682       *numFaceVertices = 2; /* Edge has 2 vertices */
4683       break;
4684     case 4: /* quadrilateral */
4685       *numFaceVertices = 2; /* Edge has 2 vertices */
4686       break;
4687     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
4688       *numFaceVertices = 3; /* Edge has 3 vertices */
4689       break;
4690     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4691       *numFaceVertices = 3; /* Edge has 3 vertices */
4692       break;
4693     default:
4694       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4695     }
4696     break;
4697   case 3:
4698     switch (numCorners) {
4699     case 4: /* tetradehdron */
4700       *numFaceVertices = 3; /* Face has 3 vertices */
4701       break;
4702     case 6: /* tet cohesive cells */
4703       *numFaceVertices = 4; /* Face has 4 vertices */
4704       break;
4705     case 8: /* hexahedron */
4706       *numFaceVertices = 4; /* Face has 4 vertices */
4707       break;
4708     case 9: /* tet cohesive Lagrange cells */
4709       *numFaceVertices = 6; /* Face has 6 vertices */
4710       break;
4711     case 10: /* quadratic tetrahedron */
4712       *numFaceVertices = 6; /* Face has 6 vertices */
4713       break;
4714     case 12: /* hex cohesive Lagrange cells */
4715       *numFaceVertices = 6; /* Face has 6 vertices */
4716       break;
4717     case 18: /* quadratic tet cohesive Lagrange cells */
4718       *numFaceVertices = 6; /* Face has 6 vertices */
4719       break;
4720     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4721       *numFaceVertices = 9; /* Face has 9 vertices */
4722       break;
4723     default:
4724       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4725     }
4726     break;
4727   default:
4728     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4729   }
4730   PetscFunctionReturn(0);
4731 }
4732 
4733 /*@
4734   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4735 
4736   Not Collective
4737 
4738   Input Parameter:
4739 . dm    - The DMPlex object
4740 
4741   Output Parameter:
4742 . depthLabel - The DMLabel recording point depth
4743 
4744   Level: developer
4745 
4746 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4747 @*/
4748 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4749 {
4750   PetscFunctionBegin;
4751   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4752   PetscValidPointer(depthLabel, 2);
4753   *depthLabel = dm->depthLabel;
4754   PetscFunctionReturn(0);
4755 }
4756 
4757 /*@
4758   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4759 
4760   Not Collective
4761 
4762   Input Parameter:
4763 . dm    - The DMPlex object
4764 
4765   Output Parameter:
4766 . depth - The number of strata (breadth first levels) in the DAG
4767 
4768   Level: developer
4769 
4770   Notes:
4771   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4772   The point depth is described more in detail in DMPlexGetDepthStratum().
4773   An empty mesh gives -1.
4774 
4775 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4776 @*/
4777 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4778 {
4779   DMLabel        label;
4780   PetscInt       d = 0;
4781 
4782   PetscFunctionBegin;
4783   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4784   PetscValidIntPointer(depth, 2);
4785   PetscCall(DMPlexGetDepthLabel(dm, &label));
4786   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4787   *depth = d-1;
4788   PetscFunctionReturn(0);
4789 }
4790 
4791 /*@
4792   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4793 
4794   Not Collective
4795 
4796   Input Parameters:
4797 + dm    - The DMPlex object
4798 - depth - The requested depth
4799 
4800   Output Parameters:
4801 + start - The first point at this depth
4802 - end   - One beyond the last point at this depth
4803 
4804   Notes:
4805   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4806   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4807   higher dimension, e.g., "edges".
4808 
4809   Level: developer
4810 
4811 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4812 @*/
4813 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4814 {
4815   DMLabel        label;
4816   PetscInt       pStart, pEnd;
4817 
4818   PetscFunctionBegin;
4819   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4820   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4821   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4822   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4823   if (pStart == pEnd) PetscFunctionReturn(0);
4824   if (depth < 0) {
4825     if (start) *start = pStart;
4826     if (end)   *end   = pEnd;
4827     PetscFunctionReturn(0);
4828   }
4829   PetscCall(DMPlexGetDepthLabel(dm, &label));
4830   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4831   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4832   PetscFunctionReturn(0);
4833 }
4834 
4835 /*@
4836   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4837 
4838   Not Collective
4839 
4840   Input Parameters:
4841 + dm     - The DMPlex object
4842 - height - The requested height
4843 
4844   Output Parameters:
4845 + start - The first point at this height
4846 - end   - One beyond the last point at this height
4847 
4848   Notes:
4849   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4850   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4851   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4852 
4853   Level: developer
4854 
4855 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4856 @*/
4857 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4858 {
4859   DMLabel        label;
4860   PetscInt       depth, pStart, pEnd;
4861 
4862   PetscFunctionBegin;
4863   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4864   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4865   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4866   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4867   if (pStart == pEnd) PetscFunctionReturn(0);
4868   if (height < 0) {
4869     if (start) *start = pStart;
4870     if (end)   *end   = pEnd;
4871     PetscFunctionReturn(0);
4872   }
4873   PetscCall(DMPlexGetDepthLabel(dm, &label));
4874   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4875   PetscCall(DMLabelGetNumValues(label, &depth));
4876   PetscCall(DMLabelGetStratumBounds(label, depth-1-height, start, end));
4877   PetscFunctionReturn(0);
4878 }
4879 
4880 /*@
4881   DMPlexGetPointDepth - Get the depth of a given point
4882 
4883   Not Collective
4884 
4885   Input Parameters:
4886 + dm    - The DMPlex object
4887 - point - The point
4888 
4889   Output Parameter:
4890 . depth - The depth of the point
4891 
4892   Level: intermediate
4893 
4894 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4895 @*/
4896 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4897 {
4898   PetscFunctionBegin;
4899   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4900   PetscValidIntPointer(depth, 3);
4901   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4902   PetscFunctionReturn(0);
4903 }
4904 
4905 /*@
4906   DMPlexGetPointHeight - Get the height of a given point
4907 
4908   Not Collective
4909 
4910   Input Parameters:
4911 + dm    - The DMPlex object
4912 - point - The point
4913 
4914   Output Parameter:
4915 . height - The height of the point
4916 
4917   Level: intermediate
4918 
4919 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4920 @*/
4921 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4922 {
4923   PetscInt       n, pDepth;
4924 
4925   PetscFunctionBegin;
4926   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4927   PetscValidIntPointer(height, 3);
4928   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4929   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4930   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
4931   PetscFunctionReturn(0);
4932 }
4933 
4934 /*@
4935   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4936 
4937   Not Collective
4938 
4939   Input Parameter:
4940 . dm - The DMPlex object
4941 
4942   Output Parameter:
4943 . celltypeLabel - The DMLabel recording cell polytope type
4944 
4945   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4946   DMCreateLabel(dm, "celltype") beforehand.
4947 
4948   Level: developer
4949 
4950 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4951 @*/
4952 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
4953 {
4954   PetscFunctionBegin;
4955   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4956   PetscValidPointer(celltypeLabel, 2);
4957   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4958   *celltypeLabel = dm->celltypeLabel;
4959   PetscFunctionReturn(0);
4960 }
4961 
4962 /*@
4963   DMPlexGetCellType - Get the polytope type of a given cell
4964 
4965   Not Collective
4966 
4967   Input Parameters:
4968 + dm   - The DMPlex object
4969 - cell - The cell
4970 
4971   Output Parameter:
4972 . celltype - The polytope type of the cell
4973 
4974   Level: intermediate
4975 
4976 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4977 @*/
4978 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
4979 {
4980   DMLabel        label;
4981   PetscInt       ct;
4982 
4983   PetscFunctionBegin;
4984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4985   PetscValidPointer(celltype, 3);
4986   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4987   PetscCall(DMLabelGetValue(label, cell, &ct));
4988   PetscCheck(ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4989   *celltype = (DMPolytopeType) ct;
4990   PetscFunctionReturn(0);
4991 }
4992 
4993 /*@
4994   DMPlexSetCellType - Set the polytope type of a given cell
4995 
4996   Not Collective
4997 
4998   Input Parameters:
4999 + dm   - The DMPlex object
5000 . cell - The cell
5001 - celltype - The polytope type of the cell
5002 
5003   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
5004   is executed. This function will override the computed type. However, if automatic classification will not succeed
5005   and a user wants to manually specify all types, the classification must be disabled by calling
5006   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5007 
5008   Level: advanced
5009 
5010 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5011 @*/
5012 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5013 {
5014   DMLabel        label;
5015 
5016   PetscFunctionBegin;
5017   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5018   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5019   PetscCall(DMLabelSetValue(label, cell, celltype));
5020   PetscFunctionReturn(0);
5021 }
5022 
5023 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5024 {
5025   PetscSection   section, s;
5026   Mat            m;
5027   PetscInt       maxHeight;
5028 
5029   PetscFunctionBegin;
5030   PetscCall(DMClone(dm, cdm));
5031   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5032   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5033   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5034   PetscCall(DMSetLocalSection(*cdm, section));
5035   PetscCall(PetscSectionDestroy(&section));
5036   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5037   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5038   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5039   PetscCall(PetscSectionDestroy(&s));
5040   PetscCall(MatDestroy(&m));
5041 
5042   PetscCall(DMSetNumFields(*cdm, 1));
5043   PetscCall(DMCreateDS(*cdm));
5044   PetscFunctionReturn(0);
5045 }
5046 
5047 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5048 {
5049   Vec            coordsLocal;
5050   DM             coordsDM;
5051 
5052   PetscFunctionBegin;
5053   *field = NULL;
5054   PetscCall(DMGetCoordinatesLocal(dm,&coordsLocal));
5055   PetscCall(DMGetCoordinateDM(dm,&coordsDM));
5056   if (coordsLocal && coordsDM) {
5057     PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5058   }
5059   PetscFunctionReturn(0);
5060 }
5061 
5062 /*@C
5063   DMPlexGetConeSection - Return a section which describes the layout of cone data
5064 
5065   Not Collective
5066 
5067   Input Parameters:
5068 . dm        - The DMPlex object
5069 
5070   Output Parameter:
5071 . section - The PetscSection object
5072 
5073   Level: developer
5074 
5075 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5076 @*/
5077 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5078 {
5079   DM_Plex *mesh = (DM_Plex*) dm->data;
5080 
5081   PetscFunctionBegin;
5082   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5083   if (section) *section = mesh->coneSection;
5084   PetscFunctionReturn(0);
5085 }
5086 
5087 /*@C
5088   DMPlexGetSupportSection - Return a section which describes the layout of support data
5089 
5090   Not Collective
5091 
5092   Input Parameters:
5093 . dm        - The DMPlex object
5094 
5095   Output Parameter:
5096 . section - The PetscSection object
5097 
5098   Level: developer
5099 
5100 .seealso: `DMPlexGetConeSection()`
5101 @*/
5102 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5103 {
5104   DM_Plex *mesh = (DM_Plex*) dm->data;
5105 
5106   PetscFunctionBegin;
5107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5108   if (section) *section = mesh->supportSection;
5109   PetscFunctionReturn(0);
5110 }
5111 
5112 /*@C
5113   DMPlexGetCones - Return cone data
5114 
5115   Not Collective
5116 
5117   Input Parameters:
5118 . dm        - The DMPlex object
5119 
5120   Output Parameter:
5121 . cones - The cone for each point
5122 
5123   Level: developer
5124 
5125 .seealso: `DMPlexGetConeSection()`
5126 @*/
5127 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5128 {
5129   DM_Plex *mesh = (DM_Plex*) dm->data;
5130 
5131   PetscFunctionBegin;
5132   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5133   if (cones) *cones = mesh->cones;
5134   PetscFunctionReturn(0);
5135 }
5136 
5137 /*@C
5138   DMPlexGetConeOrientations - Return cone orientation data
5139 
5140   Not Collective
5141 
5142   Input Parameters:
5143 . dm        - The DMPlex object
5144 
5145   Output Parameter:
5146 . coneOrientations - The array of cone orientations for all points
5147 
5148   Level: developer
5149 
5150   Notes:
5151   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5152 
5153   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5154 
5155 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5156 @*/
5157 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5158 {
5159   DM_Plex *mesh = (DM_Plex*) dm->data;
5160 
5161   PetscFunctionBegin;
5162   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5163   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5164   PetscFunctionReturn(0);
5165 }
5166 
5167 /******************************** FEM Support **********************************/
5168 
5169 /*
5170  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5171  representing a line in the section.
5172 */
5173 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
5174 {
5175   PetscFunctionBeginHot;
5176   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5177   if (line < 0) {
5178     *k = 0;
5179     *Nc = 0;
5180   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
5181     *k = 1;
5182   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
5183     /* An order k SEM disc has k-1 dofs on an edge */
5184     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5185     *k = *k / *Nc + 1;
5186   }
5187   PetscFunctionReturn(0);
5188 }
5189 
5190 /*@
5191 
5192   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5193   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5194   section provided (or the section of the DM).
5195 
5196   Input Parameters:
5197 + dm      - The DM
5198 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5199 - section - The PetscSection to reorder, or NULL for the default section
5200 
5201   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5202   degree of the basis.
5203 
5204   Example:
5205   A typical interpolated single-quad mesh might order points as
5206 .vb
5207   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5208 
5209   v4 -- e6 -- v3
5210   |           |
5211   e7    c0    e8
5212   |           |
5213   v1 -- e5 -- v2
5214 .ve
5215 
5216   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5217   dofs in the order of points, e.g.,
5218 .vb
5219     c0 -> [0,1,2,3]
5220     v1 -> [4]
5221     ...
5222     e5 -> [8, 9]
5223 .ve
5224 
5225   which corresponds to the dofs
5226 .vb
5227     6   10  11  7
5228     13  2   3   15
5229     12  0   1   14
5230     4   8   9   5
5231 .ve
5232 
5233   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5234 .vb
5235   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5236 .ve
5237 
5238   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5239 .vb
5240    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5241 .ve
5242 
5243   Level: developer
5244 
5245 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5246 @*/
5247 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5248 {
5249   DMLabel        label;
5250   PetscInt       dim, depth = -1, eStart = -1, Nf;
5251   PetscBool      vertexchart;
5252 
5253   PetscFunctionBegin;
5254   PetscCall(DMGetDimension(dm, &dim));
5255   if (dim < 1) PetscFunctionReturn(0);
5256   if (point < 0) {
5257     PetscInt sStart,sEnd;
5258 
5259     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5260     point = sEnd-sStart ? sStart : point;
5261   }
5262   PetscCall(DMPlexGetDepthLabel(dm, &label));
5263   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5264   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5265   if (depth == 1) {eStart = point;}
5266   else if  (depth == dim) {
5267     const PetscInt *cone;
5268 
5269     PetscCall(DMPlexGetCone(dm, point, &cone));
5270     if (dim == 2) eStart = cone[0];
5271     else if (dim == 3) {
5272       const PetscInt *cone2;
5273       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5274       eStart = cone2[0];
5275     } 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);
5276   } 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);
5277   {                             /* Determine whether the chart covers all points or just vertices. */
5278     PetscInt pStart,pEnd,cStart,cEnd;
5279     PetscCall(DMPlexGetDepthStratum(dm,0,&pStart,&pEnd));
5280     PetscCall(PetscSectionGetChart(section,&cStart,&cEnd));
5281     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5282     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5283     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5284   }
5285   PetscCall(PetscSectionGetNumFields(section, &Nf));
5286   for (PetscInt d=1; d<=dim; d++) {
5287     PetscInt k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5288     PetscInt *perm;
5289 
5290     for (f = 0; f < Nf; ++f) {
5291       PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5292       size += PetscPowInt(k+1, d)*Nc;
5293     }
5294     PetscCall(PetscMalloc1(size, &perm));
5295     for (f = 0; f < Nf; ++f) {
5296       switch (d) {
5297       case 1:
5298         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5299         /*
5300          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5301          We want              [ vtx0; edge of length k-1; vtx1 ]
5302          */
5303         for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
5304         for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
5305         for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
5306         foffset = offset;
5307         break;
5308       case 2:
5309         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5310         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5311         /* The SEM order is
5312 
5313          v_lb, {e_b}, v_rb,
5314          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5315          v_lt, reverse {e_t}, v_rt
5316          */
5317         {
5318           const PetscInt of   = 0;
5319           const PetscInt oeb  = of   + PetscSqr(k-1);
5320           const PetscInt oer  = oeb  + (k-1);
5321           const PetscInt oet  = oer  + (k-1);
5322           const PetscInt oel  = oet  + (k-1);
5323           const PetscInt ovlb = oel  + (k-1);
5324           const PetscInt ovrb = ovlb + 1;
5325           const PetscInt ovrt = ovrb + 1;
5326           const PetscInt ovlt = ovrt + 1;
5327           PetscInt       o;
5328 
5329           /* bottom */
5330           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
5331           for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5332           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
5333           /* middle */
5334           for (i = 0; i < k-1; ++i) {
5335             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
5336             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;
5337             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
5338           }
5339           /* top */
5340           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
5341           for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5342           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
5343           foffset = offset;
5344         }
5345         break;
5346       case 3:
5347         /* The original hex closure is
5348 
5349          {c,
5350          f_b, f_t, f_f, f_b, f_r, f_l,
5351          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5352          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5353          */
5354         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5355         /* The SEM order is
5356          Bottom Slice
5357          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5358          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5359          v_blb, {e_bb}, v_brb,
5360 
5361          Middle Slice (j)
5362          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5363          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5364          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5365 
5366          Top Slice
5367          v_tlf, {e_tf}, v_trf,
5368          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5369          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5370          */
5371         {
5372           const PetscInt oc    = 0;
5373           const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
5374           const PetscInt oft   = ofb   + PetscSqr(k-1);
5375           const PetscInt off   = oft   + PetscSqr(k-1);
5376           const PetscInt ofk   = off   + PetscSqr(k-1);
5377           const PetscInt ofr   = ofk   + PetscSqr(k-1);
5378           const PetscInt ofl   = ofr   + PetscSqr(k-1);
5379           const PetscInt oebl  = ofl   + PetscSqr(k-1);
5380           const PetscInt oebb  = oebl  + (k-1);
5381           const PetscInt oebr  = oebb  + (k-1);
5382           const PetscInt oebf  = oebr  + (k-1);
5383           const PetscInt oetf  = oebf  + (k-1);
5384           const PetscInt oetr  = oetf  + (k-1);
5385           const PetscInt oetb  = oetr  + (k-1);
5386           const PetscInt oetl  = oetb  + (k-1);
5387           const PetscInt oerf  = oetl  + (k-1);
5388           const PetscInt oelf  = oerf  + (k-1);
5389           const PetscInt oelb  = oelf  + (k-1);
5390           const PetscInt oerb  = oelb  + (k-1);
5391           const PetscInt ovblf = oerb  + (k-1);
5392           const PetscInt ovblb = ovblf + 1;
5393           const PetscInt ovbrb = ovblb + 1;
5394           const PetscInt ovbrf = ovbrb + 1;
5395           const PetscInt ovtlf = ovbrf + 1;
5396           const PetscInt ovtrf = ovtlf + 1;
5397           const PetscInt ovtrb = ovtrf + 1;
5398           const PetscInt ovtlb = ovtrb + 1;
5399           PetscInt       o, n;
5400 
5401           /* Bottom Slice */
5402           /*   bottom */
5403           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
5404           for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5405           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
5406           /*   middle */
5407           for (i = 0; i < k-1; ++i) {
5408             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
5409             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;}
5410             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
5411           }
5412           /*   top */
5413           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
5414           for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5415           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
5416 
5417           /* Middle Slice */
5418           for (j = 0; j < k-1; ++j) {
5419             /*   bottom */
5420             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
5421             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;
5422             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
5423             /*   middle */
5424             for (i = 0; i < k-1; ++i) {
5425               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
5426               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;
5427               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
5428             }
5429             /*   top */
5430             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
5431             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;
5432             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
5433           }
5434 
5435           /* Top Slice */
5436           /*   bottom */
5437           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
5438           for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5439           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
5440           /*   middle */
5441           for (i = 0; i < k-1; ++i) {
5442             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
5443             for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
5444             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
5445           }
5446           /*   top */
5447           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
5448           for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5449           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
5450 
5451           foffset = offset;
5452         }
5453         break;
5454       default: SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5455       }
5456     }
5457     PetscCheck(offset == size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5458     /* Check permutation */
5459     {
5460       PetscInt *check;
5461 
5462       PetscCall(PetscMalloc1(size, &check));
5463       for (i = 0; i < size; ++i) {
5464         check[i] = -1;
5465         PetscCheck(perm[i] >= 0 && perm[i] < size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5466       }
5467       for (i = 0; i < size; ++i) check[perm[i]] = i;
5468       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5469       PetscCall(PetscFree(check));
5470     }
5471     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size, PETSC_OWN_POINTER, perm));
5472     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5473       PetscInt *loc_perm;
5474       PetscCall(PetscMalloc1(size*2, &loc_perm));
5475       for (PetscInt i=0; i<size; i++) {
5476         loc_perm[i] = perm[i];
5477         loc_perm[size+i] = size + perm[i];
5478       }
5479       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size*2, PETSC_OWN_POINTER, loc_perm));
5480     }
5481   }
5482   PetscFunctionReturn(0);
5483 }
5484 
5485 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5486 {
5487   PetscDS        prob;
5488   PetscInt       depth, Nf, h;
5489   DMLabel        label;
5490 
5491   PetscFunctionBeginHot;
5492   PetscCall(DMGetDS(dm, &prob));
5493   Nf      = prob->Nf;
5494   label   = dm->depthLabel;
5495   *dspace = NULL;
5496   if (field < Nf) {
5497     PetscObject disc = prob->disc[field];
5498 
5499     if (disc->classid == PETSCFE_CLASSID) {
5500       PetscDualSpace dsp;
5501 
5502       PetscCall(PetscFEGetDualSpace((PetscFE)disc,&dsp));
5503       PetscCall(DMLabelGetNumValues(label,&depth));
5504       PetscCall(DMLabelGetValue(label,point,&h));
5505       h    = depth - 1 - h;
5506       if (h) {
5507         PetscCall(PetscDualSpaceGetHeightSubspace(dsp,h,dspace));
5508       } else {
5509         *dspace = dsp;
5510       }
5511     }
5512   }
5513   PetscFunctionReturn(0);
5514 }
5515 
5516 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5517 {
5518   PetscScalar    *array, *vArray;
5519   const PetscInt *cone, *coneO;
5520   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
5521 
5522   PetscFunctionBeginHot;
5523   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5524   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5525   PetscCall(DMPlexGetCone(dm, point, &cone));
5526   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5527   if (!values || !*values) {
5528     if ((point >= pStart) && (point < pEnd)) {
5529       PetscInt dof;
5530 
5531       PetscCall(PetscSectionGetDof(section, point, &dof));
5532       size += dof;
5533     }
5534     for (p = 0; p < numPoints; ++p) {
5535       const PetscInt cp = cone[p];
5536       PetscInt       dof;
5537 
5538       if ((cp < pStart) || (cp >= pEnd)) continue;
5539       PetscCall(PetscSectionGetDof(section, cp, &dof));
5540       size += dof;
5541     }
5542     if (!values) {
5543       if (csize) *csize = size;
5544       PetscFunctionReturn(0);
5545     }
5546     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5547   } else {
5548     array = *values;
5549   }
5550   size = 0;
5551   PetscCall(VecGetArray(v, &vArray));
5552   if ((point >= pStart) && (point < pEnd)) {
5553     PetscInt     dof, off, d;
5554     PetscScalar *varr;
5555 
5556     PetscCall(PetscSectionGetDof(section, point, &dof));
5557     PetscCall(PetscSectionGetOffset(section, point, &off));
5558     varr = &vArray[off];
5559     for (d = 0; d < dof; ++d, ++offset) {
5560       array[offset] = varr[d];
5561     }
5562     size += dof;
5563   }
5564   for (p = 0; p < numPoints; ++p) {
5565     const PetscInt cp = cone[p];
5566     PetscInt       o  = coneO[p];
5567     PetscInt       dof, off, d;
5568     PetscScalar   *varr;
5569 
5570     if ((cp < pStart) || (cp >= pEnd)) continue;
5571     PetscCall(PetscSectionGetDof(section, cp, &dof));
5572     PetscCall(PetscSectionGetOffset(section, cp, &off));
5573     varr = &vArray[off];
5574     if (o >= 0) {
5575       for (d = 0; d < dof; ++d, ++offset) {
5576         array[offset] = varr[d];
5577       }
5578     } else {
5579       for (d = dof-1; d >= 0; --d, ++offset) {
5580         array[offset] = varr[d];
5581       }
5582     }
5583     size += dof;
5584   }
5585   PetscCall(VecRestoreArray(v, &vArray));
5586   if (!*values) {
5587     if (csize) *csize = size;
5588     *values = array;
5589   } else {
5590     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5591     *csize = size;
5592   }
5593   PetscFunctionReturn(0);
5594 }
5595 
5596 /* Compress out points not in the section */
5597 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5598 {
5599   const PetscInt np = *numPoints;
5600   PetscInt       pStart, pEnd, p, q;
5601 
5602   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5603   for (p = 0, q = 0; p < np; ++p) {
5604     const PetscInt r = points[p*2];
5605     if ((r >= pStart) && (r < pEnd)) {
5606       points[q*2]   = r;
5607       points[q*2+1] = points[p*2+1];
5608       ++q;
5609     }
5610   }
5611   *numPoints = q;
5612   return 0;
5613 }
5614 
5615 /* Compressed closure does not apply closure permutation */
5616 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5617 {
5618   const PetscInt *cla = NULL;
5619   PetscInt       np, *pts = NULL;
5620 
5621   PetscFunctionBeginHot;
5622   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints));
5623   if (*clPoints) {
5624     PetscInt dof, off;
5625 
5626     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5627     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5628     PetscCall(ISGetIndices(*clPoints, &cla));
5629     np   = dof/2;
5630     pts  = (PetscInt *) &cla[off];
5631   } else {
5632     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5633     PetscCall(CompressPoints_Private(section, &np, pts));
5634   }
5635   *numPoints = np;
5636   *points    = pts;
5637   *clp       = cla;
5638   PetscFunctionReturn(0);
5639 }
5640 
5641 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5642 {
5643   PetscFunctionBeginHot;
5644   if (!*clPoints) {
5645     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5646   } else {
5647     PetscCall(ISRestoreIndices(*clPoints, clp));
5648   }
5649   *numPoints = 0;
5650   *points    = NULL;
5651   *clSec     = NULL;
5652   *clPoints  = NULL;
5653   *clp       = NULL;
5654   PetscFunctionReturn(0);
5655 }
5656 
5657 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5658 {
5659   PetscInt          offset = 0, p;
5660   const PetscInt    **perms = NULL;
5661   const PetscScalar **flips = NULL;
5662 
5663   PetscFunctionBeginHot;
5664   *size = 0;
5665   PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
5666   for (p = 0; p < numPoints; p++) {
5667     const PetscInt    point = points[2*p];
5668     const PetscInt    *perm = perms ? perms[p] : NULL;
5669     const PetscScalar *flip = flips ? flips[p] : NULL;
5670     PetscInt          dof, off, d;
5671     const PetscScalar *varr;
5672 
5673     PetscCall(PetscSectionGetDof(section, point, &dof));
5674     PetscCall(PetscSectionGetOffset(section, point, &off));
5675     varr = &vArray[off];
5676     if (clperm) {
5677       if (perm) {
5678         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5679       } else {
5680         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5681       }
5682       if (flip) {
5683         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5684       }
5685     } else {
5686       if (perm) {
5687         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5688       } else {
5689         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5690       }
5691       if (flip) {
5692         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5693       }
5694     }
5695     offset += dof;
5696   }
5697   PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
5698   *size = offset;
5699   PetscFunctionReturn(0);
5700 }
5701 
5702 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[])
5703 {
5704   PetscInt          offset = 0, f;
5705 
5706   PetscFunctionBeginHot;
5707   *size = 0;
5708   for (f = 0; f < numFields; ++f) {
5709     PetscInt          p;
5710     const PetscInt    **perms = NULL;
5711     const PetscScalar **flips = NULL;
5712 
5713     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5714     for (p = 0; p < numPoints; p++) {
5715       const PetscInt    point = points[2*p];
5716       PetscInt          fdof, foff, b;
5717       const PetscScalar *varr;
5718       const PetscInt    *perm = perms ? perms[p] : NULL;
5719       const PetscScalar *flip = flips ? flips[p] : NULL;
5720 
5721       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5722       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5723       varr = &vArray[foff];
5724       if (clperm) {
5725         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5726         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5727         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5728       } else {
5729         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5730         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5731         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5732       }
5733       offset += fdof;
5734     }
5735     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5736   }
5737   *size = offset;
5738   PetscFunctionReturn(0);
5739 }
5740 
5741 /*@C
5742   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5743 
5744   Not collective
5745 
5746   Input Parameters:
5747 + dm - The DM
5748 . section - The section describing the layout in v, or NULL to use the default section
5749 . v - The local vector
5750 - point - The point in the DM
5751 
5752   Input/Output Parameters:
5753 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5754 - values - An array to use for the values, or NULL to have it allocated automatically;
5755            if the user provided NULL, it is a borrowed array and should not be freed
5756 
5757 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5758 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5759 $ assembly function, and a user may already have allocated storage for this operation.
5760 $
5761 $ A typical use could be
5762 $
5763 $  values = NULL;
5764 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5765 $  for (cl = 0; cl < clSize; ++cl) {
5766 $    <Compute on closure>
5767 $  }
5768 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5769 $
5770 $ or
5771 $
5772 $  PetscMalloc1(clMaxSize, &values);
5773 $  for (p = pStart; p < pEnd; ++p) {
5774 $    clSize = clMaxSize;
5775 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5776 $    for (cl = 0; cl < clSize; ++cl) {
5777 $      <Compute on closure>
5778 $    }
5779 $  }
5780 $  PetscFree(values);
5781 
5782   Fortran Notes:
5783   Since it returns an array, this routine is only available in Fortran 90, and you must
5784   include petsc.h90 in your code.
5785 
5786   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5787 
5788   Level: intermediate
5789 
5790 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5791 @*/
5792 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5793 {
5794   PetscSection       clSection;
5795   IS                 clPoints;
5796   PetscInt          *points = NULL;
5797   const PetscInt    *clp, *perm;
5798   PetscInt           depth, numFields, numPoints, asize;
5799 
5800   PetscFunctionBeginHot;
5801   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5802   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5803   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5804   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5805   PetscCall(DMPlexGetDepth(dm, &depth));
5806   PetscCall(PetscSectionGetNumFields(section, &numFields));
5807   if (depth == 1 && numFields < 2) {
5808     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5809     PetscFunctionReturn(0);
5810   }
5811   /* Get points */
5812   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5813   /* Get sizes */
5814   asize = 0;
5815   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5816     PetscInt dof;
5817     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5818     asize += dof;
5819   }
5820   if (values) {
5821     const PetscScalar *vArray;
5822     PetscInt          size;
5823 
5824     if (*values) {
5825       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);
5826     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5827     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm));
5828     PetscCall(VecGetArrayRead(v, &vArray));
5829     /* Get values */
5830     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5831     else               PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5832     PetscCheck(asize == size,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5833     /* Cleanup array */
5834     PetscCall(VecRestoreArrayRead(v, &vArray));
5835   }
5836   if (csize) *csize = asize;
5837   /* Cleanup points */
5838   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5839   PetscFunctionReturn(0);
5840 }
5841 
5842 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5843 {
5844   DMLabel            depthLabel;
5845   PetscSection       clSection;
5846   IS                 clPoints;
5847   PetscScalar       *array;
5848   const PetscScalar *vArray;
5849   PetscInt          *points = NULL;
5850   const PetscInt    *clp, *perm = NULL;
5851   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5852 
5853   PetscFunctionBeginHot;
5854   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5855   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5856   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5857   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5858   PetscCall(DMPlexGetDepth(dm, &mdepth));
5859   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5860   PetscCall(PetscSectionGetNumFields(section, &numFields));
5861   if (mdepth == 1 && numFields < 2) {
5862     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5863     PetscFunctionReturn(0);
5864   }
5865   /* Get points */
5866   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5867   for (clsize=0,p=0; p<Np; p++) {
5868     PetscInt dof;
5869     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
5870     clsize += dof;
5871   }
5872   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm));
5873   /* Filter points */
5874   for (p = 0; p < numPoints*2; p += 2) {
5875     PetscInt dep;
5876 
5877     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5878     if (dep != depth) continue;
5879     points[Np*2+0] = points[p];
5880     points[Np*2+1] = points[p+1];
5881     ++Np;
5882   }
5883   /* Get array */
5884   if (!values || !*values) {
5885     PetscInt asize = 0, dof;
5886 
5887     for (p = 0; p < Np*2; p += 2) {
5888       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5889       asize += dof;
5890     }
5891     if (!values) {
5892       PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5893       if (csize) *csize = asize;
5894       PetscFunctionReturn(0);
5895     }
5896     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5897   } else {
5898     array = *values;
5899   }
5900   PetscCall(VecGetArrayRead(v, &vArray));
5901   /* Get values */
5902   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5903   else               PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5904   /* Cleanup points */
5905   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5906   /* Cleanup array */
5907   PetscCall(VecRestoreArrayRead(v, &vArray));
5908   if (!*values) {
5909     if (csize) *csize = size;
5910     *values = array;
5911   } else {
5912     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5913     *csize = size;
5914   }
5915   PetscFunctionReturn(0);
5916 }
5917 
5918 /*@C
5919   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5920 
5921   Not collective
5922 
5923   Input Parameters:
5924 + dm - The DM
5925 . section - The section describing the layout in v, or NULL to use the default section
5926 . v - The local vector
5927 . point - The point in the DM
5928 . csize - The number of values in the closure, or NULL
5929 - values - The array of values, which is a borrowed array and should not be freed
5930 
5931   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5932 
5933   Fortran Notes:
5934   Since it returns an array, this routine is only available in Fortran 90, and you must
5935   include petsc.h90 in your code.
5936 
5937   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5938 
5939   Level: intermediate
5940 
5941 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5942 @*/
5943 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5944 {
5945   PetscInt       size = 0;
5946 
5947   PetscFunctionBegin;
5948   /* Should work without recalculating size */
5949   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values));
5950   *values = NULL;
5951   PetscFunctionReturn(0);
5952 }
5953 
5954 static inline void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5955 static inline void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5956 
5957 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[])
5958 {
5959   PetscInt        cdof;   /* The number of constraints on this point */
5960   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5961   PetscScalar    *a;
5962   PetscInt        off, cind = 0, k;
5963 
5964   PetscFunctionBegin;
5965   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5966   PetscCall(PetscSectionGetOffset(section, point, &off));
5967   a    = &array[off];
5968   if (!cdof || setBC) {
5969     if (clperm) {
5970       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5971       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5972     } else {
5973       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5974       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5975     }
5976   } else {
5977     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5978     if (clperm) {
5979       if (perm) {for (k = 0; k < dof; ++k) {
5980           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5981           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5982         }
5983       } else {
5984         for (k = 0; k < dof; ++k) {
5985           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5986           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5987         }
5988       }
5989     } else {
5990       if (perm) {
5991         for (k = 0; k < dof; ++k) {
5992           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5993           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
5994         }
5995       } else {
5996         for (k = 0; k < dof; ++k) {
5997           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5998           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
5999         }
6000       }
6001     }
6002   }
6003   PetscFunctionReturn(0);
6004 }
6005 
6006 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[])
6007 {
6008   PetscInt        cdof;   /* The number of constraints on this point */
6009   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6010   PetscScalar    *a;
6011   PetscInt        off, cind = 0, k;
6012 
6013   PetscFunctionBegin;
6014   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6015   PetscCall(PetscSectionGetOffset(section, point, &off));
6016   a    = &array[off];
6017   if (cdof) {
6018     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6019     if (clperm) {
6020       if (perm) {
6021         for (k = 0; k < dof; ++k) {
6022           if ((cind < cdof) && (k == cdofs[cind])) {
6023             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
6024             cind++;
6025           }
6026         }
6027       } else {
6028         for (k = 0; k < dof; ++k) {
6029           if ((cind < cdof) && (k == cdofs[cind])) {
6030             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
6031             cind++;
6032           }
6033         }
6034       }
6035     } else {
6036       if (perm) {
6037         for (k = 0; k < dof; ++k) {
6038           if ((cind < cdof) && (k == cdofs[cind])) {
6039             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6040             cind++;
6041           }
6042         }
6043       } else {
6044         for (k = 0; k < dof; ++k) {
6045           if ((cind < cdof) && (k == cdofs[cind])) {
6046             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6047             cind++;
6048           }
6049         }
6050       }
6051     }
6052   }
6053   PetscFunctionReturn(0);
6054 }
6055 
6056 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[])
6057 {
6058   PetscScalar    *a;
6059   PetscInt        fdof, foff, fcdof, foffset = *offset;
6060   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6061   PetscInt        cind = 0, b;
6062 
6063   PetscFunctionBegin;
6064   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6065   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6066   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6067   a    = &array[foff];
6068   if (!fcdof || setBC) {
6069     if (clperm) {
6070       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
6071       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
6072     } else {
6073       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
6074       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
6075     }
6076   } else {
6077     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6078     if (clperm) {
6079       if (perm) {
6080         for (b = 0; b < fdof; b++) {
6081           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6082           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6083         }
6084       } else {
6085         for (b = 0; b < fdof; b++) {
6086           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6087           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6088         }
6089       }
6090     } else {
6091       if (perm) {
6092         for (b = 0; b < fdof; b++) {
6093           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6094           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6095         }
6096       } else {
6097         for (b = 0; b < fdof; b++) {
6098           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6099           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6100         }
6101       }
6102     }
6103   }
6104   *offset += fdof;
6105   PetscFunctionReturn(0);
6106 }
6107 
6108 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[])
6109 {
6110   PetscScalar    *a;
6111   PetscInt        fdof, foff, fcdof, foffset = *offset;
6112   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6113   PetscInt        Nc, cind = 0, ncind = 0, b;
6114   PetscBool       ncSet, fcSet;
6115 
6116   PetscFunctionBegin;
6117   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6118   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6119   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6120   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6121   a    = &array[foff];
6122   if (fcdof) {
6123     /* We just override fcdof and fcdofs with Ncc and comps */
6124     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6125     if (clperm) {
6126       if (perm) {
6127         if (comps) {
6128           for (b = 0; b < fdof; b++) {
6129             ncSet = fcSet = PETSC_FALSE;
6130             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6131             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6132             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
6133           }
6134         } else {
6135           for (b = 0; b < fdof; b++) {
6136             if ((cind < fcdof) && (b == fcdofs[cind])) {
6137               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6138               ++cind;
6139             }
6140           }
6141         }
6142       } else {
6143         if (comps) {
6144           for (b = 0; b < fdof; b++) {
6145             ncSet = fcSet = PETSC_FALSE;
6146             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6147             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6148             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
6149           }
6150         } else {
6151           for (b = 0; b < fdof; b++) {
6152             if ((cind < fcdof) && (b == fcdofs[cind])) {
6153               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6154               ++cind;
6155             }
6156           }
6157         }
6158       }
6159     } else {
6160       if (perm) {
6161         if (comps) {
6162           for (b = 0; b < fdof; b++) {
6163             ncSet = fcSet = PETSC_FALSE;
6164             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6165             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6166             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
6167           }
6168         } else {
6169           for (b = 0; b < fdof; b++) {
6170             if ((cind < fcdof) && (b == fcdofs[cind])) {
6171               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6172               ++cind;
6173             }
6174           }
6175         }
6176       } else {
6177         if (comps) {
6178           for (b = 0; b < fdof; b++) {
6179             ncSet = fcSet = PETSC_FALSE;
6180             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6181             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6182             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
6183           }
6184         } else {
6185           for (b = 0; b < fdof; b++) {
6186             if ((cind < fcdof) && (b == fcdofs[cind])) {
6187               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6188               ++cind;
6189             }
6190           }
6191         }
6192       }
6193     }
6194   }
6195   *offset += fdof;
6196   PetscFunctionReturn(0);
6197 }
6198 
6199 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6200 {
6201   PetscScalar    *array;
6202   const PetscInt *cone, *coneO;
6203   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6204 
6205   PetscFunctionBeginHot;
6206   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6207   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6208   PetscCall(DMPlexGetCone(dm, point, &cone));
6209   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6210   PetscCall(VecGetArray(v, &array));
6211   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6212     const PetscInt cp = !p ? point : cone[p-1];
6213     const PetscInt o  = !p ? 0     : coneO[p-1];
6214 
6215     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
6216     PetscCall(PetscSectionGetDof(section, cp, &dof));
6217     /* ADD_VALUES */
6218     {
6219       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6220       PetscScalar    *a;
6221       PetscInt        cdof, coff, cind = 0, k;
6222 
6223       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6224       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6225       a    = &array[coff];
6226       if (!cdof) {
6227         if (o >= 0) {
6228           for (k = 0; k < dof; ++k) {
6229             a[k] += values[off+k];
6230           }
6231         } else {
6232           for (k = 0; k < dof; ++k) {
6233             a[k] += values[off+dof-k-1];
6234           }
6235         }
6236       } else {
6237         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6238         if (o >= 0) {
6239           for (k = 0; k < dof; ++k) {
6240             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6241             a[k] += values[off+k];
6242           }
6243         } else {
6244           for (k = 0; k < dof; ++k) {
6245             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6246             a[k] += values[off+dof-k-1];
6247           }
6248         }
6249       }
6250     }
6251   }
6252   PetscCall(VecRestoreArray(v, &array));
6253   PetscFunctionReturn(0);
6254 }
6255 
6256 /*@C
6257   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6258 
6259   Not collective
6260 
6261   Input Parameters:
6262 + dm - The DM
6263 . section - The section describing the layout in v, or NULL to use the default section
6264 . v - The local vector
6265 . point - The point in the DM
6266 . values - The array of values
6267 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6268          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6269 
6270   Fortran Notes:
6271   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6272 
6273   Level: intermediate
6274 
6275 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6276 @*/
6277 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6278 {
6279   PetscSection    clSection;
6280   IS              clPoints;
6281   PetscScalar    *array;
6282   PetscInt       *points = NULL;
6283   const PetscInt *clp, *clperm = NULL;
6284   PetscInt        depth, numFields, numPoints, p, clsize;
6285 
6286   PetscFunctionBeginHot;
6287   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6288   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6289   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6290   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6291   PetscCall(DMPlexGetDepth(dm, &depth));
6292   PetscCall(PetscSectionGetNumFields(section, &numFields));
6293   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6294     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6295     PetscFunctionReturn(0);
6296   }
6297   /* Get points */
6298   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6299   for (clsize=0,p=0; p<numPoints; p++) {
6300     PetscInt dof;
6301     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
6302     clsize += dof;
6303   }
6304   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
6305   /* Get array */
6306   PetscCall(VecGetArray(v, &array));
6307   /* Get values */
6308   if (numFields > 0) {
6309     PetscInt offset = 0, f;
6310     for (f = 0; f < numFields; ++f) {
6311       const PetscInt    **perms = NULL;
6312       const PetscScalar **flips = NULL;
6313 
6314       PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6315       switch (mode) {
6316       case INSERT_VALUES:
6317         for (p = 0; p < numPoints; p++) {
6318           const PetscInt    point = points[2*p];
6319           const PetscInt    *perm = perms ? perms[p] : NULL;
6320           const PetscScalar *flip = flips ? flips[p] : NULL;
6321           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6322         } break;
6323       case INSERT_ALL_VALUES:
6324         for (p = 0; p < numPoints; p++) {
6325           const PetscInt    point = points[2*p];
6326           const PetscInt    *perm = perms ? perms[p] : NULL;
6327           const PetscScalar *flip = flips ? flips[p] : NULL;
6328           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6329         } break;
6330       case INSERT_BC_VALUES:
6331         for (p = 0; p < numPoints; p++) {
6332           const PetscInt    point = points[2*p];
6333           const PetscInt    *perm = perms ? perms[p] : NULL;
6334           const PetscScalar *flip = flips ? flips[p] : NULL;
6335           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6336         } break;
6337       case ADD_VALUES:
6338         for (p = 0; p < numPoints; p++) {
6339           const PetscInt    point = points[2*p];
6340           const PetscInt    *perm = perms ? perms[p] : NULL;
6341           const PetscScalar *flip = flips ? flips[p] : NULL;
6342           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6343         } break;
6344       case ADD_ALL_VALUES:
6345         for (p = 0; p < numPoints; p++) {
6346           const PetscInt    point = points[2*p];
6347           const PetscInt    *perm = perms ? perms[p] : NULL;
6348           const PetscScalar *flip = flips ? flips[p] : NULL;
6349           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6350         } break;
6351       case ADD_BC_VALUES:
6352         for (p = 0; p < numPoints; p++) {
6353           const PetscInt    point = points[2*p];
6354           const PetscInt    *perm = perms ? perms[p] : NULL;
6355           const PetscScalar *flip = flips ? flips[p] : NULL;
6356           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6357         } break;
6358       default:
6359         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6360       }
6361       PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6362     }
6363   } else {
6364     PetscInt dof, off;
6365     const PetscInt    **perms = NULL;
6366     const PetscScalar **flips = NULL;
6367 
6368     PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
6369     switch (mode) {
6370     case INSERT_VALUES:
6371       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6372         const PetscInt    point = points[2*p];
6373         const PetscInt    *perm = perms ? perms[p] : NULL;
6374         const PetscScalar *flip = flips ? flips[p] : NULL;
6375         PetscCall(PetscSectionGetDof(section, point, &dof));
6376         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6377       } break;
6378     case INSERT_ALL_VALUES:
6379       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6380         const PetscInt    point = points[2*p];
6381         const PetscInt    *perm = perms ? perms[p] : NULL;
6382         const PetscScalar *flip = flips ? flips[p] : NULL;
6383         PetscCall(PetscSectionGetDof(section, point, &dof));
6384         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
6385       } break;
6386     case INSERT_BC_VALUES:
6387       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6388         const PetscInt    point = points[2*p];
6389         const PetscInt    *perm = perms ? perms[p] : NULL;
6390         const PetscScalar *flip = flips ? flips[p] : NULL;
6391         PetscCall(PetscSectionGetDof(section, point, &dof));
6392         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
6393       } break;
6394     case ADD_VALUES:
6395       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6396         const PetscInt    point = points[2*p];
6397         const PetscInt    *perm = perms ? perms[p] : NULL;
6398         const PetscScalar *flip = flips ? flips[p] : NULL;
6399         PetscCall(PetscSectionGetDof(section, point, &dof));
6400         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
6401       } break;
6402     case ADD_ALL_VALUES:
6403       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6404         const PetscInt    point = points[2*p];
6405         const PetscInt    *perm = perms ? perms[p] : NULL;
6406         const PetscScalar *flip = flips ? flips[p] : NULL;
6407         PetscCall(PetscSectionGetDof(section, point, &dof));
6408         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
6409       } break;
6410     case ADD_BC_VALUES:
6411       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6412         const PetscInt    point = points[2*p];
6413         const PetscInt    *perm = perms ? perms[p] : NULL;
6414         const PetscScalar *flip = flips ? flips[p] : NULL;
6415         PetscCall(PetscSectionGetDof(section, point, &dof));
6416         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
6417       } break;
6418     default:
6419       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6420     }
6421     PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
6422   }
6423   /* Cleanup points */
6424   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6425   /* Cleanup array */
6426   PetscCall(VecRestoreArray(v, &array));
6427   PetscFunctionReturn(0);
6428 }
6429 
6430 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6431 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
6432 {
6433   PetscFunctionBegin;
6434   if (label) {
6435     PetscInt       val, fdof;
6436 
6437     /* There is a problem with this:
6438          Suppose we have two label values, defining surfaces, interecting along a line in 3D. When we add cells to the label, the cells that
6439        touch both surfaces must pick a label value. Thus we miss setting values for the surface with that other value intersecting that cell.
6440        Thus I am only going to check val != -1, not val != labelId
6441     */
6442     PetscCall(DMLabelGetValue(label, point, &val));
6443     if (val < 0) {
6444       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6445       *offset += fdof;
6446       PetscFunctionReturn(1);
6447     }
6448   }
6449   PetscFunctionReturn(0);
6450 }
6451 
6452 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6453 PetscErrorCode DMPlexVecSetFieldClosure_Internal(DM dm, PetscSection section, Vec v, PetscBool fieldActive[], PetscInt point, PetscInt Ncc, const PetscInt comps[], DMLabel label, PetscInt labelId, const PetscScalar values[], InsertMode mode)
6454 {
6455   PetscSection    clSection;
6456   IS              clPoints;
6457   PetscScalar    *array;
6458   PetscInt       *points = NULL;
6459   const PetscInt *clp;
6460   PetscInt        numFields, numPoints, p;
6461   PetscInt        offset = 0, f;
6462 
6463   PetscFunctionBeginHot;
6464   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6465   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6466   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6467   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6468   PetscCall(PetscSectionGetNumFields(section, &numFields));
6469   /* Get points */
6470   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6471   /* Get array */
6472   PetscCall(VecGetArray(v, &array));
6473   /* Get values */
6474   for (f = 0; f < numFields; ++f) {
6475     const PetscInt    **perms = NULL;
6476     const PetscScalar **flips = NULL;
6477 
6478     if (!fieldActive[f]) {
6479       for (p = 0; p < numPoints*2; p += 2) {
6480         PetscInt fdof;
6481         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6482         offset += fdof;
6483       }
6484       continue;
6485     }
6486     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6487     switch (mode) {
6488     case INSERT_VALUES:
6489       for (p = 0; p < numPoints; p++) {
6490         const PetscInt    point = points[2*p];
6491         const PetscInt    *perm = perms ? perms[p] : NULL;
6492         const PetscScalar *flip = flips ? flips[p] : NULL;
6493         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6494         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6495       } break;
6496     case INSERT_ALL_VALUES:
6497       for (p = 0; p < numPoints; p++) {
6498         const PetscInt    point = points[2*p];
6499         const PetscInt    *perm = perms ? perms[p] : NULL;
6500         const PetscScalar *flip = flips ? flips[p] : NULL;
6501         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6502         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6503       } break;
6504     case INSERT_BC_VALUES:
6505       for (p = 0; p < numPoints; p++) {
6506         const PetscInt    point = points[2*p];
6507         const PetscInt    *perm = perms ? perms[p] : NULL;
6508         const PetscScalar *flip = flips ? flips[p] : NULL;
6509         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6510         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6511       } break;
6512     case ADD_VALUES:
6513       for (p = 0; p < numPoints; p++) {
6514         const PetscInt    point = points[2*p];
6515         const PetscInt    *perm = perms ? perms[p] : NULL;
6516         const PetscScalar *flip = flips ? flips[p] : NULL;
6517         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6518         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6519       } break;
6520     case ADD_ALL_VALUES:
6521       for (p = 0; p < numPoints; p++) {
6522         const PetscInt    point = points[2*p];
6523         const PetscInt    *perm = perms ? perms[p] : NULL;
6524         const PetscScalar *flip = flips ? flips[p] : NULL;
6525         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6526         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6527       } break;
6528     default:
6529       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6530     }
6531     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6532   }
6533   /* Cleanup points */
6534   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6535   /* Cleanup array */
6536   PetscCall(VecRestoreArray(v, &array));
6537   PetscFunctionReturn(0);
6538 }
6539 
6540 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6541 {
6542   PetscMPIInt    rank;
6543   PetscInt       i, j;
6544 
6545   PetscFunctionBegin;
6546   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6547   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6548   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6549   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6550   numCIndices = numCIndices ? numCIndices : numRIndices;
6551   if (!values) PetscFunctionReturn(0);
6552   for (i = 0; i < numRIndices; i++) {
6553     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6554     for (j = 0; j < numCIndices; j++) {
6555 #if defined(PETSC_USE_COMPLEX)
6556       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j])));
6557 #else
6558       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]));
6559 #endif
6560     }
6561     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6562   }
6563   PetscFunctionReturn(0);
6564 }
6565 
6566 /*
6567   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6568 
6569   Input Parameters:
6570 + section - The section for this data layout
6571 . islocal - Is the section (and thus indices being requested) local or global?
6572 . point   - The point contributing dofs with these indices
6573 . off     - The global offset of this point
6574 . loff    - The local offset of each field
6575 . setBC   - The flag determining whether to include indices of boundary values
6576 . perm    - A permutation of the dofs on this point, or NULL
6577 - indperm - A permutation of the entire indices array, or NULL
6578 
6579   Output Parameter:
6580 . indices - Indices for dofs on this point
6581 
6582   Level: developer
6583 
6584   Note: The indices could be local or global, depending on the value of 'off'.
6585 */
6586 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6587 {
6588   PetscInt        dof;   /* The number of unknowns on this point */
6589   PetscInt        cdof;  /* The number of constraints on this point */
6590   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6591   PetscInt        cind = 0, k;
6592 
6593   PetscFunctionBegin;
6594   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6595   PetscCall(PetscSectionGetDof(section, point, &dof));
6596   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6597   if (!cdof || setBC) {
6598     for (k = 0; k < dof; ++k) {
6599       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6600       const PetscInt ind    = indperm ? indperm[preind] : preind;
6601 
6602       indices[ind] = off + k;
6603     }
6604   } else {
6605     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6606     for (k = 0; k < dof; ++k) {
6607       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6608       const PetscInt ind    = indperm ? indperm[preind] : preind;
6609 
6610       if ((cind < cdof) && (k == cdofs[cind])) {
6611         /* Insert check for returning constrained indices */
6612         indices[ind] = -(off+k+1);
6613         ++cind;
6614       } else {
6615         indices[ind] = off + k - (islocal ? 0 : cind);
6616       }
6617     }
6618   }
6619   *loff += dof;
6620   PetscFunctionReturn(0);
6621 }
6622 
6623 /*
6624  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6625 
6626  Input Parameters:
6627 + section - a section (global or local)
6628 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6629 . point - point within section
6630 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6631 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6632 . setBC - identify constrained (boundary condition) points via involution.
6633 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6634 . permsoff - offset
6635 - indperm - index permutation
6636 
6637  Output Parameter:
6638 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6639 . indices - array to hold indices (as defined by section) of each dof associated with point
6640 
6641  Notes:
6642  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6643  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6644  in the local vector.
6645 
6646  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6647  significant).  It is invalid to call with a global section and setBC=true.
6648 
6649  Developer Note:
6650  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6651  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6652  offset could be obtained from the section instead of passing it explicitly as we do now.
6653 
6654  Example:
6655  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6656  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6657  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6658  The global vector does not store constrained dofs, so when this function returns global indices, say {110, -112, 111}, the value of -112 is an arbitrary flag that should not be interpreted beyond its sign.
6659 
6660  Level: developer
6661 */
6662 PetscErrorCode DMPlexGetIndicesPointFields_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt foffs[], PetscBool setBC, const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6663 {
6664   PetscInt       numFields, foff, f;
6665 
6666   PetscFunctionBegin;
6667   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6668   PetscCall(PetscSectionGetNumFields(section, &numFields));
6669   for (f = 0, foff = 0; f < numFields; ++f) {
6670     PetscInt        fdof, cfdof;
6671     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6672     PetscInt        cind = 0, b;
6673     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6674 
6675     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6676     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6677     if (!cfdof || setBC) {
6678       for (b = 0; b < fdof; ++b) {
6679         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6680         const PetscInt ind    = indperm ? indperm[preind] : preind;
6681 
6682         indices[ind] = off+foff+b;
6683       }
6684     } else {
6685       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6686       for (b = 0; b < fdof; ++b) {
6687         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6688         const PetscInt ind    = indperm ? indperm[preind] : preind;
6689 
6690         if ((cind < cfdof) && (b == fcdofs[cind])) {
6691           indices[ind] = -(off+foff+b+1);
6692           ++cind;
6693         } else {
6694           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6695         }
6696       }
6697     }
6698     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6699     foffs[f] += fdof;
6700   }
6701   PetscFunctionReturn(0);
6702 }
6703 
6704 /*
6705   This version believes the globalSection offsets for each field, rather than just the point offset
6706 
6707  . foffs - The offset into 'indices' for each field, since it is segregated by field
6708 
6709  Notes:
6710  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6711  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6712 */
6713 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6714 {
6715   PetscInt       numFields, foff, f;
6716 
6717   PetscFunctionBegin;
6718   PetscCall(PetscSectionGetNumFields(section, &numFields));
6719   for (f = 0; f < numFields; ++f) {
6720     PetscInt        fdof, cfdof;
6721     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6722     PetscInt        cind = 0, b;
6723     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6724 
6725     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6726     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6727     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6728     if (!cfdof) {
6729       for (b = 0; b < fdof; ++b) {
6730         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6731         const PetscInt ind    = indperm ? indperm[preind] : preind;
6732 
6733         indices[ind] = foff+b;
6734       }
6735     } else {
6736       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6737       for (b = 0; b < fdof; ++b) {
6738         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6739         const PetscInt ind    = indperm ? indperm[preind] : preind;
6740 
6741         if ((cind < cfdof) && (b == fcdofs[cind])) {
6742           indices[ind] = -(foff+b+1);
6743           ++cind;
6744         } else {
6745           indices[ind] = foff+b-cind;
6746         }
6747       }
6748     }
6749     foffs[f] += fdof;
6750   }
6751   PetscFunctionReturn(0);
6752 }
6753 
6754 PetscErrorCode DMPlexAnchorsModifyMat(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, const PetscScalar values[], PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscScalar *outValues[], PetscInt offsets[], PetscBool multiplyLeft)
6755 {
6756   Mat             cMat;
6757   PetscSection    aSec, cSec;
6758   IS              aIS;
6759   PetscInt        aStart = -1, aEnd = -1;
6760   const PetscInt  *anchors;
6761   PetscInt        numFields, f, p, q, newP = 0;
6762   PetscInt        newNumPoints = 0, newNumIndices = 0;
6763   PetscInt        *newPoints, *indices, *newIndices;
6764   PetscInt        maxAnchor, maxDof;
6765   PetscInt        newOffsets[32];
6766   PetscInt        *pointMatOffsets[32];
6767   PetscInt        *newPointOffsets[32];
6768   PetscScalar     *pointMat[32];
6769   PetscScalar     *newValues=NULL,*tmpValues;
6770   PetscBool       anyConstrained = PETSC_FALSE;
6771 
6772   PetscFunctionBegin;
6773   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6774   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6775   PetscCall(PetscSectionGetNumFields(section, &numFields));
6776 
6777   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
6778   /* if there are point-to-point constraints */
6779   if (aSec) {
6780     PetscCall(PetscArrayzero(newOffsets, 32));
6781     PetscCall(ISGetIndices(aIS,&anchors));
6782     PetscCall(PetscSectionGetChart(aSec,&aStart,&aEnd));
6783     /* figure out how many points are going to be in the new element matrix
6784      * (we allow double counting, because it's all just going to be summed
6785      * into the global matrix anyway) */
6786     for (p = 0; p < 2*numPoints; p+=2) {
6787       PetscInt b    = points[p];
6788       PetscInt bDof = 0, bSecDof;
6789 
6790       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6791       if (!bSecDof) {
6792         continue;
6793       }
6794       if (b >= aStart && b < aEnd) {
6795         PetscCall(PetscSectionGetDof(aSec,b,&bDof));
6796       }
6797       if (bDof) {
6798         /* this point is constrained */
6799         /* it is going to be replaced by its anchors */
6800         PetscInt bOff, q;
6801 
6802         anyConstrained = PETSC_TRUE;
6803         newNumPoints  += bDof;
6804         PetscCall(PetscSectionGetOffset(aSec,b,&bOff));
6805         for (q = 0; q < bDof; q++) {
6806           PetscInt a = anchors[bOff + q];
6807           PetscInt aDof;
6808 
6809           PetscCall(PetscSectionGetDof(section,a,&aDof));
6810           newNumIndices += aDof;
6811           for (f = 0; f < numFields; ++f) {
6812             PetscInt fDof;
6813 
6814             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6815             newOffsets[f+1] += fDof;
6816           }
6817         }
6818       }
6819       else {
6820         /* this point is not constrained */
6821         newNumPoints++;
6822         newNumIndices += bSecDof;
6823         for (f = 0; f < numFields; ++f) {
6824           PetscInt fDof;
6825 
6826           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6827           newOffsets[f+1] += fDof;
6828         }
6829       }
6830     }
6831   }
6832   if (!anyConstrained) {
6833     if (outNumPoints)  *outNumPoints  = 0;
6834     if (outNumIndices) *outNumIndices = 0;
6835     if (outPoints)     *outPoints     = NULL;
6836     if (outValues)     *outValues     = NULL;
6837     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6838     PetscFunctionReturn(0);
6839   }
6840 
6841   if (outNumPoints)  *outNumPoints  = newNumPoints;
6842   if (outNumIndices) *outNumIndices = newNumIndices;
6843 
6844   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6845 
6846   if (!outPoints && !outValues) {
6847     if (offsets) {
6848       for (f = 0; f <= numFields; f++) {
6849         offsets[f] = newOffsets[f];
6850       }
6851     }
6852     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6853     PetscFunctionReturn(0);
6854   }
6855 
6856   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6857 
6858   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6859 
6860   /* workspaces */
6861   if (numFields) {
6862     for (f = 0; f < numFields; f++) {
6863       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
6864       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
6865     }
6866   }
6867   else {
6868     PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
6869     PetscCall(DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]));
6870   }
6871 
6872   /* get workspaces for the point-to-point matrices */
6873   if (numFields) {
6874     PetscInt totalOffset, totalMatOffset;
6875 
6876     for (p = 0; p < numPoints; p++) {
6877       PetscInt b    = points[2*p];
6878       PetscInt bDof = 0, bSecDof;
6879 
6880       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6881       if (!bSecDof) {
6882         for (f = 0; f < numFields; f++) {
6883           newPointOffsets[f][p + 1] = 0;
6884           pointMatOffsets[f][p + 1] = 0;
6885         }
6886         continue;
6887       }
6888       if (b >= aStart && b < aEnd) {
6889         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6890       }
6891       if (bDof) {
6892         for (f = 0; f < numFields; f++) {
6893           PetscInt fDof, q, bOff, allFDof = 0;
6894 
6895           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6896           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6897           for (q = 0; q < bDof; q++) {
6898             PetscInt a = anchors[bOff + q];
6899             PetscInt aFDof;
6900 
6901             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6902             allFDof += aFDof;
6903           }
6904           newPointOffsets[f][p+1] = allFDof;
6905           pointMatOffsets[f][p+1] = fDof * allFDof;
6906         }
6907       }
6908       else {
6909         for (f = 0; f < numFields; f++) {
6910           PetscInt fDof;
6911 
6912           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6913           newPointOffsets[f][p+1] = fDof;
6914           pointMatOffsets[f][p+1] = 0;
6915         }
6916       }
6917     }
6918     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6919       newPointOffsets[f][0] = totalOffset;
6920       pointMatOffsets[f][0] = totalMatOffset;
6921       for (p = 0; p < numPoints; p++) {
6922         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6923         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6924       }
6925       totalOffset    = newPointOffsets[f][numPoints];
6926       totalMatOffset = pointMatOffsets[f][numPoints];
6927       PetscCall(DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
6928     }
6929   }
6930   else {
6931     for (p = 0; p < numPoints; p++) {
6932       PetscInt b    = points[2*p];
6933       PetscInt bDof = 0, bSecDof;
6934 
6935       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6936       if (!bSecDof) {
6937         newPointOffsets[0][p + 1] = 0;
6938         pointMatOffsets[0][p + 1] = 0;
6939         continue;
6940       }
6941       if (b >= aStart && b < aEnd) {
6942         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6943       }
6944       if (bDof) {
6945         PetscInt bOff, q, allDof = 0;
6946 
6947         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6948         for (q = 0; q < bDof; q++) {
6949           PetscInt a = anchors[bOff + q], aDof;
6950 
6951           PetscCall(PetscSectionGetDof(section, a, &aDof));
6952           allDof += aDof;
6953         }
6954         newPointOffsets[0][p+1] = allDof;
6955         pointMatOffsets[0][p+1] = bSecDof * allDof;
6956       }
6957       else {
6958         newPointOffsets[0][p+1] = bSecDof;
6959         pointMatOffsets[0][p+1] = 0;
6960       }
6961     }
6962     newPointOffsets[0][0] = 0;
6963     pointMatOffsets[0][0] = 0;
6964     for (p = 0; p < numPoints; p++) {
6965       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6966       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6967     }
6968     PetscCall(DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
6969   }
6970 
6971   /* output arrays */
6972   PetscCall(DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
6973 
6974   /* get the point-to-point matrices; construct newPoints */
6975   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6976   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6977   PetscCall(DMGetWorkArray(dm,maxDof,MPIU_INT,&indices));
6978   PetscCall(DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
6979   if (numFields) {
6980     for (p = 0, newP = 0; p < numPoints; p++) {
6981       PetscInt b    = points[2*p];
6982       PetscInt o    = points[2*p+1];
6983       PetscInt bDof = 0, bSecDof;
6984 
6985       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6986       if (!bSecDof) {
6987         continue;
6988       }
6989       if (b >= aStart && b < aEnd) {
6990         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6991       }
6992       if (bDof) {
6993         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6994 
6995         fStart[0] = 0;
6996         fEnd[0]   = 0;
6997         for (f = 0; f < numFields; f++) {
6998           PetscInt fDof;
6999 
7000           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7001           fStart[f+1] = fStart[f] + fDof;
7002           fEnd[f+1]   = fStart[f+1];
7003         }
7004         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7005         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7006 
7007         fAnchorStart[0] = 0;
7008         fAnchorEnd[0]   = 0;
7009         for (f = 0; f < numFields; f++) {
7010           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7011 
7012           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
7013           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
7014         }
7015         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7016         for (q = 0; q < bDof; q++) {
7017           PetscInt a = anchors[bOff + q], aOff;
7018 
7019           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7020           newPoints[2*(newP + q)]     = a;
7021           newPoints[2*(newP + q) + 1] = 0;
7022           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7023           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7024         }
7025         newP += bDof;
7026 
7027         if (outValues) {
7028           /* get the point-to-point submatrix */
7029           for (f = 0; f < numFields; f++) {
7030             PetscCall(MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]));
7031           }
7032         }
7033       }
7034       else {
7035         newPoints[2 * newP]     = b;
7036         newPoints[2 * newP + 1] = o;
7037         newP++;
7038       }
7039     }
7040   } else {
7041     for (p = 0; p < numPoints; p++) {
7042       PetscInt b    = points[2*p];
7043       PetscInt o    = points[2*p+1];
7044       PetscInt bDof = 0, bSecDof;
7045 
7046       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7047       if (!bSecDof) {
7048         continue;
7049       }
7050       if (b >= aStart && b < aEnd) {
7051         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7052       }
7053       if (bDof) {
7054         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7055 
7056         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7057         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7058 
7059         PetscCall(PetscSectionGetOffset (aSec, b, &bOff));
7060         for (q = 0; q < bDof; q++) {
7061           PetscInt a = anchors[bOff + q], aOff;
7062 
7063           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7064 
7065           newPoints[2*(newP + q)]     = a;
7066           newPoints[2*(newP + q) + 1] = 0;
7067           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7068           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7069         }
7070         newP += bDof;
7071 
7072         /* get the point-to-point submatrix */
7073         if (outValues) {
7074           PetscCall(MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]));
7075         }
7076       }
7077       else {
7078         newPoints[2 * newP]     = b;
7079         newPoints[2 * newP + 1] = o;
7080         newP++;
7081       }
7082     }
7083   }
7084 
7085   if (outValues) {
7086     PetscCall(DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7087     PetscCall(PetscArrayzero(tmpValues,newNumIndices*numIndices));
7088     /* multiply constraints on the right */
7089     if (numFields) {
7090       for (f = 0; f < numFields; f++) {
7091         PetscInt oldOff = offsets[f];
7092 
7093         for (p = 0; p < numPoints; p++) {
7094           PetscInt cStart = newPointOffsets[f][p];
7095           PetscInt b      = points[2 * p];
7096           PetscInt c, r, k;
7097           PetscInt dof;
7098 
7099           PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7100           if (!dof) {
7101             continue;
7102           }
7103           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7104             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
7105             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
7106 
7107             for (r = 0; r < numIndices; r++) {
7108               for (c = 0; c < nCols; c++) {
7109                 for (k = 0; k < dof; k++) {
7110                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7111                 }
7112               }
7113             }
7114           }
7115           else {
7116             /* copy this column as is */
7117             for (r = 0; r < numIndices; r++) {
7118               for (c = 0; c < dof; c++) {
7119                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7120               }
7121             }
7122           }
7123           oldOff += dof;
7124         }
7125       }
7126     }
7127     else {
7128       PetscInt oldOff = 0;
7129       for (p = 0; p < numPoints; p++) {
7130         PetscInt cStart = newPointOffsets[0][p];
7131         PetscInt b      = points[2 * p];
7132         PetscInt c, r, k;
7133         PetscInt dof;
7134 
7135         PetscCall(PetscSectionGetDof(section,b,&dof));
7136         if (!dof) {
7137           continue;
7138         }
7139         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7140           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
7141           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
7142 
7143           for (r = 0; r < numIndices; r++) {
7144             for (c = 0; c < nCols; c++) {
7145               for (k = 0; k < dof; k++) {
7146                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7147               }
7148             }
7149           }
7150         }
7151         else {
7152           /* copy this column as is */
7153           for (r = 0; r < numIndices; r++) {
7154             for (c = 0; c < dof; c++) {
7155               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7156             }
7157           }
7158         }
7159         oldOff += dof;
7160       }
7161     }
7162 
7163     if (multiplyLeft) {
7164       PetscCall(DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues));
7165       PetscCall(PetscArrayzero(newValues,newNumIndices*newNumIndices));
7166       /* multiply constraints transpose on the left */
7167       if (numFields) {
7168         for (f = 0; f < numFields; f++) {
7169           PetscInt oldOff = offsets[f];
7170 
7171           for (p = 0; p < numPoints; p++) {
7172             PetscInt rStart = newPointOffsets[f][p];
7173             PetscInt b      = points[2 * p];
7174             PetscInt c, r, k;
7175             PetscInt dof;
7176 
7177             PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7178             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7179               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
7180               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
7181 
7182               for (r = 0; r < nRows; r++) {
7183                 for (c = 0; c < newNumIndices; c++) {
7184                   for (k = 0; k < dof; k++) {
7185                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7186                   }
7187                 }
7188               }
7189             }
7190             else {
7191               /* copy this row as is */
7192               for (r = 0; r < dof; r++) {
7193                 for (c = 0; c < newNumIndices; c++) {
7194                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7195                 }
7196               }
7197             }
7198             oldOff += dof;
7199           }
7200         }
7201       }
7202       else {
7203         PetscInt oldOff = 0;
7204 
7205         for (p = 0; p < numPoints; p++) {
7206           PetscInt rStart = newPointOffsets[0][p];
7207           PetscInt b      = points[2 * p];
7208           PetscInt c, r, k;
7209           PetscInt dof;
7210 
7211           PetscCall(PetscSectionGetDof(section,b,&dof));
7212           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7213             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
7214             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
7215 
7216             for (r = 0; r < nRows; r++) {
7217               for (c = 0; c < newNumIndices; c++) {
7218                 for (k = 0; k < dof; k++) {
7219                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7220                 }
7221               }
7222             }
7223           }
7224           else {
7225             /* copy this row as is */
7226             for (r = 0; r < dof; r++) {
7227               for (c = 0; c < newNumIndices; c++) {
7228                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7229               }
7230             }
7231           }
7232           oldOff += dof;
7233         }
7234       }
7235 
7236       PetscCall(DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7237     }
7238     else {
7239       newValues = tmpValues;
7240     }
7241   }
7242 
7243   /* clean up */
7244   PetscCall(DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices));
7245   PetscCall(DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
7246 
7247   if (numFields) {
7248     for (f = 0; f < numFields; f++) {
7249       PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
7250       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
7251       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
7252     }
7253   }
7254   else {
7255     PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
7256     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
7257     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]));
7258   }
7259   PetscCall(ISRestoreIndices(aIS,&anchors));
7260 
7261   /* output */
7262   if (outPoints) {
7263     *outPoints = newPoints;
7264   }
7265   else {
7266     PetscCall(DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
7267   }
7268   if (outValues) {
7269     *outValues = newValues;
7270   }
7271   for (f = 0; f <= numFields; f++) {
7272     offsets[f] = newOffsets[f];
7273   }
7274   PetscFunctionReturn(0);
7275 }
7276 
7277 /*@C
7278   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7279 
7280   Not collective
7281 
7282   Input Parameters:
7283 + dm         - The DM
7284 . section    - The PetscSection describing the points (a local section)
7285 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7286 . point      - The point defining the closure
7287 - useClPerm  - Use the closure point permutation if available
7288 
7289   Output Parameters:
7290 + numIndices - The number of dof indices in the closure of point with the input sections
7291 . indices    - The dof indices
7292 . outOffsets - Array to write the field offsets into, or NULL
7293 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7294 
7295   Notes:
7296   Must call DMPlexRestoreClosureIndices() to free allocated memory
7297 
7298   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7299   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7300   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7301   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7302   indices (with the above semantics) are implied.
7303 
7304   Level: advanced
7305 
7306 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7307 @*/
7308 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7309                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7310 {
7311   /* Closure ordering */
7312   PetscSection        clSection;
7313   IS                  clPoints;
7314   const PetscInt     *clp;
7315   PetscInt           *points;
7316   const PetscInt     *clperm = NULL;
7317   /* Dof permutation and sign flips */
7318   const PetscInt    **perms[32] = {NULL};
7319   const PetscScalar **flips[32] = {NULL};
7320   PetscScalar        *valCopy   = NULL;
7321   /* Hanging node constraints */
7322   PetscInt           *pointsC = NULL;
7323   PetscScalar        *valuesC = NULL;
7324   PetscInt            NclC, NiC;
7325 
7326   PetscInt           *idx;
7327   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
7328   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7329 
7330   PetscFunctionBeginHot;
7331   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7332   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7333   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7334   if (numIndices) PetscValidIntPointer(numIndices, 6);
7335   if (indices)    PetscValidPointer(indices, 7);
7336   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7337   if (values)     PetscValidPointer(values, 9);
7338   PetscCall(PetscSectionGetNumFields(section, &Nf));
7339   PetscCheck(Nf <= 31,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7340   PetscCall(PetscArrayzero(offsets, 32));
7341   /* 1) Get points in closure */
7342   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7343   if (useClPerm) {
7344     PetscInt depth, clsize;
7345     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7346     for (clsize=0,p=0; p<Ncl; p++) {
7347       PetscInt dof;
7348       PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
7349       clsize += dof;
7350     }
7351     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
7352   }
7353   /* 2) Get number of indices on these points and field offsets from section */
7354   for (p = 0; p < Ncl*2; p += 2) {
7355     PetscInt dof, fdof;
7356 
7357     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7358     for (f = 0; f < Nf; ++f) {
7359       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7360       offsets[f+1] += fdof;
7361     }
7362     Ni += dof;
7363   }
7364   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
7365   PetscCheck(!Nf || offsets[Nf] == Ni,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7366   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7367   for (f = 0; f < PetscMax(1, Nf); ++f) {
7368     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7369     else    PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7370     /* may need to apply sign changes to the element matrix */
7371     if (values && flips[f]) {
7372       PetscInt foffset = offsets[f];
7373 
7374       for (p = 0; p < Ncl; ++p) {
7375         PetscInt           pnt  = points[2*p], fdof;
7376         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7377 
7378         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7379         else     PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7380         if (flip) {
7381           PetscInt i, j, k;
7382 
7383           if (!valCopy) {
7384             PetscCall(DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7385             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7386             *values = valCopy;
7387           }
7388           for (i = 0; i < fdof; ++i) {
7389             PetscScalar fval = flip[i];
7390 
7391             for (k = 0; k < Ni; ++k) {
7392               valCopy[Ni * (foffset + i) + k] *= fval;
7393               valCopy[Ni * k + (foffset + i)] *= fval;
7394             }
7395           }
7396         }
7397         foffset += fdof;
7398       }
7399     }
7400   }
7401   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7402   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7403   if (NclC) {
7404     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7405     for (f = 0; f < PetscMax(1, Nf); ++f) {
7406       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7407       else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7408     }
7409     for (f = 0; f < PetscMax(1, Nf); ++f) {
7410       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7411       else    PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7412     }
7413     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7414     Ncl     = NclC;
7415     Ni      = NiC;
7416     points  = pointsC;
7417     if (values) *values = valuesC;
7418   }
7419   /* 5) Calculate indices */
7420   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7421   if (Nf) {
7422     PetscInt  idxOff;
7423     PetscBool useFieldOffsets;
7424 
7425     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
7426     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7427     if (useFieldOffsets) {
7428       for (p = 0; p < Ncl; ++p) {
7429         const PetscInt pnt = points[p*2];
7430 
7431         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7432       }
7433     } else {
7434       for (p = 0; p < Ncl; ++p) {
7435         const PetscInt pnt = points[p*2];
7436 
7437         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7438         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7439          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7440          * global section. */
7441         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7442       }
7443     }
7444   } else {
7445     PetscInt off = 0, idxOff;
7446 
7447     for (p = 0; p < Ncl; ++p) {
7448       const PetscInt  pnt  = points[p*2];
7449       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7450 
7451       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7452       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7453        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7454       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7455     }
7456   }
7457   /* 6) Cleanup */
7458   for (f = 0; f < PetscMax(1, Nf); ++f) {
7459     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7460     else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7461   }
7462   if (NclC) {
7463     PetscCall(DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC));
7464   } else {
7465     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7466   }
7467 
7468   if (numIndices) *numIndices = Ni;
7469   if (indices)    *indices    = idx;
7470   PetscFunctionReturn(0);
7471 }
7472 
7473 /*@C
7474   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7475 
7476   Not collective
7477 
7478   Input Parameters:
7479 + dm         - The DM
7480 . section    - The PetscSection describing the points (a local section)
7481 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7482 . point      - The point defining the closure
7483 - useClPerm  - Use the closure point permutation if available
7484 
7485   Output Parameters:
7486 + numIndices - The number of dof indices in the closure of point with the input sections
7487 . indices    - The dof indices
7488 . outOffsets - Array to write the field offsets into, or NULL
7489 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7490 
7491   Notes:
7492   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7493 
7494   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7495   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7496   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7497   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7498   indices (with the above semantics) are implied.
7499 
7500   Level: advanced
7501 
7502 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7503 @*/
7504 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7505                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7506 {
7507   PetscFunctionBegin;
7508   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7509   PetscValidPointer(indices, 7);
7510   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7511   PetscFunctionReturn(0);
7512 }
7513 
7514 /*@C
7515   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7516 
7517   Not collective
7518 
7519   Input Parameters:
7520 + dm - The DM
7521 . section - The section describing the layout in v, or NULL to use the default section
7522 . globalSection - The section describing the layout in v, or NULL to use the default global section
7523 . A - The matrix
7524 . point - The point in the DM
7525 . values - The array of values
7526 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7527 
7528   Fortran Notes:
7529   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7530 
7531   Level: intermediate
7532 
7533 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7534 @*/
7535 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7536 {
7537   DM_Plex           *mesh = (DM_Plex*) dm->data;
7538   PetscInt          *indices;
7539   PetscInt           numIndices;
7540   const PetscScalar *valuesOrig = values;
7541   PetscErrorCode     ierr;
7542 
7543   PetscFunctionBegin;
7544   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7545   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7546   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7547   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7548   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7549   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7550 
7551   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7552 
7553   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7554   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7555   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7556   if (ierr) {
7557     PetscMPIInt    rank;
7558 
7559     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7560     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7561     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7562     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7563     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7564     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7565   }
7566   if (mesh->printFEM > 1) {
7567     PetscInt i;
7568     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7569     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7570     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7571   }
7572 
7573   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7574   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7575   PetscFunctionReturn(0);
7576 }
7577 
7578 /*@C
7579   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7580 
7581   Not collective
7582 
7583   Input Parameters:
7584 + dmRow - The DM for the row fields
7585 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7586 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7587 . dmCol - The DM for the column fields
7588 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7589 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7590 . A - The matrix
7591 . point - The point in the DMs
7592 . values - The array of values
7593 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7594 
7595   Level: intermediate
7596 
7597 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7598 @*/
7599 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7600 {
7601   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7602   PetscInt          *indicesRow, *indicesCol;
7603   PetscInt           numIndicesRow, numIndicesCol;
7604   const PetscScalar *valuesOrig = values;
7605   PetscErrorCode     ierr;
7606 
7607   PetscFunctionBegin;
7608   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7609   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7610   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7611   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7612   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7613   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7614   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7615   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7616   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7617   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7618   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7619 
7620   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7621   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7622 
7623   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7624   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7625   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7626   if (ierr) {
7627     PetscMPIInt    rank;
7628 
7629     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7630     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7631     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7632     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7633     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7634     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7635   }
7636 
7637   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7638   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7639   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7640   PetscFunctionReturn(0);
7641 }
7642 
7643 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7644 {
7645   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7646   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7647   PetscInt       *cpoints = NULL;
7648   PetscInt       *findices, *cindices;
7649   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7650   PetscInt        foffsets[32], coffsets[32];
7651   DMPolytopeType  ct;
7652   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7653   PetscErrorCode  ierr;
7654 
7655   PetscFunctionBegin;
7656   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7657   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7658   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7659   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7660   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7661   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7662   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7663   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7664   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7665   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7666   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7667   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7668   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7669   PetscCall(PetscArrayzero(foffsets, 32));
7670   PetscCall(PetscArrayzero(coffsets, 32));
7671   /* Column indices */
7672   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7673   maxFPoints = numCPoints;
7674   /* Compress out points not in the section */
7675   /*   TODO: Squeeze out points with 0 dof as well */
7676   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7677   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7678     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7679       cpoints[q*2]   = cpoints[p];
7680       cpoints[q*2+1] = cpoints[p+1];
7681       ++q;
7682     }
7683   }
7684   numCPoints = q;
7685   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7686     PetscInt fdof;
7687 
7688     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7689     if (!dof) continue;
7690     for (f = 0; f < numFields; ++f) {
7691       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7692       coffsets[f+1] += fdof;
7693     }
7694     numCIndices += dof;
7695   }
7696   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7697   /* Row indices */
7698   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7699   {
7700     DMPlexTransform tr;
7701     DMPolytopeType *rct;
7702     PetscInt       *rsize, *rcone, *rornt, Nt;
7703 
7704     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7705     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7706     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7707     numSubcells = rsize[Nt-1];
7708     PetscCall(DMPlexTransformDestroy(&tr));
7709   }
7710   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7711   for (r = 0, q = 0; r < numSubcells; ++r) {
7712     /* TODO Map from coarse to fine cells */
7713     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7714     /* Compress out points not in the section */
7715     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7716     for (p = 0; p < numFPoints*2; p += 2) {
7717       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7718         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7719         if (!dof) continue;
7720         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7721         if (s < q) continue;
7722         ftotpoints[q*2]   = fpoints[p];
7723         ftotpoints[q*2+1] = fpoints[p+1];
7724         ++q;
7725       }
7726     }
7727     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7728   }
7729   numFPoints = q;
7730   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7731     PetscInt fdof;
7732 
7733     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7734     if (!dof) continue;
7735     for (f = 0; f < numFields; ++f) {
7736       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7737       foffsets[f+1] += fdof;
7738     }
7739     numFIndices += dof;
7740   }
7741   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7742 
7743   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7744   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7745   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7746   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7747   if (numFields) {
7748     const PetscInt **permsF[32] = {NULL};
7749     const PetscInt **permsC[32] = {NULL};
7750 
7751     for (f = 0; f < numFields; f++) {
7752       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7753       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7754     }
7755     for (p = 0; p < numFPoints; p++) {
7756       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7757       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7758     }
7759     for (p = 0; p < numCPoints; p++) {
7760       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7761       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7762     }
7763     for (f = 0; f < numFields; f++) {
7764       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7765       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7766     }
7767   } else {
7768     const PetscInt **permsF = NULL;
7769     const PetscInt **permsC = NULL;
7770 
7771     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7772     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7773     for (p = 0, off = 0; p < numFPoints; p++) {
7774       const PetscInt *perm = permsF ? permsF[p] : NULL;
7775 
7776       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7777       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7778     }
7779     for (p = 0, off = 0; p < numCPoints; p++) {
7780       const PetscInt *perm = permsC ? permsC[p] : NULL;
7781 
7782       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7783       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7784     }
7785     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7786     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7787   }
7788   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7789   /* TODO: flips */
7790   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7791   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7792   if (ierr) {
7793     PetscMPIInt    rank;
7794 
7795     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7796     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7797     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7798     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7799     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7800   }
7801   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7802   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7803   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7804   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7805   PetscFunctionReturn(0);
7806 }
7807 
7808 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7809 {
7810   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7811   PetscInt      *cpoints = NULL;
7812   PetscInt       foffsets[32], coffsets[32];
7813   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7814   DMPolytopeType ct;
7815   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7816 
7817   PetscFunctionBegin;
7818   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7819   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7820   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7821   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7822   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7823   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7824   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7825   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7826   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7827   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7828   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7829   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7830   PetscCall(PetscArrayzero(foffsets, 32));
7831   PetscCall(PetscArrayzero(coffsets, 32));
7832   /* Column indices */
7833   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7834   maxFPoints = numCPoints;
7835   /* Compress out points not in the section */
7836   /*   TODO: Squeeze out points with 0 dof as well */
7837   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7838   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7839     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7840       cpoints[q*2]   = cpoints[p];
7841       cpoints[q*2+1] = cpoints[p+1];
7842       ++q;
7843     }
7844   }
7845   numCPoints = q;
7846   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7847     PetscInt fdof;
7848 
7849     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7850     if (!dof) continue;
7851     for (f = 0; f < numFields; ++f) {
7852       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7853       coffsets[f+1] += fdof;
7854     }
7855     numCIndices += dof;
7856   }
7857   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7858   /* Row indices */
7859   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7860   {
7861     DMPlexTransform tr;
7862     DMPolytopeType *rct;
7863     PetscInt       *rsize, *rcone, *rornt, Nt;
7864 
7865     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7866     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7867     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7868     numSubcells = rsize[Nt-1];
7869     PetscCall(DMPlexTransformDestroy(&tr));
7870   }
7871   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7872   for (r = 0, q = 0; r < numSubcells; ++r) {
7873     /* TODO Map from coarse to fine cells */
7874     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7875     /* Compress out points not in the section */
7876     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7877     for (p = 0; p < numFPoints*2; p += 2) {
7878       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7879         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7880         if (!dof) continue;
7881         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7882         if (s < q) continue;
7883         ftotpoints[q*2]   = fpoints[p];
7884         ftotpoints[q*2+1] = fpoints[p+1];
7885         ++q;
7886       }
7887     }
7888     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7889   }
7890   numFPoints = q;
7891   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7892     PetscInt fdof;
7893 
7894     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7895     if (!dof) continue;
7896     for (f = 0; f < numFields; ++f) {
7897       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7898       foffsets[f+1] += fdof;
7899     }
7900     numFIndices += dof;
7901   }
7902   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7903 
7904   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7905   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7906   if (numFields) {
7907     const PetscInt **permsF[32] = {NULL};
7908     const PetscInt **permsC[32] = {NULL};
7909 
7910     for (f = 0; f < numFields; f++) {
7911       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7912       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7913     }
7914     for (p = 0; p < numFPoints; p++) {
7915       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7916       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7917     }
7918     for (p = 0; p < numCPoints; p++) {
7919       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7920       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7921     }
7922     for (f = 0; f < numFields; f++) {
7923       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7924       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7925     }
7926   } else {
7927     const PetscInt **permsF = NULL;
7928     const PetscInt **permsC = NULL;
7929 
7930     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7931     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7932     for (p = 0, off = 0; p < numFPoints; p++) {
7933       const PetscInt *perm = permsF ? permsF[p] : NULL;
7934 
7935       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7936       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7937     }
7938     for (p = 0, off = 0; p < numCPoints; p++) {
7939       const PetscInt *perm = permsC ? permsC[p] : NULL;
7940 
7941       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7942       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7943     }
7944     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7945     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7946   }
7947   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7948   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7949   PetscFunctionReturn(0);
7950 }
7951 
7952 /*@C
7953   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7954 
7955   Input Parameter:
7956 . dm   - The DMPlex object
7957 
7958   Output Parameter:
7959 . cellHeight - The height of a cell
7960 
7961   Level: developer
7962 
7963 .seealso `DMPlexSetVTKCellHeight()`
7964 @*/
7965 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7966 {
7967   DM_Plex *mesh = (DM_Plex*) dm->data;
7968 
7969   PetscFunctionBegin;
7970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7971   PetscValidIntPointer(cellHeight, 2);
7972   *cellHeight = mesh->vtkCellHeight;
7973   PetscFunctionReturn(0);
7974 }
7975 
7976 /*@C
7977   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7978 
7979   Input Parameters:
7980 + dm   - The DMPlex object
7981 - cellHeight - The height of a cell
7982 
7983   Level: developer
7984 
7985 .seealso `DMPlexGetVTKCellHeight()`
7986 @*/
7987 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7988 {
7989   DM_Plex *mesh = (DM_Plex*) dm->data;
7990 
7991   PetscFunctionBegin;
7992   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7993   mesh->vtkCellHeight = cellHeight;
7994   PetscFunctionReturn(0);
7995 }
7996 
7997 /*@
7998   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7999 
8000   Input Parameter:
8001 . dm - The DMPlex object
8002 
8003   Output Parameters:
8004 + gcStart - The first ghost cell, or NULL
8005 - gcEnd   - The upper bound on ghost cells, or NULL
8006 
8007   Level: advanced
8008 
8009 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8010 @*/
8011 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8012 {
8013   DMLabel        ctLabel;
8014 
8015   PetscFunctionBegin;
8016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8017   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8018   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8019   PetscFunctionReturn(0);
8020 }
8021 
8022 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8023 {
8024   PetscSection   section, globalSection;
8025   PetscInt      *numbers, p;
8026 
8027   PetscFunctionBegin;
8028   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
8029   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8030   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8031   for (p = pStart; p < pEnd; ++p) {
8032     PetscCall(PetscSectionSetDof(section, p, 1));
8033   }
8034   PetscCall(PetscSectionSetUp(section));
8035   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8036   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8037   for (p = pStart; p < pEnd; ++p) {
8038     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8039     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8040     else                       numbers[p-pStart] += shift;
8041   }
8042   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8043   if (globalSize) {
8044     PetscLayout layout;
8045     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8046     PetscCall(PetscLayoutGetSize(layout, globalSize));
8047     PetscCall(PetscLayoutDestroy(&layout));
8048   }
8049   PetscCall(PetscSectionDestroy(&section));
8050   PetscCall(PetscSectionDestroy(&globalSection));
8051   PetscFunctionReturn(0);
8052 }
8053 
8054 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8055 {
8056   PetscInt       cellHeight, cStart, cEnd;
8057 
8058   PetscFunctionBegin;
8059   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8060   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8061   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8062   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8063   PetscFunctionReturn(0);
8064 }
8065 
8066 /*@
8067   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8068 
8069   Input Parameter:
8070 . dm   - The DMPlex object
8071 
8072   Output Parameter:
8073 . globalCellNumbers - Global cell numbers for all cells on this process
8074 
8075   Level: developer
8076 
8077 .seealso `DMPlexGetVertexNumbering()`
8078 @*/
8079 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8080 {
8081   DM_Plex       *mesh = (DM_Plex*) dm->data;
8082 
8083   PetscFunctionBegin;
8084   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8085   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8086   *globalCellNumbers = mesh->globalCellNumbers;
8087   PetscFunctionReturn(0);
8088 }
8089 
8090 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8091 {
8092   PetscInt       vStart, vEnd;
8093 
8094   PetscFunctionBegin;
8095   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8096   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8097   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8098   PetscFunctionReturn(0);
8099 }
8100 
8101 /*@
8102   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8103 
8104   Input Parameter:
8105 . dm   - The DMPlex object
8106 
8107   Output Parameter:
8108 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8109 
8110   Level: developer
8111 
8112 .seealso `DMPlexGetCellNumbering()`
8113 @*/
8114 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8115 {
8116   DM_Plex       *mesh = (DM_Plex*) dm->data;
8117 
8118   PetscFunctionBegin;
8119   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8120   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8121   *globalVertexNumbers = mesh->globalVertexNumbers;
8122   PetscFunctionReturn(0);
8123 }
8124 
8125 /*@
8126   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
8127 
8128   Input Parameter:
8129 . dm   - The DMPlex object
8130 
8131   Output Parameter:
8132 . globalPointNumbers - Global numbers for all points on this process
8133 
8134   Level: developer
8135 
8136 .seealso `DMPlexGetCellNumbering()`
8137 @*/
8138 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8139 {
8140   IS             nums[4];
8141   PetscInt       depths[4], gdepths[4], starts[4];
8142   PetscInt       depth, d, shift = 0;
8143 
8144   PetscFunctionBegin;
8145   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8146   PetscCall(DMPlexGetDepth(dm, &depth));
8147   /* For unstratified meshes use dim instead of depth */
8148   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8149   for (d = 0; d <= depth; ++d) {
8150     PetscInt end;
8151 
8152     depths[d] = depth-d;
8153     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8154     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8155   }
8156   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8157   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8158   for (d = 0; d <= depth; ++d) {
8159     PetscCheck(starts[d] < 0 || depths[d] == gdepths[d],PETSC_COMM_SELF,PETSC_ERR_PLIB,"Expected depth %" PetscInt_FMT ", found %" PetscInt_FMT,depths[d],gdepths[d]);
8160   }
8161   for (d = 0; d <= depth; ++d) {
8162     PetscInt pStart, pEnd, gsize;
8163 
8164     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8165     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8166     shift += gsize;
8167   }
8168   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8169   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8170   PetscFunctionReturn(0);
8171 }
8172 
8173 /*@
8174   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8175 
8176   Input Parameter:
8177 . dm - The DMPlex object
8178 
8179   Output Parameter:
8180 . ranks - The rank field
8181 
8182   Options Database Keys:
8183 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8184 
8185   Level: intermediate
8186 
8187 .seealso: `DMView()`
8188 @*/
8189 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8190 {
8191   DM             rdm;
8192   PetscFE        fe;
8193   PetscScalar   *r;
8194   PetscMPIInt    rank;
8195   DMPolytopeType ct;
8196   PetscInt       dim, cStart, cEnd, c;
8197   PetscBool      simplex;
8198 
8199   PetscFunctionBeginUser;
8200   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8201   PetscValidPointer(ranks, 2);
8202   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8203   PetscCall(DMClone(dm, &rdm));
8204   PetscCall(DMGetDimension(rdm, &dim));
8205   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8206   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8207   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8208   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8209   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8210   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8211   PetscCall(PetscFEDestroy(&fe));
8212   PetscCall(DMCreateDS(rdm));
8213   PetscCall(DMCreateGlobalVector(rdm, ranks));
8214   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8215   PetscCall(VecGetArray(*ranks, &r));
8216   for (c = cStart; c < cEnd; ++c) {
8217     PetscScalar *lr;
8218 
8219     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8220     if (lr) *lr = rank;
8221   }
8222   PetscCall(VecRestoreArray(*ranks, &r));
8223   PetscCall(DMDestroy(&rdm));
8224   PetscFunctionReturn(0);
8225 }
8226 
8227 /*@
8228   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8229 
8230   Input Parameters:
8231 + dm    - The DMPlex
8232 - label - The DMLabel
8233 
8234   Output Parameter:
8235 . val - The label value field
8236 
8237   Options Database Keys:
8238 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8239 
8240   Level: intermediate
8241 
8242 .seealso: `DMView()`
8243 @*/
8244 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8245 {
8246   DM             rdm;
8247   PetscFE        fe;
8248   PetscScalar   *v;
8249   PetscInt       dim, cStart, cEnd, c;
8250 
8251   PetscFunctionBeginUser;
8252   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8253   PetscValidPointer(label, 2);
8254   PetscValidPointer(val, 3);
8255   PetscCall(DMClone(dm, &rdm));
8256   PetscCall(DMGetDimension(rdm, &dim));
8257   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8258   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8259   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8260   PetscCall(PetscFEDestroy(&fe));
8261   PetscCall(DMCreateDS(rdm));
8262   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8263   PetscCall(DMCreateGlobalVector(rdm, val));
8264   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8265   PetscCall(VecGetArray(*val, &v));
8266   for (c = cStart; c < cEnd; ++c) {
8267     PetscScalar *lv;
8268     PetscInt     cval;
8269 
8270     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8271     PetscCall(DMLabelGetValue(label, c, &cval));
8272     *lv = cval;
8273   }
8274   PetscCall(VecRestoreArray(*val, &v));
8275   PetscCall(DMDestroy(&rdm));
8276   PetscFunctionReturn(0);
8277 }
8278 
8279 /*@
8280   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8281 
8282   Input Parameter:
8283 . dm - The DMPlex object
8284 
8285   Notes:
8286   This is a useful diagnostic when creating meshes programmatically.
8287 
8288   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8289 
8290   Level: developer
8291 
8292 .seealso: `DMCreate()`, `DMSetFromOptions()`
8293 @*/
8294 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8295 {
8296   PetscSection    coneSection, supportSection;
8297   const PetscInt *cone, *support;
8298   PetscInt        coneSize, c, supportSize, s;
8299   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8300   PetscBool       storagecheck = PETSC_TRUE;
8301 
8302   PetscFunctionBegin;
8303   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8304   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8305   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8306   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8307   /* Check that point p is found in the support of its cone points, and vice versa */
8308   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8309   for (p = pStart; p < pEnd; ++p) {
8310     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8311     PetscCall(DMPlexGetCone(dm, p, &cone));
8312     for (c = 0; c < coneSize; ++c) {
8313       PetscBool dup = PETSC_FALSE;
8314       PetscInt  d;
8315       for (d = c-1; d >= 0; --d) {
8316         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8317       }
8318       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8319       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8320       for (s = 0; s < supportSize; ++s) {
8321         if (support[s] == p) break;
8322       }
8323       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8324         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8325         for (s = 0; s < coneSize; ++s) {
8326           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8327         }
8328         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8329         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8330         for (s = 0; s < supportSize; ++s) {
8331           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8332         }
8333         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8334         PetscCheck(!dup,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not repeatedly found in support of repeated cone point %" PetscInt_FMT, p, cone[c]);
8335         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8336       }
8337     }
8338     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8339     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8340     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8341     PetscCall(DMPlexGetSupport(dm, p, &support));
8342     for (s = 0; s < supportSize; ++s) {
8343       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8344       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8345       for (c = 0; c < coneSize; ++c) {
8346         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8347         if (cone[c] != pp) { c = 0; break; }
8348         if (cone[c] == p) break;
8349       }
8350       if (c >= coneSize) {
8351         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8352         for (c = 0; c < supportSize; ++c) {
8353           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8354         }
8355         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8356         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8357         for (c = 0; c < coneSize; ++c) {
8358           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8359         }
8360         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8361         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8362       }
8363     }
8364   }
8365   if (storagecheck) {
8366     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8367     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8368     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8369   }
8370   PetscFunctionReturn(0);
8371 }
8372 
8373 /*
8374   For submeshes with cohesive cells (see DMPlexConstructCohesiveCells()), we allow a special case where some of the boundary of a face (edges and vertices) are not duplicated. We call these special boundary points "unsplit", since the same edge or vertex appears in both copies of the face. These unsplit points throw off our counting, so we have to explicitly account for them here.
8375 */
8376 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8377 {
8378   DMPolytopeType  cct;
8379   PetscInt        ptpoints[4];
8380   const PetscInt *cone, *ccone, *ptcone;
8381   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8382 
8383   PetscFunctionBegin;
8384   *unsplit = 0;
8385   switch (ct) {
8386     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8387       ptpoints[npt++] = c;
8388       break;
8389     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8390       PetscCall(DMPlexGetCone(dm, c, &cone));
8391       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8392       for (cp = 0; cp < coneSize; ++cp) {
8393         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8394         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8395       }
8396       break;
8397     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8398     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8399       PetscCall(DMPlexGetCone(dm, c, &cone));
8400       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8401       for (cp = 0; cp < coneSize; ++cp) {
8402         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8403         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8404         for (ccp = 0; ccp < cconeSize; ++ccp) {
8405           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8406           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8407             PetscInt p;
8408             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8409             if (p == npt) ptpoints[npt++] = ccone[ccp];
8410           }
8411         }
8412       }
8413       break;
8414     default: break;
8415   }
8416   for (pt = 0; pt < npt; ++pt) {
8417     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8418     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8419   }
8420   PetscFunctionReturn(0);
8421 }
8422 
8423 /*@
8424   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8425 
8426   Input Parameters:
8427 + dm - The DMPlex object
8428 - cellHeight - Normally 0
8429 
8430   Notes:
8431   This is a useful diagnostic when creating meshes programmatically.
8432   Currently applicable only to homogeneous simplex or tensor meshes.
8433 
8434   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8435 
8436   Level: developer
8437 
8438 .seealso: `DMCreate()`, `DMSetFromOptions()`
8439 @*/
8440 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8441 {
8442   DMPlexInterpolatedFlag interp;
8443   DMPolytopeType         ct;
8444   PetscInt               vStart, vEnd, cStart, cEnd, c;
8445 
8446   PetscFunctionBegin;
8447   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8448   PetscCall(DMPlexIsInterpolated(dm, &interp));
8449   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8450   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8451   for (c = cStart; c < cEnd; ++c) {
8452     PetscInt *closure = NULL;
8453     PetscInt  coneSize, closureSize, cl, Nv = 0;
8454 
8455     PetscCall(DMPlexGetCellType(dm, c, &ct));
8456     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8457     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8458     if (interp == DMPLEX_INTERPOLATED_FULL) {
8459       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8460       PetscCheck(coneSize == DMPolytopeTypeGetConeSize(ct),PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " of type %s has cone size %" PetscInt_FMT " != %" PetscInt_FMT, c, DMPolytopeTypes[ct], coneSize, DMPolytopeTypeGetConeSize(ct));
8461     }
8462     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8463     for (cl = 0; cl < closureSize*2; cl += 2) {
8464       const PetscInt p = closure[cl];
8465       if ((p >= vStart) && (p < vEnd)) ++Nv;
8466     }
8467     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8468     /* Special Case: Tensor faces with identified vertices */
8469     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8470       PetscInt unsplit;
8471 
8472       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8473       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8474     }
8475     PetscCheck(Nv == DMPolytopeTypeGetNumVertices(ct),PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " of type %s has %" PetscInt_FMT " vertices != %" PetscInt_FMT, c, DMPolytopeTypes[ct], Nv, DMPolytopeTypeGetNumVertices(ct));
8476   }
8477   PetscFunctionReturn(0);
8478 }
8479 
8480 /*@
8481   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8482 
8483   Collective
8484 
8485   Input Parameters:
8486 + dm - The DMPlex object
8487 - cellHeight - Normally 0
8488 
8489   Notes:
8490   This is a useful diagnostic when creating meshes programmatically.
8491   This routine is only relevant for meshes that are fully interpolated across all ranks.
8492   It will error out if a partially interpolated mesh is given on some rank.
8493   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8494 
8495   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8496 
8497   Level: developer
8498 
8499 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8500 @*/
8501 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8502 {
8503   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8504   DMPlexInterpolatedFlag interpEnum;
8505 
8506   PetscFunctionBegin;
8507   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8508   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8509   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8510   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8511     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8512     PetscFunctionReturn(0);
8513   }
8514 
8515   PetscCall(DMGetDimension(dm, &dim));
8516   PetscCall(DMPlexGetDepth(dm, &depth));
8517   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8518   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8519     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8520     for (c = cStart; c < cEnd; ++c) {
8521       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8522       const DMPolytopeType *faceTypes;
8523       DMPolytopeType        ct;
8524       PetscInt              numFaces, coneSize, f;
8525       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8526 
8527       PetscCall(DMPlexGetCellType(dm, c, &ct));
8528       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8529       if (unsplit) continue;
8530       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8531       PetscCall(DMPlexGetCone(dm, c, &cone));
8532       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8533       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8534       for (cl = 0; cl < closureSize*2; cl += 2) {
8535         const PetscInt p = closure[cl];
8536         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8537       }
8538       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8539       PetscCheck(coneSize == numFaces,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " of type %s has %" PetscInt_FMT " faces but should have %" PetscInt_FMT, c, DMPolytopeTypes[ct], coneSize, numFaces);
8540       for (f = 0; f < numFaces; ++f) {
8541         DMPolytopeType fct;
8542         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8543 
8544         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8545         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8546         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8547           const PetscInt p = fclosure[cl];
8548           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8549         }
8550         PetscCheck(fnumCorners == faceSizes[f],PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " of type %s (cone idx %" PetscInt_FMT ") of cell %" PetscInt_FMT " of type %s has %" PetscInt_FMT " vertices but should have %" PetscInt_FMT, cone[f], DMPolytopeTypes[fct], f, c, DMPolytopeTypes[ct], fnumCorners, faceSizes[f]);
8551         for (v = 0; v < fnumCorners; ++v) {
8552           if (fclosure[v] != faces[fOff+v]) {
8553             PetscInt v1;
8554 
8555             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8556             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8557             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8558             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff+v1]));
8559             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8560             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " of type %s (cone idx %" PetscInt_FMT ", ornt %" PetscInt_FMT ") of cell %" PetscInt_FMT " of type %s vertex %" PetscInt_FMT ", %" PetscInt_FMT " != %" PetscInt_FMT, cone[f], DMPolytopeTypes[fct], f, ornt[f], c, DMPolytopeTypes[ct], v, fclosure[v], faces[fOff+v]);
8561           }
8562         }
8563         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8564         fOff += faceSizes[f];
8565       }
8566       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8567       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8568     }
8569   }
8570   PetscFunctionReturn(0);
8571 }
8572 
8573 /*@
8574   DMPlexCheckGeometry - Check the geometry of mesh cells
8575 
8576   Input Parameter:
8577 . dm - The DMPlex object
8578 
8579   Notes:
8580   This is a useful diagnostic when creating meshes programmatically.
8581 
8582   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8583 
8584   Level: developer
8585 
8586 .seealso: `DMCreate()`, `DMSetFromOptions()`
8587 @*/
8588 PetscErrorCode DMPlexCheckGeometry(DM dm)
8589 {
8590   Vec            coordinates;
8591   PetscReal      detJ, J[9], refVol = 1.0;
8592   PetscReal      vol;
8593   PetscBool      periodic;
8594   PetscInt       dim, depth, dE, d, cStart, cEnd, c;
8595 
8596   PetscFunctionBegin;
8597   PetscCall(DMGetDimension(dm, &dim));
8598   PetscCall(DMGetCoordinateDim(dm, &dE));
8599   if (dim != dE) PetscFunctionReturn(0);
8600   PetscCall(DMPlexGetDepth(dm, &depth));
8601   PetscCall(DMGetPeriodicity(dm, &periodic, NULL, NULL, NULL));
8602   for (d = 0; d < dim; ++d) refVol *= 2.0;
8603   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8604   /* Make sure local coordinates are created, because that step is collective */
8605   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8606   for (c = cStart; c < cEnd; ++c) {
8607     DMPolytopeType ct;
8608     PetscInt       unsplit;
8609     PetscBool      ignoreZeroVol = PETSC_FALSE;
8610 
8611     PetscCall(DMPlexGetCellType(dm, c, &ct));
8612     switch (ct) {
8613       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8614       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8615       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8616         ignoreZeroVol = PETSC_TRUE; break;
8617       default: break;
8618     }
8619     switch (ct) {
8620       case DM_POLYTOPE_TRI_PRISM:
8621       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8622       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8623       case DM_POLYTOPE_PYRAMID:
8624         continue;
8625       default: break;
8626     }
8627     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8628     if (unsplit) continue;
8629     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8630     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);
8631     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ*refVol)));
8632     if (depth > 1 && !periodic) {
8633       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8634       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);
8635       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double) vol));
8636     }
8637   }
8638   PetscFunctionReturn(0);
8639 }
8640 
8641 /*@
8642   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8643 
8644   Collective
8645 
8646   Input Parameters:
8647 + dm - The DMPlex object
8648 - pointSF - The Point SF, or NULL for Point SF attached to DM
8649 
8650   Notes:
8651   This is mainly intended for debugging/testing purposes.
8652 
8653   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8654 
8655   Level: developer
8656 
8657 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8658 @*/
8659 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF)
8660 {
8661   PetscInt        l, nleaves, nroots, overlap;
8662   const PetscInt *locals;
8663   const PetscSFNode *remotes;
8664   PetscBool       distributed;
8665   MPI_Comm        comm;
8666   PetscMPIInt     rank;
8667 
8668   PetscFunctionBegin;
8669   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8670   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8671   else         pointSF = dm->sf;
8672   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8673   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8674   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8675   {
8676     PetscMPIInt    mpiFlag;
8677 
8678     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF),&mpiFlag));
8679     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)",mpiFlag);
8680   }
8681   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8682   PetscCall(DMPlexIsDistributed(dm, &distributed));
8683   if (!distributed) {
8684     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);
8685     PetscFunctionReturn(0);
8686   }
8687   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);
8688   PetscCall(DMPlexGetOverlap(dm, &overlap));
8689 
8690   /* Check SF graph is compatible with DMPlex chart */
8691   {
8692     PetscInt pStart, pEnd, maxLeaf;
8693 
8694     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8695     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8696     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd-pStart, nroots);
8697     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8698   }
8699 
8700   /* Check Point SF has no local points referenced */
8701   for (l = 0; l < nleaves; l++) {
8702     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);
8703   }
8704 
8705   /* Check there are no cells in interface */
8706   if (!overlap) {
8707     PetscInt cellHeight, cStart, cEnd;
8708 
8709     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8710     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8711     for (l = 0; l < nleaves; ++l) {
8712       const PetscInt point = locals ? locals[l] : l;
8713 
8714       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8715     }
8716   }
8717 
8718   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8719   {
8720     const PetscInt *rootdegree;
8721 
8722     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8723     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8724     for (l = 0; l < nleaves; ++l) {
8725       const PetscInt  point = locals ? locals[l] : l;
8726       const PetscInt *cone;
8727       PetscInt        coneSize, c, idx;
8728 
8729       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8730       PetscCall(DMPlexGetCone(dm, point, &cone));
8731       for (c = 0; c < coneSize; ++c) {
8732         if (!rootdegree[cone[c]]) {
8733           if (locals) {
8734             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8735           } else {
8736             idx = (cone[c] < nleaves) ? cone[c] : -1;
8737           }
8738           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8739         }
8740       }
8741     }
8742   }
8743   PetscFunctionReturn(0);
8744 }
8745 
8746 /*@
8747   DMPlexCheck - Perform various checks of Plex sanity
8748 
8749   Input Parameter:
8750 . dm - The DMPlex object
8751 
8752   Notes:
8753   This is a useful diagnostic when creating meshes programmatically.
8754 
8755   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8756 
8757   Currently does not include DMPlexCheckCellShape().
8758 
8759   Level: developer
8760 
8761 .seealso: DMCreate(), DMSetFromOptions()
8762 @*/
8763 PetscErrorCode DMPlexCheck(DM dm)
8764 {
8765   PetscInt cellHeight;
8766 
8767   PetscFunctionBegin;
8768   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8769   PetscCall(DMPlexCheckSymmetry(dm));
8770   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8771   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8772   PetscCall(DMPlexCheckGeometry(dm));
8773   PetscCall(DMPlexCheckPointSF(dm, NULL));
8774   PetscCall(DMPlexCheckInterfaceCones(dm));
8775   PetscFunctionReturn(0);
8776 }
8777 
8778 typedef struct cell_stats
8779 {
8780   PetscReal min, max, sum, squaresum;
8781   PetscInt  count;
8782 } cell_stats_t;
8783 
8784 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8785 {
8786   PetscInt i, N = *len;
8787 
8788   for (i = 0; i < N; i++) {
8789     cell_stats_t *A = (cell_stats_t *) a;
8790     cell_stats_t *B = (cell_stats_t *) b;
8791 
8792     B->min = PetscMin(A->min,B->min);
8793     B->max = PetscMax(A->max,B->max);
8794     B->sum += A->sum;
8795     B->squaresum += A->squaresum;
8796     B->count += A->count;
8797   }
8798 }
8799 
8800 /*@
8801   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8802 
8803   Collective on dm
8804 
8805   Input Parameters:
8806 + dm        - The DMPlex object
8807 . output    - If true, statistics will be displayed on stdout
8808 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8809 
8810   Notes:
8811   This is mainly intended for debugging/testing purposes.
8812 
8813   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8814 
8815   Level: developer
8816 
8817 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8818 @*/
8819 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8820 {
8821   DM             dmCoarse;
8822   cell_stats_t   stats, globalStats;
8823   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8824   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8825   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8826   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8827   PetscMPIInt    rank,size;
8828 
8829   PetscFunctionBegin;
8830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8831   stats.min   = PETSC_MAX_REAL;
8832   stats.max   = PETSC_MIN_REAL;
8833   stats.sum   = stats.squaresum = 0.;
8834   stats.count = 0;
8835 
8836   PetscCallMPI(MPI_Comm_size(comm, &size));
8837   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8838   PetscCall(DMGetCoordinateDim(dm,&cdim));
8839   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8840   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8841   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8842   for (c = cStart; c < cEnd; c++) {
8843     PetscInt  i;
8844     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8845 
8846     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8847     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8848     for (i = 0; i < PetscSqr(cdim); ++i) {
8849       frobJ    += J[i] * J[i];
8850       frobInvJ += invJ[i] * invJ[i];
8851     }
8852     cond2 = frobJ * frobInvJ;
8853     cond  = PetscSqrtReal(cond2);
8854 
8855     stats.min        = PetscMin(stats.min,cond);
8856     stats.max        = PetscMax(stats.max,cond);
8857     stats.sum       += cond;
8858     stats.squaresum += cond2;
8859     stats.count++;
8860     if (output && cond > limit) {
8861       PetscSection coordSection;
8862       Vec          coordsLocal;
8863       PetscScalar *coords = NULL;
8864       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8865 
8866       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8867       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8868       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8869       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double) cond));
8870       for (i = 0; i < Nv/cdim; ++i) {
8871         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8872         for (d = 0; d < cdim; ++d) {
8873           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8874           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8875         }
8876         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8877       }
8878       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8879       for (cl = 0; cl < clSize*2; cl += 2) {
8880         const PetscInt edge = closure[cl];
8881 
8882         if ((edge >= eStart) && (edge < eEnd)) {
8883           PetscReal len;
8884 
8885           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8886           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double) len));
8887         }
8888       }
8889       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8890       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8891     }
8892   }
8893   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8894 
8895   if (size > 1) {
8896     PetscMPIInt   blockLengths[2] = {4,1};
8897     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8898     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8899     MPI_Op        statReduce;
8900 
8901     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8902     PetscCallMPI(MPI_Type_commit(&statType));
8903     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8904     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8905     PetscCallMPI(MPI_Op_free(&statReduce));
8906     PetscCallMPI(MPI_Type_free(&statType));
8907   } else {
8908     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8909   }
8910   if (rank == 0) {
8911     count = globalStats.count;
8912     min   = globalStats.min;
8913     max   = globalStats.max;
8914     mean  = globalStats.sum / globalStats.count;
8915     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8916   }
8917 
8918   if (output) {
8919     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));
8920   }
8921   PetscCall(PetscFree2(J,invJ));
8922 
8923   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8924   if (dmCoarse) {
8925     PetscBool isplex;
8926 
8927     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8928     if (isplex) {
8929       PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8930     }
8931   }
8932   PetscFunctionReturn(0);
8933 }
8934 
8935 /*@
8936   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8937   orthogonal quality below given tolerance.
8938 
8939   Collective on dm
8940 
8941   Input Parameters:
8942 + dm   - The DMPlex object
8943 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8944 - atol - [0, 1] Absolute tolerance for tagging cells.
8945 
8946   Output Parameters:
8947 + OrthQual      - Vec containing orthogonal quality per cell
8948 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8949 
8950   Options Database Keys:
8951 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8952 supported.
8953 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8954 
8955   Notes:
8956   Orthogonal quality is given by the following formula:
8957 
8958   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8959 
8960   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
8961   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8962   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8963   calculating the cosine of the angle between these vectors.
8964 
8965   Orthogonal quality ranges from 1 (best) to 0 (worst).
8966 
8967   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8968   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8969 
8970   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8971 
8972   Level: intermediate
8973 
8974 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8975 @*/
8976 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8977 {
8978   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8979   PetscInt                *idx;
8980   PetscScalar             *oqVals;
8981   const PetscScalar       *cellGeomArr, *faceGeomArr;
8982   PetscReal               *ci, *fi, *Ai;
8983   MPI_Comm                comm;
8984   Vec                     cellgeom, facegeom;
8985   DM                      dmFace, dmCell;
8986   IS                      glob;
8987   ISLocalToGlobalMapping  ltog;
8988   PetscViewer             vwr;
8989 
8990   PetscFunctionBegin;
8991   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8992   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
8993   PetscValidPointer(OrthQual, 4);
8994   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
8995   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
8996   PetscCall(DMGetDimension(dm, &nc));
8997   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
8998   {
8999     DMPlexInterpolatedFlag interpFlag;
9000 
9001     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9002     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9003       PetscMPIInt rank;
9004 
9005       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9006       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9007     }
9008   }
9009   if (OrthQualLabel) {
9010     PetscValidPointer(OrthQualLabel, 5);
9011     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9012     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9013   } else {*OrthQualLabel = NULL;}
9014   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9015   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9016   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9017   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9018   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9019   PetscCall(VecCreate(comm, OrthQual));
9020   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9021   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
9022   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9023   PetscCall(VecSetUp(*OrthQual));
9024   PetscCall(ISDestroy(&glob));
9025   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9026   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9027   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9028   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9029   PetscCall(VecGetDM(cellgeom, &dmCell));
9030   PetscCall(VecGetDM(facegeom, &dmFace));
9031   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9032   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
9033     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9034     PetscInt           cellarr[2], *adj = NULL;
9035     PetscScalar        *cArr, *fArr;
9036     PetscReal          minvalc = 1.0, minvalf = 1.0;
9037     PetscFVCellGeom    *cg;
9038 
9039     idx[cellIter] = cell-cStart;
9040     cellarr[0] = cell;
9041     /* Make indexing into cellGeom easier */
9042     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9043     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9044     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9045     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9046     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
9047       PetscInt         i;
9048       const PetscInt   neigh = adj[cellneigh];
9049       PetscReal        normci = 0, normfi = 0, normai = 0;
9050       PetscFVCellGeom  *cgneigh;
9051       PetscFVFaceGeom  *fg;
9052 
9053       /* Don't count ourselves in the neighbor list */
9054       if (neigh == cell) continue;
9055       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9056       cellarr[1] = neigh;
9057       {
9058         PetscInt       numcovpts;
9059         const PetscInt *covpts;
9060 
9061         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9062         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9063         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9064       }
9065 
9066       /* Compute c_i, f_i and their norms */
9067       for (i = 0; i < nc; i++) {
9068         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9069         fi[i] = fg->centroid[i] - cg->centroid[i];
9070         Ai[i] = fg->normal[i];
9071         normci += PetscPowReal(ci[i], 2);
9072         normfi += PetscPowReal(fi[i], 2);
9073         normai += PetscPowReal(Ai[i], 2);
9074       }
9075       normci = PetscSqrtReal(normci);
9076       normfi = PetscSqrtReal(normfi);
9077       normai = PetscSqrtReal(normai);
9078 
9079       /* Normalize and compute for each face-cell-normal pair */
9080       for (i = 0; i < nc; i++) {
9081         ci[i] = ci[i]/normci;
9082         fi[i] = fi[i]/normfi;
9083         Ai[i] = Ai[i]/normai;
9084         /* PetscAbs because I don't know if normals are guaranteed to point out */
9085         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9086         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9087       }
9088       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9089         minvalc = PetscRealPart(cArr[cellneighiter]);
9090       }
9091       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9092         minvalf = PetscRealPart(fArr[cellneighiter]);
9093       }
9094     }
9095     PetscCall(PetscFree(adj));
9096     PetscCall(PetscFree2(cArr, fArr));
9097     /* Defer to cell if they're equal */
9098     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9099     if (OrthQualLabel) {
9100       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9101     }
9102   }
9103   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9104   PetscCall(VecAssemblyBegin(*OrthQual));
9105   PetscCall(VecAssemblyEnd(*OrthQual));
9106   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9107   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9108   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9109   if (OrthQualLabel) {
9110     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9111   }
9112   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9113   PetscCall(PetscViewerDestroy(&vwr));
9114   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9115   PetscFunctionReturn(0);
9116 }
9117 
9118 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9119  * interpolator construction */
9120 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9121 {
9122   PetscSection   section, newSection, gsection;
9123   PetscSF        sf;
9124   PetscBool      hasConstraints, ghasConstraints;
9125 
9126   PetscFunctionBegin;
9127   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9128   PetscValidPointer(odm,2);
9129   PetscCall(DMGetLocalSection(dm, &section));
9130   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9131   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9132   if (!ghasConstraints) {
9133     PetscCall(PetscObjectReference((PetscObject)dm));
9134     *odm = dm;
9135     PetscFunctionReturn(0);
9136   }
9137   PetscCall(DMClone(dm, odm));
9138   PetscCall(DMCopyFields(dm, *odm));
9139   PetscCall(DMGetLocalSection(*odm, &newSection));
9140   PetscCall(DMGetPointSF(*odm, &sf));
9141   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9142   PetscCall(DMSetGlobalSection(*odm, gsection));
9143   PetscCall(PetscSectionDestroy(&gsection));
9144   PetscFunctionReturn(0);
9145 }
9146 
9147 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9148 {
9149   DM             dmco, dmfo;
9150   Mat            interpo;
9151   Vec            rscale;
9152   Vec            cglobalo, clocal;
9153   Vec            fglobal, fglobalo, flocal;
9154   PetscBool      regular;
9155 
9156   PetscFunctionBegin;
9157   PetscCall(DMGetFullDM(dmc, &dmco));
9158   PetscCall(DMGetFullDM(dmf, &dmfo));
9159   PetscCall(DMSetCoarseDM(dmfo, dmco));
9160   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9161   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9162   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9163   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9164   PetscCall(DMCreateLocalVector(dmc, &clocal));
9165   PetscCall(VecSet(cglobalo, 0.));
9166   PetscCall(VecSet(clocal, 0.));
9167   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9168   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9169   PetscCall(DMCreateLocalVector(dmf, &flocal));
9170   PetscCall(VecSet(fglobal, 0.));
9171   PetscCall(VecSet(fglobalo, 0.));
9172   PetscCall(VecSet(flocal, 0.));
9173   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9174   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9175   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9176   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9177   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9178   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9179   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9180   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9181   *shift = fglobal;
9182   PetscCall(VecDestroy(&flocal));
9183   PetscCall(VecDestroy(&fglobalo));
9184   PetscCall(VecDestroy(&clocal));
9185   PetscCall(VecDestroy(&cglobalo));
9186   PetscCall(VecDestroy(&rscale));
9187   PetscCall(MatDestroy(&interpo));
9188   PetscCall(DMDestroy(&dmfo));
9189   PetscCall(DMDestroy(&dmco));
9190   PetscFunctionReturn(0);
9191 }
9192 
9193 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9194 {
9195   PetscObject    shifto;
9196   Vec            shift;
9197 
9198   PetscFunctionBegin;
9199   if (!interp) {
9200     Vec rscale;
9201 
9202     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9203     PetscCall(VecDestroy(&rscale));
9204   } else {
9205     PetscCall(PetscObjectReference((PetscObject)interp));
9206   }
9207   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9208   if (!shifto) {
9209     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9210     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9211     shifto = (PetscObject) shift;
9212     PetscCall(VecDestroy(&shift));
9213   }
9214   shift = (Vec) shifto;
9215   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9216   PetscCall(VecAXPY(fineSol, 1.0, shift));
9217   PetscCall(MatDestroy(&interp));
9218   PetscFunctionReturn(0);
9219 }
9220 
9221 /* Pointwise interpolation
9222      Just code FEM for now
9223      u^f = I u^c
9224      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9225      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9226      I_{ij} = psi^f_i phi^c_j
9227 */
9228 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9229 {
9230   PetscSection   gsc, gsf;
9231   PetscInt       m, n;
9232   void          *ctx;
9233   DM             cdm;
9234   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9235 
9236   PetscFunctionBegin;
9237   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9238   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9239   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9240   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9241 
9242   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9243   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9244   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9245   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9246   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9247 
9248   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9249   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9250   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9251   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9252   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9253   if (scaling) {
9254     /* Use naive scaling */
9255     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9256   }
9257   PetscFunctionReturn(0);
9258 }
9259 
9260 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9261 {
9262   VecScatter     ctx;
9263 
9264   PetscFunctionBegin;
9265   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9266   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9267   PetscCall(VecScatterDestroy(&ctx));
9268   PetscFunctionReturn(0);
9269 }
9270 
9271 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9272                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9273                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9274                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9275 {
9276   const PetscInt Nc = uOff[1] - uOff[0];
9277   PetscInt       c;
9278   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9279 }
9280 
9281 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9282 {
9283   DM             dmc;
9284   PetscDS        ds;
9285   Vec            ones, locmass;
9286   IS             cellIS;
9287   PetscFormKey   key;
9288   PetscInt       depth;
9289 
9290   PetscFunctionBegin;
9291   PetscCall(DMClone(dm, &dmc));
9292   PetscCall(DMCopyDisc(dm, dmc));
9293   PetscCall(DMGetDS(dmc, &ds));
9294   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9295   PetscCall(DMCreateGlobalVector(dmc, mass));
9296   PetscCall(DMGetLocalVector(dmc, &ones));
9297   PetscCall(DMGetLocalVector(dmc, &locmass));
9298   PetscCall(DMPlexGetDepth(dmc, &depth));
9299   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9300   PetscCall(VecSet(locmass, 0.0));
9301   PetscCall(VecSet(ones, 1.0));
9302   key.label = NULL;
9303   key.value = 0;
9304   key.field = 0;
9305   key.part  = 0;
9306   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9307   PetscCall(ISDestroy(&cellIS));
9308   PetscCall(VecSet(*mass, 0.0));
9309   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9310   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9311   PetscCall(DMRestoreLocalVector(dmc, &ones));
9312   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9313   PetscCall(DMDestroy(&dmc));
9314   PetscFunctionReturn(0);
9315 }
9316 
9317 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9318 {
9319   PetscSection   gsc, gsf;
9320   PetscInt       m, n;
9321   void          *ctx;
9322   DM             cdm;
9323   PetscBool      regular;
9324 
9325   PetscFunctionBegin;
9326   if (dmFine == dmCoarse) {
9327     DM            dmc;
9328     PetscDS       ds;
9329     PetscWeakForm wf;
9330     Vec           u;
9331     IS            cellIS;
9332     PetscFormKey  key;
9333     PetscInt      depth;
9334 
9335     PetscCall(DMClone(dmFine, &dmc));
9336     PetscCall(DMCopyDisc(dmFine, dmc));
9337     PetscCall(DMGetDS(dmc, &ds));
9338     PetscCall(PetscDSGetWeakForm(ds, &wf));
9339     PetscCall(PetscWeakFormClear(wf));
9340     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9341     PetscCall(DMCreateMatrix(dmc, mass));
9342     PetscCall(DMGetGlobalVector(dmc, &u));
9343     PetscCall(DMPlexGetDepth(dmc, &depth));
9344     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9345     PetscCall(MatZeroEntries(*mass));
9346     key.label = NULL;
9347     key.value = 0;
9348     key.field = 0;
9349     key.part  = 0;
9350     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9351     PetscCall(ISDestroy(&cellIS));
9352     PetscCall(DMRestoreGlobalVector(dmc, &u));
9353     PetscCall(DMDestroy(&dmc));
9354   } else {
9355     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9356     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9357     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9358     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9359 
9360     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9361     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9362     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9363     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9364 
9365     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9366     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9367     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9368     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9369   }
9370   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9371   PetscFunctionReturn(0);
9372 }
9373 
9374 /*@
9375   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9376 
9377   Input Parameter:
9378 . dm - The DMPlex object
9379 
9380   Output Parameter:
9381 . regular - The flag
9382 
9383   Level: intermediate
9384 
9385 .seealso: `DMPlexSetRegularRefinement()`
9386 @*/
9387 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9388 {
9389   PetscFunctionBegin;
9390   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9391   PetscValidBoolPointer(regular, 2);
9392   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9393   PetscFunctionReturn(0);
9394 }
9395 
9396 /*@
9397   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9398 
9399   Input Parameters:
9400 + dm - The DMPlex object
9401 - regular - The flag
9402 
9403   Level: intermediate
9404 
9405 .seealso: `DMPlexGetRegularRefinement()`
9406 @*/
9407 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9408 {
9409   PetscFunctionBegin;
9410   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9411   ((DM_Plex *) dm->data)->regularRefinement = regular;
9412   PetscFunctionReturn(0);
9413 }
9414 
9415 /* anchors */
9416 /*@
9417   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9418   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9419 
9420   not collective
9421 
9422   Input Parameter:
9423 . dm - The DMPlex object
9424 
9425   Output Parameters:
9426 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9427 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9428 
9429   Level: intermediate
9430 
9431 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9432 @*/
9433 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9434 {
9435   DM_Plex *plex = (DM_Plex *)dm->data;
9436 
9437   PetscFunctionBegin;
9438   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9439   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9440   if (anchorSection) *anchorSection = plex->anchorSection;
9441   if (anchorIS) *anchorIS = plex->anchorIS;
9442   PetscFunctionReturn(0);
9443 }
9444 
9445 /*@
9446   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9447   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9448   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9449 
9450   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9451   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9452 
9453   collective on dm
9454 
9455   Input Parameters:
9456 + dm - The DMPlex object
9457 . 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).
9458 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9459 
9460   The reference counts of anchorSection and anchorIS are incremented.
9461 
9462   Level: intermediate
9463 
9464 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9465 @*/
9466 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9467 {
9468   DM_Plex        *plex = (DM_Plex *)dm->data;
9469   PetscMPIInt    result;
9470 
9471   PetscFunctionBegin;
9472   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9473   if (anchorSection) {
9474     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9475     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9476     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9477   }
9478   if (anchorIS) {
9479     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9480     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9481     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9482   }
9483 
9484   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9485   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9486   plex->anchorSection = anchorSection;
9487 
9488   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9489   PetscCall(ISDestroy(&plex->anchorIS));
9490   plex->anchorIS = anchorIS;
9491 
9492   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9493     PetscInt size, a, pStart, pEnd;
9494     const PetscInt *anchors;
9495 
9496     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9497     PetscCall(ISGetLocalSize(anchorIS,&size));
9498     PetscCall(ISGetIndices(anchorIS,&anchors));
9499     for (a = 0; a < size; a++) {
9500       PetscInt p;
9501 
9502       p = anchors[a];
9503       if (p >= pStart && p < pEnd) {
9504         PetscInt dof;
9505 
9506         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9507         if (dof) {
9508 
9509           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9510           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %" PetscInt_FMT " cannot be constrained and an anchor",p);
9511         }
9512       }
9513     }
9514     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9515   }
9516   /* reset the generic constraints */
9517   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9518   PetscFunctionReturn(0);
9519 }
9520 
9521 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9522 {
9523   PetscSection anchorSection;
9524   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9525 
9526   PetscFunctionBegin;
9527   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9528   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9529   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9530   PetscCall(PetscSectionGetNumFields(section,&numFields));
9531   if (numFields) {
9532     PetscInt f;
9533     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9534 
9535     for (f = 0; f < numFields; f++) {
9536       PetscInt numComp;
9537 
9538       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9539       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9540     }
9541   }
9542   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9543   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9544   pStart = PetscMax(pStart,sStart);
9545   pEnd   = PetscMin(pEnd,sEnd);
9546   pEnd   = PetscMax(pStart,pEnd);
9547   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9548   for (p = pStart; p < pEnd; p++) {
9549     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9550     if (dof) {
9551       PetscCall(PetscSectionGetDof(section,p,&dof));
9552       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9553       for (f = 0; f < numFields; f++) {
9554         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9555         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9556       }
9557     }
9558   }
9559   PetscCall(PetscSectionSetUp(*cSec));
9560   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9561   PetscFunctionReturn(0);
9562 }
9563 
9564 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9565 {
9566   PetscSection   aSec;
9567   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9568   const PetscInt *anchors;
9569   PetscInt       numFields, f;
9570   IS             aIS;
9571   MatType        mtype;
9572   PetscBool      iscuda,iskokkos;
9573 
9574   PetscFunctionBegin;
9575   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9576   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9577   PetscCall(PetscSectionGetStorageSize(section, &n));
9578   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9579   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9580   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9581   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9582   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9583   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9584   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9585   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9586   else mtype = MATSEQAIJ;
9587   PetscCall(MatSetType(*cMat,mtype));
9588   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9589   PetscCall(ISGetIndices(aIS,&anchors));
9590   /* cSec will be a subset of aSec and section */
9591   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9592   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9593   PetscCall(PetscMalloc1(m+1,&i));
9594   i[0] = 0;
9595   PetscCall(PetscSectionGetNumFields(section,&numFields));
9596   for (p = pStart; p < pEnd; p++) {
9597     PetscInt rDof, rOff, r;
9598 
9599     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9600     if (!rDof) continue;
9601     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9602     if (numFields) {
9603       for (f = 0; f < numFields; f++) {
9604         annz = 0;
9605         for (r = 0; r < rDof; r++) {
9606           a = anchors[rOff + r];
9607           if (a < sStart || a >= sEnd) continue;
9608           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9609           annz += aDof;
9610         }
9611         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9612         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9613         for (q = 0; q < dof; q++) {
9614           i[off + q + 1] = i[off + q] + annz;
9615         }
9616       }
9617     } else {
9618       annz = 0;
9619       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9620       for (q = 0; q < dof; q++) {
9621         a = anchors[rOff + q];
9622         if (a < sStart || a >= sEnd) continue;
9623         PetscCall(PetscSectionGetDof(section,a,&aDof));
9624         annz += aDof;
9625       }
9626       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9627       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9628       for (q = 0; q < dof; q++) {
9629         i[off + q + 1] = i[off + q] + annz;
9630       }
9631     }
9632   }
9633   nnz = i[m];
9634   PetscCall(PetscMalloc1(nnz,&j));
9635   offset = 0;
9636   for (p = pStart; p < pEnd; p++) {
9637     if (numFields) {
9638       for (f = 0; f < numFields; f++) {
9639         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9640         for (q = 0; q < dof; q++) {
9641           PetscInt rDof, rOff, r;
9642           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9643           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9644           for (r = 0; r < rDof; r++) {
9645             PetscInt s;
9646 
9647             a = anchors[rOff + r];
9648             if (a < sStart || a >= sEnd) continue;
9649             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9650             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9651             for (s = 0; s < aDof; s++) {
9652               j[offset++] = aOff + s;
9653             }
9654           }
9655         }
9656       }
9657     } else {
9658       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9659       for (q = 0; q < dof; q++) {
9660         PetscInt rDof, rOff, r;
9661         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9662         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9663         for (r = 0; r < rDof; r++) {
9664           PetscInt s;
9665 
9666           a = anchors[rOff + r];
9667           if (a < sStart || a >= sEnd) continue;
9668           PetscCall(PetscSectionGetDof(section,a,&aDof));
9669           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9670           for (s = 0; s < aDof; s++) {
9671             j[offset++] = aOff + s;
9672           }
9673         }
9674       }
9675     }
9676   }
9677   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9678   PetscCall(PetscFree(i));
9679   PetscCall(PetscFree(j));
9680   PetscCall(ISRestoreIndices(aIS,&anchors));
9681   PetscFunctionReturn(0);
9682 }
9683 
9684 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9685 {
9686   DM_Plex        *plex = (DM_Plex *)dm->data;
9687   PetscSection   anchorSection, section, cSec;
9688   Mat            cMat;
9689 
9690   PetscFunctionBegin;
9691   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9692   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9693   if (anchorSection) {
9694     PetscInt Nf;
9695 
9696     PetscCall(DMGetLocalSection(dm,&section));
9697     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9698     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9699     PetscCall(DMGetNumFields(dm,&Nf));
9700     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9701     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9702     PetscCall(PetscSectionDestroy(&cSec));
9703     PetscCall(MatDestroy(&cMat));
9704   }
9705   PetscFunctionReturn(0);
9706 }
9707 
9708 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9709 {
9710   IS             subis;
9711   PetscSection   section, subsection;
9712 
9713   PetscFunctionBegin;
9714   PetscCall(DMGetLocalSection(dm, &section));
9715   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9716   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9717   /* Create subdomain */
9718   PetscCall(DMPlexFilter(dm, label, value, subdm));
9719   /* Create submodel */
9720   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9721   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9722   PetscCall(DMSetLocalSection(*subdm, subsection));
9723   PetscCall(PetscSectionDestroy(&subsection));
9724   PetscCall(DMCopyDisc(dm, *subdm));
9725   /* Create map from submodel to global model */
9726   if (is) {
9727     PetscSection    sectionGlobal, subsectionGlobal;
9728     IS              spIS;
9729     const PetscInt *spmap;
9730     PetscInt       *subIndices;
9731     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9732     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9733 
9734     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9735     PetscCall(ISGetIndices(spIS, &spmap));
9736     PetscCall(PetscSectionGetNumFields(section, &Nf));
9737     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9738     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9739     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9740     for (p = pStart; p < pEnd; ++p) {
9741       PetscInt gdof, pSubSize  = 0;
9742 
9743       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9744       if (gdof > 0) {
9745         for (f = 0; f < Nf; ++f) {
9746           PetscInt fdof, fcdof;
9747 
9748           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9749           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9750           pSubSize += fdof-fcdof;
9751         }
9752         subSize += pSubSize;
9753         if (pSubSize) {
9754           if (bs < 0) {
9755             bs = pSubSize;
9756           } else if (bs != pSubSize) {
9757             /* Layout does not admit a pointwise block size */
9758             bs = 1;
9759           }
9760         }
9761       }
9762     }
9763     /* Must have same blocksize on all procs (some might have no points) */
9764     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9765     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9766     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9767     else                            {bs = bsMinMax[0];}
9768     PetscCall(PetscMalloc1(subSize, &subIndices));
9769     for (p = pStart; p < pEnd; ++p) {
9770       PetscInt gdof, goff;
9771 
9772       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9773       if (gdof > 0) {
9774         const PetscInt point = spmap[p];
9775 
9776         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9777         for (f = 0; f < Nf; ++f) {
9778           PetscInt fdof, fcdof, fc, f2, poff = 0;
9779 
9780           /* Can get rid of this loop by storing field information in the global section */
9781           for (f2 = 0; f2 < f; ++f2) {
9782             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9783             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9784             poff += fdof-fcdof;
9785           }
9786           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9787           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9788           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9789             subIndices[subOff] = goff+poff+fc;
9790           }
9791         }
9792       }
9793     }
9794     PetscCall(ISRestoreIndices(spIS, &spmap));
9795     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9796     if (bs > 1) {
9797       /* We need to check that the block size does not come from non-contiguous fields */
9798       PetscInt i, j, set = 1;
9799       for (i = 0; i < subSize; i += bs) {
9800         for (j = 0; j < bs; ++j) {
9801           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9802         }
9803       }
9804       if (set) PetscCall(ISSetBlockSize(*is, bs));
9805     }
9806     /* Attach nullspace */
9807     for (f = 0; f < Nf; ++f) {
9808       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9809       if ((*subdm)->nullspaceConstructors[f]) break;
9810     }
9811     if (f < Nf) {
9812       MatNullSpace nullSpace;
9813       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9814 
9815       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9816       PetscCall(MatNullSpaceDestroy(&nullSpace));
9817     }
9818   }
9819   PetscFunctionReturn(0);
9820 }
9821 
9822 /*@
9823   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9824 
9825   Input Parameter:
9826 - dm - The DM
9827 
9828   Level: developer
9829 
9830   Options Database Keys:
9831 . -dm_plex_monitor_throughput - Activate the monitor
9832 
9833 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9834 @*/
9835 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9836 {
9837 #if defined(PETSC_USE_LOG)
9838   PetscStageLog      stageLog;
9839   PetscLogEvent      event;
9840   PetscLogStage      stage;
9841   PetscEventPerfInfo eventInfo;
9842   PetscReal          cellRate, flopRate;
9843   PetscInt           cStart, cEnd, Nf, N;
9844   const char        *name;
9845 #endif
9846 
9847   PetscFunctionBegin;
9848   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9849 #if defined(PETSC_USE_LOG)
9850   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9851   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9852   PetscCall(DMGetNumFields(dm, &Nf));
9853   PetscCall(PetscLogGetStageLog(&stageLog));
9854   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9855   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9856   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9857   N        = (cEnd - cStart)*Nf*eventInfo.count;
9858   flopRate = eventInfo.flops/eventInfo.time;
9859   cellRate = N/eventInfo.time;
9860   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)));
9861 #else
9862   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9863 #endif
9864   PetscFunctionReturn(0);
9865 }
9866