xref: /petsc/src/dm/impls/plex/plex.c (revision 8da24d32403b711d95ab43313acc68d97deb82f3)
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           PetscCheck(numVals % Nc == 0,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         PetscCheck(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         PetscCheck(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         PetscCheck(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       PetscCheck(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         PetscCheck(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                 PetscCheck(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     PetscCheck(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     PetscCall(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     PetscCall(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     PetscCall(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   PetscCheck(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   PetscCall(MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm)));
1666   PetscCall(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     PetscCheck(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     PetscCheck(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     PetscCheck(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     PetscCheck(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   PetscCheck(!(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     PetscCheck(!(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   PetscCheck(!(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   PetscCheck(!(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   PetscCheck(!(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   PetscCheck(!(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   PetscCheck(!(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   PetscCheck(!(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   PetscCheck(!(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     PetscCheck(!(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   PetscCheck(!(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   PetscCheck(!(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   PetscCheck(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       PetscCheck(!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     PetscCheck(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     PetscCheck(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     PetscCheck(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   PetscCheck(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 PetscCheck(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     PetscCheck(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) {PetscCheck(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     PetscCheck(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       PetscCheck(*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     PetscCheck(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     PetscCheck(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   PetscCheck(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   PetscCheck(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   PetscCheck(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   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7529   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7530   if (ierr) {
7531     PetscMPIInt    rank;
7532 
7533     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7534     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7535     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7536     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7537     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7538     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7539   }
7540   if (mesh->printFEM > 1) {
7541     PetscInt i;
7542     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7543     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %D", indices[i]));
7544     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7545   }
7546 
7547   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7548   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7549   PetscFunctionReturn(0);
7550 }
7551 
7552 /*@C
7553   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7554 
7555   Not collective
7556 
7557   Input Parameters:
7558 + dmRow - The DM for the row fields
7559 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7560 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7561 . dmCol - The DM for the column fields
7562 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7563 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7564 . A - The matrix
7565 . point - The point in the DMs
7566 . values - The array of values
7567 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7568 
7569   Level: intermediate
7570 
7571 .seealso DMPlexMatSetClosure(), DMPlexVecGetClosure(), DMPlexVecSetClosure()
7572 @*/
7573 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7574 {
7575   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7576   PetscInt          *indicesRow, *indicesCol;
7577   PetscInt           numIndicesRow, numIndicesCol;
7578   const PetscScalar *valuesOrig = values;
7579   PetscErrorCode     ierr;
7580 
7581   PetscFunctionBegin;
7582   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7583   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7584   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7585   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7586   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7587   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7588   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7589   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7590   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7591   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7592   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7593 
7594   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7595   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7596 
7597   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7598   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7599   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7600   if (ierr) {
7601     PetscMPIInt    rank;
7602 
7603     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7604     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7605     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7606     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7607     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7608     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7609   }
7610 
7611   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7612   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7613   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7614   PetscFunctionReturn(0);
7615 }
7616 
7617 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7618 {
7619   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7620   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7621   PetscInt       *cpoints = NULL;
7622   PetscInt       *findices, *cindices;
7623   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7624   PetscInt        foffsets[32], coffsets[32];
7625   DMPolytopeType  ct;
7626   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7627   PetscErrorCode  ierr;
7628 
7629   PetscFunctionBegin;
7630   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7631   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7632   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7633   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7634   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7635   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7636   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7637   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7638   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7639   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7640   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7641   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7642   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %D limited to 31", numFields);
7643   PetscCall(PetscArrayzero(foffsets, 32));
7644   PetscCall(PetscArrayzero(coffsets, 32));
7645   /* Column indices */
7646   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7647   maxFPoints = numCPoints;
7648   /* Compress out points not in the section */
7649   /*   TODO: Squeeze out points with 0 dof as well */
7650   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7651   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7652     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7653       cpoints[q*2]   = cpoints[p];
7654       cpoints[q*2+1] = cpoints[p+1];
7655       ++q;
7656     }
7657   }
7658   numCPoints = q;
7659   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7660     PetscInt fdof;
7661 
7662     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7663     if (!dof) continue;
7664     for (f = 0; f < numFields; ++f) {
7665       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7666       coffsets[f+1] += fdof;
7667     }
7668     numCIndices += dof;
7669   }
7670   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7671   /* Row indices */
7672   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7673   {
7674     DMPlexTransform tr;
7675     DMPolytopeType *rct;
7676     PetscInt       *rsize, *rcone, *rornt, Nt;
7677 
7678     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7679     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7680     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7681     numSubcells = rsize[Nt-1];
7682     PetscCall(DMPlexTransformDestroy(&tr));
7683   }
7684   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7685   for (r = 0, q = 0; r < numSubcells; ++r) {
7686     /* TODO Map from coarse to fine cells */
7687     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7688     /* Compress out points not in the section */
7689     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7690     for (p = 0; p < numFPoints*2; p += 2) {
7691       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7692         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7693         if (!dof) continue;
7694         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7695         if (s < q) continue;
7696         ftotpoints[q*2]   = fpoints[p];
7697         ftotpoints[q*2+1] = fpoints[p+1];
7698         ++q;
7699       }
7700     }
7701     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7702   }
7703   numFPoints = q;
7704   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7705     PetscInt fdof;
7706 
7707     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7708     if (!dof) continue;
7709     for (f = 0; f < numFields; ++f) {
7710       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7711       foffsets[f+1] += fdof;
7712     }
7713     numFIndices += dof;
7714   }
7715   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7716 
7717   PetscCheckFalse(numFields && foffsets[numFields] != numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", foffsets[numFields], numFIndices);
7718   PetscCheckFalse(numFields && coffsets[numFields] != numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", coffsets[numFields], numCIndices);
7719   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7720   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7721   if (numFields) {
7722     const PetscInt **permsF[32] = {NULL};
7723     const PetscInt **permsC[32] = {NULL};
7724 
7725     for (f = 0; f < numFields; f++) {
7726       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7727       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7728     }
7729     for (p = 0; p < numFPoints; p++) {
7730       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7731       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7732     }
7733     for (p = 0; p < numCPoints; p++) {
7734       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7735       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7736     }
7737     for (f = 0; f < numFields; f++) {
7738       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7739       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7740     }
7741   } else {
7742     const PetscInt **permsF = NULL;
7743     const PetscInt **permsC = NULL;
7744 
7745     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7746     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7747     for (p = 0, off = 0; p < numFPoints; p++) {
7748       const PetscInt *perm = permsF ? permsF[p] : NULL;
7749 
7750       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7751       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7752     }
7753     for (p = 0, off = 0; p < numCPoints; p++) {
7754       const PetscInt *perm = permsC ? permsC[p] : NULL;
7755 
7756       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7757       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7758     }
7759     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7760     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7761   }
7762   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7763   /* TODO: flips */
7764   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7765   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7766   if (ierr) {
7767     PetscMPIInt    rank;
7768 
7769     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7770     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7771     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7772     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7773     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7774   }
7775   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7776   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7777   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7778   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7779   PetscFunctionReturn(0);
7780 }
7781 
7782 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7783 {
7784   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7785   PetscInt      *cpoints = NULL;
7786   PetscInt       foffsets[32], coffsets[32];
7787   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7788   DMPolytopeType ct;
7789   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7790 
7791   PetscFunctionBegin;
7792   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7793   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7794   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7795   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7796   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7797   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7798   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7799   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7800   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7801   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7802   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7803   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %D limited to 31", numFields);
7804   PetscCall(PetscArrayzero(foffsets, 32));
7805   PetscCall(PetscArrayzero(coffsets, 32));
7806   /* Column indices */
7807   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7808   maxFPoints = numCPoints;
7809   /* Compress out points not in the section */
7810   /*   TODO: Squeeze out points with 0 dof as well */
7811   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7812   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7813     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7814       cpoints[q*2]   = cpoints[p];
7815       cpoints[q*2+1] = cpoints[p+1];
7816       ++q;
7817     }
7818   }
7819   numCPoints = q;
7820   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7821     PetscInt fdof;
7822 
7823     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7824     if (!dof) continue;
7825     for (f = 0; f < numFields; ++f) {
7826       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7827       coffsets[f+1] += fdof;
7828     }
7829     numCIndices += dof;
7830   }
7831   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7832   /* Row indices */
7833   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7834   {
7835     DMPlexTransform tr;
7836     DMPolytopeType *rct;
7837     PetscInt       *rsize, *rcone, *rornt, Nt;
7838 
7839     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7840     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7841     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7842     numSubcells = rsize[Nt-1];
7843     PetscCall(DMPlexTransformDestroy(&tr));
7844   }
7845   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7846   for (r = 0, q = 0; r < numSubcells; ++r) {
7847     /* TODO Map from coarse to fine cells */
7848     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7849     /* Compress out points not in the section */
7850     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7851     for (p = 0; p < numFPoints*2; p += 2) {
7852       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7853         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7854         if (!dof) continue;
7855         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7856         if (s < q) continue;
7857         ftotpoints[q*2]   = fpoints[p];
7858         ftotpoints[q*2+1] = fpoints[p+1];
7859         ++q;
7860       }
7861     }
7862     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7863   }
7864   numFPoints = q;
7865   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7866     PetscInt fdof;
7867 
7868     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7869     if (!dof) continue;
7870     for (f = 0; f < numFields; ++f) {
7871       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7872       foffsets[f+1] += fdof;
7873     }
7874     numFIndices += dof;
7875   }
7876   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7877 
7878   PetscCheckFalse(numFields && foffsets[numFields] != numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", foffsets[numFields], numFIndices);
7879   PetscCheckFalse(numFields && coffsets[numFields] != numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", coffsets[numFields], numCIndices);
7880   if (numFields) {
7881     const PetscInt **permsF[32] = {NULL};
7882     const PetscInt **permsC[32] = {NULL};
7883 
7884     for (f = 0; f < numFields; f++) {
7885       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7886       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7887     }
7888     for (p = 0; p < numFPoints; p++) {
7889       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7890       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7891     }
7892     for (p = 0; p < numCPoints; p++) {
7893       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7894       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7895     }
7896     for (f = 0; f < numFields; f++) {
7897       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7898       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7899     }
7900   } else {
7901     const PetscInt **permsF = NULL;
7902     const PetscInt **permsC = NULL;
7903 
7904     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7905     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7906     for (p = 0, off = 0; p < numFPoints; p++) {
7907       const PetscInt *perm = permsF ? permsF[p] : NULL;
7908 
7909       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7910       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7911     }
7912     for (p = 0, off = 0; p < numCPoints; p++) {
7913       const PetscInt *perm = permsC ? permsC[p] : NULL;
7914 
7915       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7916       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7917     }
7918     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7919     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7920   }
7921   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7922   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7923   PetscFunctionReturn(0);
7924 }
7925 
7926 /*@C
7927   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7928 
7929   Input Parameter:
7930 . dm   - The DMPlex object
7931 
7932   Output Parameter:
7933 . cellHeight - The height of a cell
7934 
7935   Level: developer
7936 
7937 .seealso DMPlexSetVTKCellHeight()
7938 @*/
7939 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7940 {
7941   DM_Plex *mesh = (DM_Plex*) dm->data;
7942 
7943   PetscFunctionBegin;
7944   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7945   PetscValidIntPointer(cellHeight, 2);
7946   *cellHeight = mesh->vtkCellHeight;
7947   PetscFunctionReturn(0);
7948 }
7949 
7950 /*@C
7951   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7952 
7953   Input Parameters:
7954 + dm   - The DMPlex object
7955 - cellHeight - The height of a cell
7956 
7957   Level: developer
7958 
7959 .seealso DMPlexGetVTKCellHeight()
7960 @*/
7961 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7962 {
7963   DM_Plex *mesh = (DM_Plex*) dm->data;
7964 
7965   PetscFunctionBegin;
7966   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7967   mesh->vtkCellHeight = cellHeight;
7968   PetscFunctionReturn(0);
7969 }
7970 
7971 /*@
7972   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7973 
7974   Input Parameter:
7975 . dm - The DMPlex object
7976 
7977   Output Parameters:
7978 + gcStart - The first ghost cell, or NULL
7979 - gcEnd   - The upper bound on ghost cells, or NULL
7980 
7981   Level: advanced
7982 
7983 .seealso DMPlexConstructGhostCells(), DMPlexGetGhostCellStratum()
7984 @*/
7985 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
7986 {
7987   DMLabel        ctLabel;
7988 
7989   PetscFunctionBegin;
7990   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7991   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
7992   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
7993   PetscFunctionReturn(0);
7994 }
7995 
7996 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
7997 {
7998   PetscSection   section, globalSection;
7999   PetscInt      *numbers, p;
8000 
8001   PetscFunctionBegin;
8002   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8003   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8004   for (p = pStart; p < pEnd; ++p) {
8005     PetscCall(PetscSectionSetDof(section, p, 1));
8006   }
8007   PetscCall(PetscSectionSetUp(section));
8008   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8009   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8010   for (p = pStart; p < pEnd; ++p) {
8011     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8012     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8013     else                       numbers[p-pStart] += shift;
8014   }
8015   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8016   if (globalSize) {
8017     PetscLayout layout;
8018     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8019     PetscCall(PetscLayoutGetSize(layout, globalSize));
8020     PetscCall(PetscLayoutDestroy(&layout));
8021   }
8022   PetscCall(PetscSectionDestroy(&section));
8023   PetscCall(PetscSectionDestroy(&globalSection));
8024   PetscFunctionReturn(0);
8025 }
8026 
8027 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8028 {
8029   PetscInt       cellHeight, cStart, cEnd;
8030 
8031   PetscFunctionBegin;
8032   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8033   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8034   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8035   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8036   PetscFunctionReturn(0);
8037 }
8038 
8039 /*@
8040   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8041 
8042   Input Parameter:
8043 . dm   - The DMPlex object
8044 
8045   Output Parameter:
8046 . globalCellNumbers - Global cell numbers for all cells on this process
8047 
8048   Level: developer
8049 
8050 .seealso DMPlexGetVertexNumbering()
8051 @*/
8052 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8053 {
8054   DM_Plex       *mesh = (DM_Plex*) dm->data;
8055 
8056   PetscFunctionBegin;
8057   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8058   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8059   *globalCellNumbers = mesh->globalCellNumbers;
8060   PetscFunctionReturn(0);
8061 }
8062 
8063 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8064 {
8065   PetscInt       vStart, vEnd;
8066 
8067   PetscFunctionBegin;
8068   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8069   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8070   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8071   PetscFunctionReturn(0);
8072 }
8073 
8074 /*@
8075   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8076 
8077   Input Parameter:
8078 . dm   - The DMPlex object
8079 
8080   Output Parameter:
8081 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8082 
8083   Level: developer
8084 
8085 .seealso DMPlexGetCellNumbering()
8086 @*/
8087 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8088 {
8089   DM_Plex       *mesh = (DM_Plex*) dm->data;
8090 
8091   PetscFunctionBegin;
8092   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8093   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8094   *globalVertexNumbers = mesh->globalVertexNumbers;
8095   PetscFunctionReturn(0);
8096 }
8097 
8098 /*@
8099   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
8100 
8101   Input Parameter:
8102 . dm   - The DMPlex object
8103 
8104   Output Parameter:
8105 . globalPointNumbers - Global numbers for all points on this process
8106 
8107   Level: developer
8108 
8109 .seealso DMPlexGetCellNumbering()
8110 @*/
8111 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8112 {
8113   IS             nums[4];
8114   PetscInt       depths[4], gdepths[4], starts[4];
8115   PetscInt       depth, d, shift = 0;
8116 
8117   PetscFunctionBegin;
8118   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8119   PetscCall(DMPlexGetDepth(dm, &depth));
8120   /* For unstratified meshes use dim instead of depth */
8121   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8122   for (d = 0; d <= depth; ++d) {
8123     PetscInt end;
8124 
8125     depths[d] = depth-d;
8126     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8127     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8128   }
8129   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8130   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8131   for (d = 0; d <= depth; ++d) {
8132     PetscCheckFalse(starts[d] >= 0 && depths[d] != gdepths[d],PETSC_COMM_SELF,PETSC_ERR_PLIB,"Expected depth %D, found %D",depths[d],gdepths[d]);
8133   }
8134   for (d = 0; d <= depth; ++d) {
8135     PetscInt pStart, pEnd, gsize;
8136 
8137     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8138     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8139     shift += gsize;
8140   }
8141   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8142   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8143   PetscFunctionReturn(0);
8144 }
8145 
8146 /*@
8147   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8148 
8149   Input Parameter:
8150 . dm - The DMPlex object
8151 
8152   Output Parameter:
8153 . ranks - The rank field
8154 
8155   Options Database Keys:
8156 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8157 
8158   Level: intermediate
8159 
8160 .seealso: DMView()
8161 @*/
8162 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8163 {
8164   DM             rdm;
8165   PetscFE        fe;
8166   PetscScalar   *r;
8167   PetscMPIInt    rank;
8168   DMPolytopeType ct;
8169   PetscInt       dim, cStart, cEnd, c;
8170   PetscBool      simplex;
8171 
8172   PetscFunctionBeginUser;
8173   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8174   PetscValidPointer(ranks, 2);
8175   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8176   PetscCall(DMClone(dm, &rdm));
8177   PetscCall(DMGetDimension(rdm, &dim));
8178   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8179   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8180   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8181   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8182   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8183   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8184   PetscCall(PetscFEDestroy(&fe));
8185   PetscCall(DMCreateDS(rdm));
8186   PetscCall(DMCreateGlobalVector(rdm, ranks));
8187   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8188   PetscCall(VecGetArray(*ranks, &r));
8189   for (c = cStart; c < cEnd; ++c) {
8190     PetscScalar *lr;
8191 
8192     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8193     if (lr) *lr = rank;
8194   }
8195   PetscCall(VecRestoreArray(*ranks, &r));
8196   PetscCall(DMDestroy(&rdm));
8197   PetscFunctionReturn(0);
8198 }
8199 
8200 /*@
8201   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8202 
8203   Input Parameters:
8204 + dm    - The DMPlex
8205 - label - The DMLabel
8206 
8207   Output Parameter:
8208 . val - The label value field
8209 
8210   Options Database Keys:
8211 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8212 
8213   Level: intermediate
8214 
8215 .seealso: DMView()
8216 @*/
8217 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8218 {
8219   DM             rdm;
8220   PetscFE        fe;
8221   PetscScalar   *v;
8222   PetscInt       dim, cStart, cEnd, c;
8223 
8224   PetscFunctionBeginUser;
8225   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8226   PetscValidPointer(label, 2);
8227   PetscValidPointer(val, 3);
8228   PetscCall(DMClone(dm, &rdm));
8229   PetscCall(DMGetDimension(rdm, &dim));
8230   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8231   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8232   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8233   PetscCall(PetscFEDestroy(&fe));
8234   PetscCall(DMCreateDS(rdm));
8235   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8236   PetscCall(DMCreateGlobalVector(rdm, val));
8237   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8238   PetscCall(VecGetArray(*val, &v));
8239   for (c = cStart; c < cEnd; ++c) {
8240     PetscScalar *lv;
8241     PetscInt     cval;
8242 
8243     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8244     PetscCall(DMLabelGetValue(label, c, &cval));
8245     *lv = cval;
8246   }
8247   PetscCall(VecRestoreArray(*val, &v));
8248   PetscCall(DMDestroy(&rdm));
8249   PetscFunctionReturn(0);
8250 }
8251 
8252 /*@
8253   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8254 
8255   Input Parameter:
8256 . dm - The DMPlex object
8257 
8258   Notes:
8259   This is a useful diagnostic when creating meshes programmatically.
8260 
8261   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8262 
8263   Level: developer
8264 
8265 .seealso: DMCreate(), DMSetFromOptions()
8266 @*/
8267 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8268 {
8269   PetscSection    coneSection, supportSection;
8270   const PetscInt *cone, *support;
8271   PetscInt        coneSize, c, supportSize, s;
8272   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8273   PetscBool       storagecheck = PETSC_TRUE;
8274 
8275   PetscFunctionBegin;
8276   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8277   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8278   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8279   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8280   /* Check that point p is found in the support of its cone points, and vice versa */
8281   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8282   for (p = pStart; p < pEnd; ++p) {
8283     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8284     PetscCall(DMPlexGetCone(dm, p, &cone));
8285     for (c = 0; c < coneSize; ++c) {
8286       PetscBool dup = PETSC_FALSE;
8287       PetscInt  d;
8288       for (d = c-1; d >= 0; --d) {
8289         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8290       }
8291       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8292       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8293       for (s = 0; s < supportSize; ++s) {
8294         if (support[s] == p) break;
8295       }
8296       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8297         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %D cone: ", p));
8298         for (s = 0; s < coneSize; ++s) {
8299           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%D, ", cone[s]));
8300         }
8301         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8302         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %D support: ", cone[c]));
8303         for (s = 0; s < supportSize; ++s) {
8304           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%D, ", support[s]));
8305         }
8306         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8307         PetscCheck(!dup,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %D not repeatedly found in support of repeated cone point %D", p, cone[c]);
8308         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %D not found in support of cone point %D", p, cone[c]);
8309       }
8310     }
8311     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8312     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8313     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8314     PetscCall(DMPlexGetSupport(dm, p, &support));
8315     for (s = 0; s < supportSize; ++s) {
8316       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8317       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8318       for (c = 0; c < coneSize; ++c) {
8319         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8320         if (cone[c] != pp) { c = 0; break; }
8321         if (cone[c] == p) break;
8322       }
8323       if (c >= coneSize) {
8324         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %D support: ", p));
8325         for (c = 0; c < supportSize; ++c) {
8326           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%D, ", support[c]));
8327         }
8328         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8329         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %D cone: ", support[s]));
8330         for (c = 0; c < coneSize; ++c) {
8331           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%D, ", cone[c]));
8332         }
8333         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8334         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %D not found in cone of support point %D", p, support[s]);
8335       }
8336     }
8337   }
8338   if (storagecheck) {
8339     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8340     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8341     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %D != Total support size %D", csize, ssize);
8342   }
8343   PetscFunctionReturn(0);
8344 }
8345 
8346 /*
8347   For submeshes with cohesive cells (see DMPlexConstructCohesiveCells()), we allow a special case where some of the boundary of a face (edges and vertices) are not duplicated. We call these special boundary points "unsplit", since the same edge or vertex appears in both copies of the face. These unsplit points throw off our counting, so we have to explicitly account for them here.
8348 */
8349 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8350 {
8351   DMPolytopeType  cct;
8352   PetscInt        ptpoints[4];
8353   const PetscInt *cone, *ccone, *ptcone;
8354   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8355 
8356   PetscFunctionBegin;
8357   *unsplit = 0;
8358   switch (ct) {
8359     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8360       ptpoints[npt++] = c;
8361       break;
8362     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8363       PetscCall(DMPlexGetCone(dm, c, &cone));
8364       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8365       for (cp = 0; cp < coneSize; ++cp) {
8366         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8367         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8368       }
8369       break;
8370     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8371     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8372       PetscCall(DMPlexGetCone(dm, c, &cone));
8373       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8374       for (cp = 0; cp < coneSize; ++cp) {
8375         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8376         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8377         for (ccp = 0; ccp < cconeSize; ++ccp) {
8378           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8379           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8380             PetscInt p;
8381             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8382             if (p == npt) ptpoints[npt++] = ccone[ccp];
8383           }
8384         }
8385       }
8386       break;
8387     default: break;
8388   }
8389   for (pt = 0; pt < npt; ++pt) {
8390     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8391     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8392   }
8393   PetscFunctionReturn(0);
8394 }
8395 
8396 /*@
8397   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8398 
8399   Input Parameters:
8400 + dm - The DMPlex object
8401 - cellHeight - Normally 0
8402 
8403   Notes:
8404   This is a useful diagnostic when creating meshes programmatically.
8405   Currently applicable only to homogeneous simplex or tensor meshes.
8406 
8407   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8408 
8409   Level: developer
8410 
8411 .seealso: DMCreate(), DMSetFromOptions()
8412 @*/
8413 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8414 {
8415   DMPlexInterpolatedFlag interp;
8416   DMPolytopeType         ct;
8417   PetscInt               vStart, vEnd, cStart, cEnd, c;
8418 
8419   PetscFunctionBegin;
8420   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8421   PetscCall(DMPlexIsInterpolated(dm, &interp));
8422   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8423   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8424   for (c = cStart; c < cEnd; ++c) {
8425     PetscInt *closure = NULL;
8426     PetscInt  coneSize, closureSize, cl, Nv = 0;
8427 
8428     PetscCall(DMPlexGetCellType(dm, c, &ct));
8429     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %D has no cell type", c);
8430     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8431     if (interp == DMPLEX_INTERPOLATED_FULL) {
8432       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8433       PetscCheck(coneSize == DMPolytopeTypeGetConeSize(ct),PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %D of type %s has cone size %D != %D", c, DMPolytopeTypes[ct], coneSize, DMPolytopeTypeGetConeSize(ct));
8434     }
8435     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8436     for (cl = 0; cl < closureSize*2; cl += 2) {
8437       const PetscInt p = closure[cl];
8438       if ((p >= vStart) && (p < vEnd)) ++Nv;
8439     }
8440     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8441     /* Special Case: Tensor faces with identified vertices */
8442     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8443       PetscInt unsplit;
8444 
8445       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8446       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8447     }
8448     PetscCheck(Nv == DMPolytopeTypeGetNumVertices(ct),PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %D of type %s has %D vertices != %D", c, DMPolytopeTypes[ct], Nv, DMPolytopeTypeGetNumVertices(ct));
8449   }
8450   PetscFunctionReturn(0);
8451 }
8452 
8453 /*@
8454   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8455 
8456   Not Collective
8457 
8458   Input Parameters:
8459 + dm - The DMPlex object
8460 - cellHeight - Normally 0
8461 
8462   Notes:
8463   This is a useful diagnostic when creating meshes programmatically.
8464   This routine is only relevant for meshes that are fully interpolated across all ranks.
8465   It will error out if a partially interpolated mesh is given on some rank.
8466   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8467 
8468   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8469 
8470   Level: developer
8471 
8472 .seealso: DMCreate(), DMPlexGetVTKCellHeight(), DMSetFromOptions()
8473 @*/
8474 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8475 {
8476   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8477   DMPlexInterpolatedFlag interpEnum;
8478 
8479   PetscFunctionBegin;
8480   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8481   PetscCall(DMPlexIsInterpolated(dm, &interpEnum));
8482   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8483   if (interpEnum == DMPLEX_INTERPOLATED_PARTIAL) {
8484     PetscMPIInt rank;
8485     MPI_Comm    comm;
8486 
8487     PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
8488     PetscCallMPI(MPI_Comm_rank(comm, &rank));
8489     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Mesh is only partially interpolated on rank %d, this is currently not supported", rank);
8490   }
8491 
8492   PetscCall(DMGetDimension(dm, &dim));
8493   PetscCall(DMPlexGetDepth(dm, &depth));
8494   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8495   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8496     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8497     for (c = cStart; c < cEnd; ++c) {
8498       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8499       const DMPolytopeType *faceTypes;
8500       DMPolytopeType        ct;
8501       PetscInt              numFaces, coneSize, f;
8502       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8503 
8504       PetscCall(DMPlexGetCellType(dm, c, &ct));
8505       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8506       if (unsplit) continue;
8507       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8508       PetscCall(DMPlexGetCone(dm, c, &cone));
8509       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8510       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8511       for (cl = 0; cl < closureSize*2; cl += 2) {
8512         const PetscInt p = closure[cl];
8513         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8514       }
8515       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8516       PetscCheck(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);
8517       for (f = 0; f < numFaces; ++f) {
8518         DMPolytopeType fct;
8519         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8520 
8521         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8522         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8523         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8524           const PetscInt p = fclosure[cl];
8525           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8526         }
8527         PetscCheck(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]);
8528         for (v = 0; v < fnumCorners; ++v) {
8529           if (fclosure[v] != faces[fOff+v]) {
8530             PetscInt v1;
8531 
8532             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8533             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %D", fclosure[v1]));
8534             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8535             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %D", faces[fOff+v1]));
8536             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8537             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]);
8538           }
8539         }
8540         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8541         fOff += faceSizes[f];
8542       }
8543       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8544       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8545     }
8546   }
8547   PetscFunctionReturn(0);
8548 }
8549 
8550 /*@
8551   DMPlexCheckGeometry - Check the geometry of mesh cells
8552 
8553   Input Parameter:
8554 . dm - The DMPlex object
8555 
8556   Notes:
8557   This is a useful diagnostic when creating meshes programmatically.
8558 
8559   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8560 
8561   Level: developer
8562 
8563 .seealso: DMCreate(), DMSetFromOptions()
8564 @*/
8565 PetscErrorCode DMPlexCheckGeometry(DM dm)
8566 {
8567   Vec            coordinates;
8568   PetscReal      detJ, J[9], refVol = 1.0;
8569   PetscReal      vol;
8570   PetscBool      periodic;
8571   PetscInt       dim, depth, dE, d, cStart, cEnd, c;
8572 
8573   PetscFunctionBegin;
8574   PetscCall(DMGetDimension(dm, &dim));
8575   PetscCall(DMGetCoordinateDim(dm, &dE));
8576   if (dim != dE) PetscFunctionReturn(0);
8577   PetscCall(DMPlexGetDepth(dm, &depth));
8578   PetscCall(DMGetPeriodicity(dm, &periodic, NULL, NULL, NULL));
8579   for (d = 0; d < dim; ++d) refVol *= 2.0;
8580   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8581   /* Make sure local coordinates are created, because that step is collective */
8582   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8583   for (c = cStart; c < cEnd; ++c) {
8584     DMPolytopeType ct;
8585     PetscInt       unsplit;
8586     PetscBool      ignoreZeroVol = PETSC_FALSE;
8587 
8588     PetscCall(DMPlexGetCellType(dm, c, &ct));
8589     switch (ct) {
8590       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8591       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8592       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8593         ignoreZeroVol = PETSC_TRUE; break;
8594       default: break;
8595     }
8596     switch (ct) {
8597       case DM_POLYTOPE_TRI_PRISM:
8598       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8599       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8600       case DM_POLYTOPE_PYRAMID:
8601         continue;
8602       default: break;
8603     }
8604     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8605     if (unsplit) continue;
8606     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8607     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);
8608     PetscCall(PetscInfo(dm, "Cell %D FEM Volume %g\n", c, (double) detJ*refVol));
8609     if (depth > 1 && !periodic) {
8610       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8611       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);
8612       PetscCall(PetscInfo(dm, "Cell %D FVM Volume %g\n", c, (double) vol));
8613     }
8614   }
8615   PetscFunctionReturn(0);
8616 }
8617 
8618 /*@
8619   DMPlexCheckPointSF - Check that several necessary conditions are met for the point SF of this plex.
8620 
8621   Input Parameters:
8622 . dm - The DMPlex object
8623 
8624   Notes:
8625   This is mainly intended for debugging/testing purposes.
8626   It currently checks only meshes with no partition overlapping.
8627 
8628   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8629 
8630   Level: developer
8631 
8632 .seealso: DMGetPointSF(), DMSetFromOptions()
8633 @*/
8634 PetscErrorCode DMPlexCheckPointSF(DM dm)
8635 {
8636   PetscSF         pointSF;
8637   PetscInt        cellHeight, cStart, cEnd, l, nleaves, nroots, overlap;
8638   const PetscInt *locals, *rootdegree;
8639   PetscBool       distributed;
8640 
8641   PetscFunctionBegin;
8642   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8643   PetscCall(DMGetPointSF(dm, &pointSF));
8644   PetscCall(DMPlexIsDistributed(dm, &distributed));
8645   if (!distributed) PetscFunctionReturn(0);
8646   PetscCall(DMPlexGetOverlap(dm, &overlap));
8647   if (overlap) {
8648     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "Warning: DMPlexCheckPointSF() is currently not implemented for meshes with partition overlapping"));
8649     PetscFunctionReturn(0);
8650   }
8651   PetscCheck(pointSF,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "This DMPlex is distributed but does not have PointSF attached");
8652   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, NULL));
8653   PetscCheck(nroots >= 0,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "This DMPlex is distributed but its PointSF has no graph set");
8654   PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8655   PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8656 
8657   /* 1) check there are no faces in 2D, cells in 3D, in interface */
8658   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8659   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8660   for (l = 0; l < nleaves; ++l) {
8661     const PetscInt point = locals[l];
8662 
8663     PetscCheckFalse(point >= cStart && point < cEnd,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %D which is a cell", point);
8664   }
8665 
8666   /* 2) if some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8667   for (l = 0; l < nleaves; ++l) {
8668     const PetscInt  point = locals[l];
8669     const PetscInt *cone;
8670     PetscInt        coneSize, c, idx;
8671 
8672     PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8673     PetscCall(DMPlexGetCone(dm, point, &cone));
8674     for (c = 0; c < coneSize; ++c) {
8675       if (!rootdegree[cone[c]]) {
8676         PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8677         PetscCheck(idx >= 0,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %D but not %D from its cone", point, cone[c]);
8678       }
8679     }
8680   }
8681   PetscFunctionReturn(0);
8682 }
8683 
8684 PetscErrorCode DMPlexCheckAll_Internal(DM dm, PetscInt cellHeight)
8685 {
8686   PetscFunctionBegin;
8687   PetscCall(DMPlexCheckSymmetry(dm));
8688   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8689   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8690   PetscCall(DMPlexCheckGeometry(dm));
8691   PetscCall(DMPlexCheckPointSF(dm));
8692   PetscCall(DMPlexCheckInterfaceCones(dm));
8693   PetscFunctionReturn(0);
8694 }
8695 
8696 typedef struct cell_stats
8697 {
8698   PetscReal min, max, sum, squaresum;
8699   PetscInt  count;
8700 } cell_stats_t;
8701 
8702 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8703 {
8704   PetscInt i, N = *len;
8705 
8706   for (i = 0; i < N; i++) {
8707     cell_stats_t *A = (cell_stats_t *) a;
8708     cell_stats_t *B = (cell_stats_t *) b;
8709 
8710     B->min = PetscMin(A->min,B->min);
8711     B->max = PetscMax(A->max,B->max);
8712     B->sum += A->sum;
8713     B->squaresum += A->squaresum;
8714     B->count += A->count;
8715   }
8716 }
8717 
8718 /*@
8719   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8720 
8721   Collective on dm
8722 
8723   Input Parameters:
8724 + dm        - The DMPlex object
8725 . output    - If true, statistics will be displayed on stdout
8726 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8727 
8728   Notes:
8729   This is mainly intended for debugging/testing purposes.
8730 
8731   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8732 
8733   Level: developer
8734 
8735 .seealso: DMSetFromOptions(), DMPlexComputeOrthogonalQuality()
8736 @*/
8737 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8738 {
8739   DM             dmCoarse;
8740   cell_stats_t   stats, globalStats;
8741   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8742   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8743   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8744   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8745   PetscMPIInt    rank,size;
8746 
8747   PetscFunctionBegin;
8748   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8749   stats.min   = PETSC_MAX_REAL;
8750   stats.max   = PETSC_MIN_REAL;
8751   stats.sum   = stats.squaresum = 0.;
8752   stats.count = 0;
8753 
8754   PetscCallMPI(MPI_Comm_size(comm, &size));
8755   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8756   PetscCall(DMGetCoordinateDim(dm,&cdim));
8757   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8758   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8759   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8760   for (c = cStart; c < cEnd; c++) {
8761     PetscInt  i;
8762     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8763 
8764     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8765     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %D is inverted", c);
8766     for (i = 0; i < PetscSqr(cdim); ++i) {
8767       frobJ    += J[i] * J[i];
8768       frobInvJ += invJ[i] * invJ[i];
8769     }
8770     cond2 = frobJ * frobInvJ;
8771     cond  = PetscSqrtReal(cond2);
8772 
8773     stats.min        = PetscMin(stats.min,cond);
8774     stats.max        = PetscMax(stats.max,cond);
8775     stats.sum       += cond;
8776     stats.squaresum += cond2;
8777     stats.count++;
8778     if (output && cond > limit) {
8779       PetscSection coordSection;
8780       Vec          coordsLocal;
8781       PetscScalar *coords = NULL;
8782       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8783 
8784       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8785       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8786       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8787       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %D cond %g\n", rank, c, (double) cond));
8788       for (i = 0; i < Nv/cdim; ++i) {
8789         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %D: (", i));
8790         for (d = 0; d < cdim; ++d) {
8791           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8792           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8793         }
8794         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8795       }
8796       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8797       for (cl = 0; cl < clSize*2; cl += 2) {
8798         const PetscInt edge = closure[cl];
8799 
8800         if ((edge >= eStart) && (edge < eEnd)) {
8801           PetscReal len;
8802 
8803           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8804           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %D: length %g\n", edge, (double) len));
8805         }
8806       }
8807       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8808       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8809     }
8810   }
8811   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8812 
8813   if (size > 1) {
8814     PetscMPIInt   blockLengths[2] = {4,1};
8815     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8816     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8817     MPI_Op        statReduce;
8818 
8819     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8820     PetscCallMPI(MPI_Type_commit(&statType));
8821     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8822     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8823     PetscCallMPI(MPI_Op_free(&statReduce));
8824     PetscCallMPI(MPI_Type_free(&statType));
8825   } else {
8826     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8827   }
8828   if (rank == 0) {
8829     count = globalStats.count;
8830     min   = globalStats.min;
8831     max   = globalStats.max;
8832     mean  = globalStats.sum / globalStats.count;
8833     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8834   }
8835 
8836   if (output) {
8837     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));
8838   }
8839   PetscCall(PetscFree2(J,invJ));
8840 
8841   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8842   if (dmCoarse) {
8843     PetscBool isplex;
8844 
8845     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8846     if (isplex) {
8847       PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8848     }
8849   }
8850   PetscFunctionReturn(0);
8851 }
8852 
8853 /*@
8854   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8855   orthogonal quality below given tolerance.
8856 
8857   Collective on dm
8858 
8859   Input Parameters:
8860 + dm   - The DMPlex object
8861 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8862 - atol - [0, 1] Absolute tolerance for tagging cells.
8863 
8864   Output Parameters:
8865 + OrthQual      - Vec containing orthogonal quality per cell
8866 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8867 
8868   Options Database Keys:
8869 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8870 supported.
8871 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8872 
8873   Notes:
8874   Orthogonal quality is given by the following formula:
8875 
8876   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8877 
8878   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
8879   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8880   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8881   calculating the cosine of the angle between these vectors.
8882 
8883   Orthogonal quality ranges from 1 (best) to 0 (worst).
8884 
8885   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8886   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8887 
8888   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8889 
8890   Level: intermediate
8891 
8892 .seealso: DMPlexCheckCellShape(), DMCreateLabel()
8893 @*/
8894 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8895 {
8896   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8897   PetscInt                *idx;
8898   PetscScalar             *oqVals;
8899   const PetscScalar       *cellGeomArr, *faceGeomArr;
8900   PetscReal               *ci, *fi, *Ai;
8901   MPI_Comm                comm;
8902   Vec                     cellgeom, facegeom;
8903   DM                      dmFace, dmCell;
8904   IS                      glob;
8905   ISLocalToGlobalMapping  ltog;
8906   PetscViewer             vwr;
8907 
8908   PetscFunctionBegin;
8909   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8910   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
8911   PetscValidPointer(OrthQual, 4);
8912   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
8913   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
8914   PetscCall(DMGetDimension(dm, &nc));
8915   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %D)", nc);
8916   {
8917     DMPlexInterpolatedFlag interpFlag;
8918 
8919     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
8920     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
8921       PetscMPIInt rank;
8922 
8923       PetscCallMPI(MPI_Comm_rank(comm, &rank));
8924       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
8925     }
8926   }
8927   if (OrthQualLabel) {
8928     PetscValidPointer(OrthQualLabel, 5);
8929     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
8930     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
8931   } else {*OrthQualLabel = NULL;}
8932   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8933   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8934   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
8935   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
8936   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
8937   PetscCall(VecCreate(comm, OrthQual));
8938   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
8939   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
8940   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
8941   PetscCall(VecSetUp(*OrthQual));
8942   PetscCall(ISDestroy(&glob));
8943   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
8944   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
8945   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
8946   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
8947   PetscCall(VecGetDM(cellgeom, &dmCell));
8948   PetscCall(VecGetDM(facegeom, &dmFace));
8949   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
8950   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
8951     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
8952     PetscInt           cellarr[2], *adj = NULL;
8953     PetscScalar        *cArr, *fArr;
8954     PetscReal          minvalc = 1.0, minvalf = 1.0;
8955     PetscFVCellGeom    *cg;
8956 
8957     idx[cellIter] = cell-cStart;
8958     cellarr[0] = cell;
8959     /* Make indexing into cellGeom easier */
8960     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
8961     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
8962     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
8963     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
8964     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
8965       PetscInt         i;
8966       const PetscInt   neigh = adj[cellneigh];
8967       PetscReal        normci = 0, normfi = 0, normai = 0;
8968       PetscFVCellGeom  *cgneigh;
8969       PetscFVFaceGeom  *fg;
8970 
8971       /* Don't count ourselves in the neighbor list */
8972       if (neigh == cell) continue;
8973       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
8974       cellarr[1] = neigh;
8975       {
8976         PetscInt       numcovpts;
8977         const PetscInt *covpts;
8978 
8979         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
8980         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
8981         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
8982       }
8983 
8984       /* Compute c_i, f_i and their norms */
8985       for (i = 0; i < nc; i++) {
8986         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
8987         fi[i] = fg->centroid[i] - cg->centroid[i];
8988         Ai[i] = fg->normal[i];
8989         normci += PetscPowReal(ci[i], 2);
8990         normfi += PetscPowReal(fi[i], 2);
8991         normai += PetscPowReal(Ai[i], 2);
8992       }
8993       normci = PetscSqrtReal(normci);
8994       normfi = PetscSqrtReal(normfi);
8995       normai = PetscSqrtReal(normai);
8996 
8997       /* Normalize and compute for each face-cell-normal pair */
8998       for (i = 0; i < nc; i++) {
8999         ci[i] = ci[i]/normci;
9000         fi[i] = fi[i]/normfi;
9001         Ai[i] = Ai[i]/normai;
9002         /* PetscAbs because I don't know if normals are guaranteed to point out */
9003         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9004         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9005       }
9006       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9007         minvalc = PetscRealPart(cArr[cellneighiter]);
9008       }
9009       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9010         minvalf = PetscRealPart(fArr[cellneighiter]);
9011       }
9012     }
9013     PetscCall(PetscFree(adj));
9014     PetscCall(PetscFree2(cArr, fArr));
9015     /* Defer to cell if they're equal */
9016     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9017     if (OrthQualLabel) {
9018       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9019     }
9020   }
9021   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9022   PetscCall(VecAssemblyBegin(*OrthQual));
9023   PetscCall(VecAssemblyEnd(*OrthQual));
9024   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9025   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9026   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9027   if (OrthQualLabel) {
9028     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9029   }
9030   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9031   PetscCall(PetscViewerDestroy(&vwr));
9032   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9033   PetscFunctionReturn(0);
9034 }
9035 
9036 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9037  * interpolator construction */
9038 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9039 {
9040   PetscSection   section, newSection, gsection;
9041   PetscSF        sf;
9042   PetscBool      hasConstraints, ghasConstraints;
9043 
9044   PetscFunctionBegin;
9045   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9046   PetscValidPointer(odm,2);
9047   PetscCall(DMGetLocalSection(dm, &section));
9048   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9049   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9050   if (!ghasConstraints) {
9051     PetscCall(PetscObjectReference((PetscObject)dm));
9052     *odm = dm;
9053     PetscFunctionReturn(0);
9054   }
9055   PetscCall(DMClone(dm, odm));
9056   PetscCall(DMCopyFields(dm, *odm));
9057   PetscCall(DMGetLocalSection(*odm, &newSection));
9058   PetscCall(DMGetPointSF(*odm, &sf));
9059   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9060   PetscCall(DMSetGlobalSection(*odm, gsection));
9061   PetscCall(PetscSectionDestroy(&gsection));
9062   PetscFunctionReturn(0);
9063 }
9064 
9065 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9066 {
9067   DM             dmco, dmfo;
9068   Mat            interpo;
9069   Vec            rscale;
9070   Vec            cglobalo, clocal;
9071   Vec            fglobal, fglobalo, flocal;
9072   PetscBool      regular;
9073 
9074   PetscFunctionBegin;
9075   PetscCall(DMGetFullDM(dmc, &dmco));
9076   PetscCall(DMGetFullDM(dmf, &dmfo));
9077   PetscCall(DMSetCoarseDM(dmfo, dmco));
9078   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9079   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9080   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9081   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9082   PetscCall(DMCreateLocalVector(dmc, &clocal));
9083   PetscCall(VecSet(cglobalo, 0.));
9084   PetscCall(VecSet(clocal, 0.));
9085   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9086   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9087   PetscCall(DMCreateLocalVector(dmf, &flocal));
9088   PetscCall(VecSet(fglobal, 0.));
9089   PetscCall(VecSet(fglobalo, 0.));
9090   PetscCall(VecSet(flocal, 0.));
9091   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9092   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9093   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9094   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9095   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9096   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9097   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9098   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9099   *shift = fglobal;
9100   PetscCall(VecDestroy(&flocal));
9101   PetscCall(VecDestroy(&fglobalo));
9102   PetscCall(VecDestroy(&clocal));
9103   PetscCall(VecDestroy(&cglobalo));
9104   PetscCall(VecDestroy(&rscale));
9105   PetscCall(MatDestroy(&interpo));
9106   PetscCall(DMDestroy(&dmfo));
9107   PetscCall(DMDestroy(&dmco));
9108   PetscFunctionReturn(0);
9109 }
9110 
9111 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9112 {
9113   PetscObject    shifto;
9114   Vec            shift;
9115 
9116   PetscFunctionBegin;
9117   if (!interp) {
9118     Vec rscale;
9119 
9120     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9121     PetscCall(VecDestroy(&rscale));
9122   } else {
9123     PetscCall(PetscObjectReference((PetscObject)interp));
9124   }
9125   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9126   if (!shifto) {
9127     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9128     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9129     shifto = (PetscObject) shift;
9130     PetscCall(VecDestroy(&shift));
9131   }
9132   shift = (Vec) shifto;
9133   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9134   PetscCall(VecAXPY(fineSol, 1.0, shift));
9135   PetscCall(MatDestroy(&interp));
9136   PetscFunctionReturn(0);
9137 }
9138 
9139 /* Pointwise interpolation
9140      Just code FEM for now
9141      u^f = I u^c
9142      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9143      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9144      I_{ij} = psi^f_i phi^c_j
9145 */
9146 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9147 {
9148   PetscSection   gsc, gsf;
9149   PetscInt       m, n;
9150   void          *ctx;
9151   DM             cdm;
9152   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9153 
9154   PetscFunctionBegin;
9155   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9156   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9157   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9158   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9159 
9160   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9161   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9162   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9163   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9164   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9165 
9166   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9167   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9168   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9169   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9170   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9171   if (scaling) {
9172     /* Use naive scaling */
9173     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9174   }
9175   PetscFunctionReturn(0);
9176 }
9177 
9178 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9179 {
9180   VecScatter     ctx;
9181 
9182   PetscFunctionBegin;
9183   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9184   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9185   PetscCall(VecScatterDestroy(&ctx));
9186   PetscFunctionReturn(0);
9187 }
9188 
9189 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9190                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9191                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9192                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9193 {
9194   const PetscInt Nc = uOff[1] - uOff[0];
9195   PetscInt       c;
9196   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9197 }
9198 
9199 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9200 {
9201   DM             dmc;
9202   PetscDS        ds;
9203   Vec            ones, locmass;
9204   IS             cellIS;
9205   PetscFormKey   key;
9206   PetscInt       depth;
9207 
9208   PetscFunctionBegin;
9209   PetscCall(DMClone(dm, &dmc));
9210   PetscCall(DMCopyDisc(dm, dmc));
9211   PetscCall(DMGetDS(dmc, &ds));
9212   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9213   PetscCall(DMCreateGlobalVector(dmc, mass));
9214   PetscCall(DMGetLocalVector(dmc, &ones));
9215   PetscCall(DMGetLocalVector(dmc, &locmass));
9216   PetscCall(DMPlexGetDepth(dmc, &depth));
9217   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9218   PetscCall(VecSet(locmass, 0.0));
9219   PetscCall(VecSet(ones, 1.0));
9220   key.label = NULL;
9221   key.value = 0;
9222   key.field = 0;
9223   key.part  = 0;
9224   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9225   PetscCall(ISDestroy(&cellIS));
9226   PetscCall(VecSet(*mass, 0.0));
9227   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9228   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9229   PetscCall(DMRestoreLocalVector(dmc, &ones));
9230   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9231   PetscCall(DMDestroy(&dmc));
9232   PetscFunctionReturn(0);
9233 }
9234 
9235 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9236 {
9237   PetscSection   gsc, gsf;
9238   PetscInt       m, n;
9239   void          *ctx;
9240   DM             cdm;
9241   PetscBool      regular;
9242 
9243   PetscFunctionBegin;
9244   if (dmFine == dmCoarse) {
9245     DM            dmc;
9246     PetscDS       ds;
9247     PetscWeakForm wf;
9248     Vec           u;
9249     IS            cellIS;
9250     PetscFormKey  key;
9251     PetscInt      depth;
9252 
9253     PetscCall(DMClone(dmFine, &dmc));
9254     PetscCall(DMCopyDisc(dmFine, dmc));
9255     PetscCall(DMGetDS(dmc, &ds));
9256     PetscCall(PetscDSGetWeakForm(ds, &wf));
9257     PetscCall(PetscWeakFormClear(wf));
9258     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9259     PetscCall(DMCreateMatrix(dmc, mass));
9260     PetscCall(DMGetGlobalVector(dmc, &u));
9261     PetscCall(DMPlexGetDepth(dmc, &depth));
9262     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9263     PetscCall(MatZeroEntries(*mass));
9264     key.label = NULL;
9265     key.value = 0;
9266     key.field = 0;
9267     key.part  = 0;
9268     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9269     PetscCall(ISDestroy(&cellIS));
9270     PetscCall(DMRestoreGlobalVector(dmc, &u));
9271     PetscCall(DMDestroy(&dmc));
9272   } else {
9273     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9274     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9275     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9276     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9277 
9278     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9279     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9280     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9281     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9282 
9283     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9284     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9285     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9286     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9287   }
9288   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9289   PetscFunctionReturn(0);
9290 }
9291 
9292 /*@
9293   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9294 
9295   Input Parameter:
9296 . dm - The DMPlex object
9297 
9298   Output Parameter:
9299 . regular - The flag
9300 
9301   Level: intermediate
9302 
9303 .seealso: DMPlexSetRegularRefinement()
9304 @*/
9305 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9306 {
9307   PetscFunctionBegin;
9308   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9309   PetscValidBoolPointer(regular, 2);
9310   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9311   PetscFunctionReturn(0);
9312 }
9313 
9314 /*@
9315   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9316 
9317   Input Parameters:
9318 + dm - The DMPlex object
9319 - regular - The flag
9320 
9321   Level: intermediate
9322 
9323 .seealso: DMPlexGetRegularRefinement()
9324 @*/
9325 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9326 {
9327   PetscFunctionBegin;
9328   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9329   ((DM_Plex *) dm->data)->regularRefinement = regular;
9330   PetscFunctionReturn(0);
9331 }
9332 
9333 /* anchors */
9334 /*@
9335   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9336   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9337 
9338   not collective
9339 
9340   Input Parameter:
9341 . dm - The DMPlex object
9342 
9343   Output Parameters:
9344 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9345 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9346 
9347   Level: intermediate
9348 
9349 .seealso: DMPlexSetAnchors(), DMGetDefaultConstraints(), DMSetDefaultConstraints()
9350 @*/
9351 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9352 {
9353   DM_Plex *plex = (DM_Plex *)dm->data;
9354 
9355   PetscFunctionBegin;
9356   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9357   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9358   if (anchorSection) *anchorSection = plex->anchorSection;
9359   if (anchorIS) *anchorIS = plex->anchorIS;
9360   PetscFunctionReturn(0);
9361 }
9362 
9363 /*@
9364   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9365   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9366   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9367 
9368   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9369   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9370 
9371   collective on dm
9372 
9373   Input Parameters:
9374 + dm - The DMPlex object
9375 . 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).
9376 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9377 
9378   The reference counts of anchorSection and anchorIS are incremented.
9379 
9380   Level: intermediate
9381 
9382 .seealso: DMPlexGetAnchors(), DMGetDefaultConstraints(), DMSetDefaultConstraints()
9383 @*/
9384 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9385 {
9386   DM_Plex        *plex = (DM_Plex *)dm->data;
9387   PetscMPIInt    result;
9388 
9389   PetscFunctionBegin;
9390   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9391   if (anchorSection) {
9392     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9393     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9394     PetscCheckFalse(result != MPI_CONGRUENT && result != MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9395   }
9396   if (anchorIS) {
9397     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9398     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9399     PetscCheckFalse(result != MPI_CONGRUENT && result != MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9400   }
9401 
9402   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9403   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9404   plex->anchorSection = anchorSection;
9405 
9406   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9407   PetscCall(ISDestroy(&plex->anchorIS));
9408   plex->anchorIS = anchorIS;
9409 
9410   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9411     PetscInt size, a, pStart, pEnd;
9412     const PetscInt *anchors;
9413 
9414     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9415     PetscCall(ISGetLocalSize(anchorIS,&size));
9416     PetscCall(ISGetIndices(anchorIS,&anchors));
9417     for (a = 0; a < size; a++) {
9418       PetscInt p;
9419 
9420       p = anchors[a];
9421       if (p >= pStart && p < pEnd) {
9422         PetscInt dof;
9423 
9424         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9425         if (dof) {
9426 
9427           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9428           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %D cannot be constrained and an anchor",p);
9429         }
9430       }
9431     }
9432     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9433   }
9434   /* reset the generic constraints */
9435   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9436   PetscFunctionReturn(0);
9437 }
9438 
9439 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9440 {
9441   PetscSection anchorSection;
9442   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9443 
9444   PetscFunctionBegin;
9445   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9446   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9447   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9448   PetscCall(PetscSectionGetNumFields(section,&numFields));
9449   if (numFields) {
9450     PetscInt f;
9451     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9452 
9453     for (f = 0; f < numFields; f++) {
9454       PetscInt numComp;
9455 
9456       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9457       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9458     }
9459   }
9460   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9461   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9462   pStart = PetscMax(pStart,sStart);
9463   pEnd   = PetscMin(pEnd,sEnd);
9464   pEnd   = PetscMax(pStart,pEnd);
9465   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9466   for (p = pStart; p < pEnd; p++) {
9467     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9468     if (dof) {
9469       PetscCall(PetscSectionGetDof(section,p,&dof));
9470       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9471       for (f = 0; f < numFields; f++) {
9472         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9473         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9474       }
9475     }
9476   }
9477   PetscCall(PetscSectionSetUp(*cSec));
9478   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9479   PetscFunctionReturn(0);
9480 }
9481 
9482 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9483 {
9484   PetscSection   aSec;
9485   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9486   const PetscInt *anchors;
9487   PetscInt       numFields, f;
9488   IS             aIS;
9489   MatType        mtype;
9490   PetscBool      iscuda,iskokkos;
9491 
9492   PetscFunctionBegin;
9493   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9494   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9495   PetscCall(PetscSectionGetStorageSize(section, &n));
9496   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9497   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9498   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9499   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9500   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9501   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9502   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9503   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9504   else mtype = MATSEQAIJ;
9505   PetscCall(MatSetType(*cMat,mtype));
9506   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9507   PetscCall(ISGetIndices(aIS,&anchors));
9508   /* cSec will be a subset of aSec and section */
9509   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9510   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9511   PetscCall(PetscMalloc1(m+1,&i));
9512   i[0] = 0;
9513   PetscCall(PetscSectionGetNumFields(section,&numFields));
9514   for (p = pStart; p < pEnd; p++) {
9515     PetscInt rDof, rOff, r;
9516 
9517     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9518     if (!rDof) continue;
9519     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9520     if (numFields) {
9521       for (f = 0; f < numFields; f++) {
9522         annz = 0;
9523         for (r = 0; r < rDof; r++) {
9524           a = anchors[rOff + r];
9525           if (a < sStart || a >= sEnd) continue;
9526           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9527           annz += aDof;
9528         }
9529         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9530         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9531         for (q = 0; q < dof; q++) {
9532           i[off + q + 1] = i[off + q] + annz;
9533         }
9534       }
9535     } else {
9536       annz = 0;
9537       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9538       for (q = 0; q < dof; q++) {
9539         a = anchors[rOff + q];
9540         if (a < sStart || a >= sEnd) continue;
9541         PetscCall(PetscSectionGetDof(section,a,&aDof));
9542         annz += aDof;
9543       }
9544       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9545       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9546       for (q = 0; q < dof; q++) {
9547         i[off + q + 1] = i[off + q] + annz;
9548       }
9549     }
9550   }
9551   nnz = i[m];
9552   PetscCall(PetscMalloc1(nnz,&j));
9553   offset = 0;
9554   for (p = pStart; p < pEnd; p++) {
9555     if (numFields) {
9556       for (f = 0; f < numFields; f++) {
9557         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9558         for (q = 0; q < dof; q++) {
9559           PetscInt rDof, rOff, r;
9560           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9561           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9562           for (r = 0; r < rDof; r++) {
9563             PetscInt s;
9564 
9565             a = anchors[rOff + r];
9566             if (a < sStart || a >= sEnd) continue;
9567             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9568             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9569             for (s = 0; s < aDof; s++) {
9570               j[offset++] = aOff + s;
9571             }
9572           }
9573         }
9574       }
9575     } else {
9576       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9577       for (q = 0; q < dof; q++) {
9578         PetscInt rDof, rOff, r;
9579         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9580         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9581         for (r = 0; r < rDof; r++) {
9582           PetscInt s;
9583 
9584           a = anchors[rOff + r];
9585           if (a < sStart || a >= sEnd) continue;
9586           PetscCall(PetscSectionGetDof(section,a,&aDof));
9587           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9588           for (s = 0; s < aDof; s++) {
9589             j[offset++] = aOff + s;
9590           }
9591         }
9592       }
9593     }
9594   }
9595   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9596   PetscCall(PetscFree(i));
9597   PetscCall(PetscFree(j));
9598   PetscCall(ISRestoreIndices(aIS,&anchors));
9599   PetscFunctionReturn(0);
9600 }
9601 
9602 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9603 {
9604   DM_Plex        *plex = (DM_Plex *)dm->data;
9605   PetscSection   anchorSection, section, cSec;
9606   Mat            cMat;
9607 
9608   PetscFunctionBegin;
9609   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9610   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9611   if (anchorSection) {
9612     PetscInt Nf;
9613 
9614     PetscCall(DMGetLocalSection(dm,&section));
9615     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9616     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9617     PetscCall(DMGetNumFields(dm,&Nf));
9618     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9619     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9620     PetscCall(PetscSectionDestroy(&cSec));
9621     PetscCall(MatDestroy(&cMat));
9622   }
9623   PetscFunctionReturn(0);
9624 }
9625 
9626 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9627 {
9628   IS             subis;
9629   PetscSection   section, subsection;
9630 
9631   PetscFunctionBegin;
9632   PetscCall(DMGetLocalSection(dm, &section));
9633   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9634   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9635   /* Create subdomain */
9636   PetscCall(DMPlexFilter(dm, label, value, subdm));
9637   /* Create submodel */
9638   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9639   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9640   PetscCall(DMSetLocalSection(*subdm, subsection));
9641   PetscCall(PetscSectionDestroy(&subsection));
9642   PetscCall(DMCopyDisc(dm, *subdm));
9643   /* Create map from submodel to global model */
9644   if (is) {
9645     PetscSection    sectionGlobal, subsectionGlobal;
9646     IS              spIS;
9647     const PetscInt *spmap;
9648     PetscInt       *subIndices;
9649     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9650     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9651 
9652     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9653     PetscCall(ISGetIndices(spIS, &spmap));
9654     PetscCall(PetscSectionGetNumFields(section, &Nf));
9655     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9656     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9657     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9658     for (p = pStart; p < pEnd; ++p) {
9659       PetscInt gdof, pSubSize  = 0;
9660 
9661       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9662       if (gdof > 0) {
9663         for (f = 0; f < Nf; ++f) {
9664           PetscInt fdof, fcdof;
9665 
9666           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9667           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9668           pSubSize += fdof-fcdof;
9669         }
9670         subSize += pSubSize;
9671         if (pSubSize) {
9672           if (bs < 0) {
9673             bs = pSubSize;
9674           } else if (bs != pSubSize) {
9675             /* Layout does not admit a pointwise block size */
9676             bs = 1;
9677           }
9678         }
9679       }
9680     }
9681     /* Must have same blocksize on all procs (some might have no points) */
9682     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9683     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9684     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9685     else                            {bs = bsMinMax[0];}
9686     PetscCall(PetscMalloc1(subSize, &subIndices));
9687     for (p = pStart; p < pEnd; ++p) {
9688       PetscInt gdof, goff;
9689 
9690       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9691       if (gdof > 0) {
9692         const PetscInt point = spmap[p];
9693 
9694         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9695         for (f = 0; f < Nf; ++f) {
9696           PetscInt fdof, fcdof, fc, f2, poff = 0;
9697 
9698           /* Can get rid of this loop by storing field information in the global section */
9699           for (f2 = 0; f2 < f; ++f2) {
9700             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9701             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9702             poff += fdof-fcdof;
9703           }
9704           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9705           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9706           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9707             subIndices[subOff] = goff+poff+fc;
9708           }
9709         }
9710       }
9711     }
9712     PetscCall(ISRestoreIndices(spIS, &spmap));
9713     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9714     if (bs > 1) {
9715       /* We need to check that the block size does not come from non-contiguous fields */
9716       PetscInt i, j, set = 1;
9717       for (i = 0; i < subSize; i += bs) {
9718         for (j = 0; j < bs; ++j) {
9719           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9720         }
9721       }
9722       if (set) PetscCall(ISSetBlockSize(*is, bs));
9723     }
9724     /* Attach nullspace */
9725     for (f = 0; f < Nf; ++f) {
9726       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9727       if ((*subdm)->nullspaceConstructors[f]) break;
9728     }
9729     if (f < Nf) {
9730       MatNullSpace nullSpace;
9731       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9732 
9733       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9734       PetscCall(MatNullSpaceDestroy(&nullSpace));
9735     }
9736   }
9737   PetscFunctionReturn(0);
9738 }
9739 
9740 /*@
9741   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9742 
9743   Input Parameter:
9744 - dm - The DM
9745 
9746   Level: developer
9747 
9748   Options Database Keys:
9749 . -dm_plex_monitor_throughput - Activate the monitor
9750 
9751 .seealso: DMSetFromOptions(), DMPlexCreate()
9752 @*/
9753 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9754 {
9755 #if defined(PETSC_USE_LOG)
9756   PetscStageLog      stageLog;
9757   PetscLogEvent      event;
9758   PetscLogStage      stage;
9759   PetscEventPerfInfo eventInfo;
9760   PetscReal          cellRate, flopRate;
9761   PetscInt           cStart, cEnd, Nf, N;
9762   const char        *name;
9763 #endif
9764 
9765   PetscFunctionBegin;
9766   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9767 #if defined(PETSC_USE_LOG)
9768   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9769   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9770   PetscCall(DMGetNumFields(dm, &Nf));
9771   PetscCall(PetscLogGetStageLog(&stageLog));
9772   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9773   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9774   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9775   N        = (cEnd - cStart)*Nf*eventInfo.count;
9776   flopRate = eventInfo.flops/eventInfo.time;
9777   cellRate = N/eventInfo.time;
9778   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)));
9779 #else
9780   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9781 #endif
9782   PetscFunctionReturn(0);
9783 }
9784