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