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