xref: /petsc/src/dm/impls/plex/plex.c (revision 6bc1bd014737fb62d674de59be60f34b0220da5d)
1 #include <petsc/private/dmpleximpl.h>   /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/isimpl.h>
3 #include <petsc/private/vecimpl.h>
4 #include <petsc/private/glvisvecimpl.h>
5 #include <petscsf.h>
6 #include <petscds.h>
7 #include <petscdraw.h>
8 #include <petscdmfield.h>
9 #include <petscdmplextransform.h>
10 
11 /* Logging support */
12 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF,DMPLEX_LocatePoints,DMPLEX_TopologyView,DMPLEX_LabelsView,DMPLEX_CoordinatesView,DMPLEX_SectionView,DMPLEX_GlobalVectorView,DMPLEX_LocalVectorView,DMPLEX_TopologyLoad,DMPLEX_LabelsLoad,DMPLEX_CoordinatesLoad,DMPLEX_SectionLoad,DMPLEX_GlobalVectorLoad,DMPLEX_LocalVectorLoad;
13 
14 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
15 
16 /*@
17   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
18 
19   Input Parameter:
20 . dm      - The DMPlex object
21 
22   Output Parameter:
23 . simplex - Flag checking for a simplex
24 
25   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
26   If the mesh has no cells, this returns PETSC_FALSE.
27 
28   Level: intermediate
29 
30 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
31 @*/
32 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
33 {
34   DMPolytopeType ct;
35   PetscInt       cStart, cEnd;
36 
37   PetscFunctionBegin;
38   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
39   if (cEnd <= cStart) {*simplex = PETSC_FALSE; PetscFunctionReturn(0);}
40   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
41   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
42   PetscFunctionReturn(0);
43 }
44 
45 /*@
46   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
47 
48   Input Parameters:
49 + dm     - The DMPlex object
50 - height - The cell height in the Plex, 0 is the default
51 
52   Output Parameters:
53 + cStart - The first "normal" cell
54 - cEnd   - The upper bound on "normal"" cells
55 
56   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
57 
58   Level: developer
59 
60 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
61 @*/
62 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
63 {
64   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
65   PetscInt       cS, cE, c;
66 
67   PetscFunctionBegin;
68   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
69   for (c = cS; c < cE; ++c) {
70     DMPolytopeType cct;
71 
72     PetscCall(DMPlexGetCellType(dm, c, &cct));
73     if ((PetscInt) cct < 0) break;
74     switch (cct) {
75       case DM_POLYTOPE_POINT:
76       case DM_POLYTOPE_SEGMENT:
77       case DM_POLYTOPE_TRIANGLE:
78       case DM_POLYTOPE_QUADRILATERAL:
79       case DM_POLYTOPE_TETRAHEDRON:
80       case DM_POLYTOPE_HEXAHEDRON:
81         ct = cct;
82         break;
83       default: break;
84     }
85     if (ct != DM_POLYTOPE_UNKNOWN) break;
86   }
87   if (ct != DM_POLYTOPE_UNKNOWN) {
88     DMLabel ctLabel;
89 
90     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
91     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
92   }
93   if (cStart) *cStart = cS;
94   if (cEnd)   *cEnd   = cE;
95   PetscFunctionReturn(0);
96 }
97 
98 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
99 {
100   PetscInt       cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
101   PetscInt       vcdof[2] = {0,0}, globalvcdof[2];
102 
103   PetscFunctionBegin;
104   *ft  = PETSC_VTK_INVALID;
105   PetscCall(DMGetCoordinateDim(dm, &cdim));
106   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
107   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
108   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
109   if (field >= 0) {
110     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
111     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
112   } else {
113     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
114     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
115   }
116   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
117   if (globalvcdof[0]) {
118     *sStart = vStart;
119     *sEnd   = vEnd;
120     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
121     else                        *ft = PETSC_VTK_POINT_FIELD;
122   } else if (globalvcdof[1]) {
123     *sStart = cStart;
124     *sEnd   = cEnd;
125     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
126     else                        *ft = PETSC_VTK_CELL_FIELD;
127   } else {
128     if (field >= 0) {
129       const char *fieldname;
130 
131       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
132       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
133     } else {
134       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section\n"));
135     }
136   }
137   PetscFunctionReturn(0);
138 }
139 
140 /*@
141   DMPlexVecView1D - Plot many 1D solutions on the same line graph
142 
143   Collective on dm
144 
145   Input Parameters:
146 + dm - The DMPlex
147 . n  - The number of vectors
148 . u  - The array of local vectors
149 - viewer - The Draw viewer
150 
151   Level: advanced
152 
153 .seealso: `VecViewFromOptions()`, `VecView()`
154 @*/
155 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
156 {
157   PetscDS            ds;
158   PetscDraw          draw = NULL;
159   PetscDrawLG        lg;
160   Vec                coordinates;
161   const PetscScalar *coords, **sol;
162   PetscReal         *vals;
163   PetscInt          *Nc;
164   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
165   char             **names;
166 
167   PetscFunctionBegin;
168   PetscCall(DMGetDS(dm, &ds));
169   PetscCall(PetscDSGetNumFields(ds, &Nf));
170   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
171   PetscCall(PetscDSGetComponents(ds, &Nc));
172 
173   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
174   if (!draw) PetscFunctionReturn(0);
175   PetscCall(PetscDrawLGCreate(draw, n*Nl, &lg));
176 
177   PetscCall(PetscMalloc3(n, &sol, n*Nl, &names, n*Nl, &vals));
178   for (i = 0, l = 0; i < n; ++i) {
179     const char *vname;
180 
181     PetscCall(PetscObjectGetName((PetscObject) u[i], &vname));
182     for (f = 0; f < Nf; ++f) {
183       PetscObject disc;
184       const char *fname;
185       char        tmpname[PETSC_MAX_PATH_LEN];
186 
187       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
188       /* TODO Create names for components */
189       for (c = 0; c < Nc[f]; ++c, ++l) {
190         PetscCall(PetscObjectGetName(disc, &fname));
191         PetscCall(PetscStrcpy(tmpname, vname));
192         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
193         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
194         PetscCall(PetscStrallocpy(tmpname, &names[l]));
195       }
196     }
197   }
198   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *) names));
199   /* Just add P_1 support for now */
200   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
201   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
202   PetscCall(VecGetArrayRead(coordinates, &coords));
203   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
204   for (v = vStart; v < vEnd; ++v) {
205     PetscScalar *x, *svals;
206 
207     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
208     for (i = 0; i < n; ++i) {
209       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
210       for (l = 0; l < Nl; ++l) vals[i*Nl + l] = PetscRealPart(svals[l]);
211     }
212     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
213   }
214   PetscCall(VecRestoreArrayRead(coordinates, &coords));
215   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
216   for (l = 0; l < n*Nl; ++l) PetscCall(PetscFree(names[l]));
217   PetscCall(PetscFree3(sol, names, vals));
218 
219   PetscCall(PetscDrawLGDraw(lg));
220   PetscCall(PetscDrawLGDestroy(&lg));
221   PetscFunctionReturn(0);
222 }
223 
224 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
225 {
226   DM             dm;
227 
228   PetscFunctionBegin;
229   PetscCall(VecGetDM(u, &dm));
230   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
231   PetscFunctionReturn(0);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
235 {
236   DM                 dm;
237   PetscSection       s;
238   PetscDraw          draw, popup;
239   DM                 cdm;
240   PetscSection       coordSection;
241   Vec                coordinates;
242   const PetscScalar *coords, *array;
243   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
244   PetscReal          vbound[2], time;
245   PetscBool          flg;
246   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
247   const char        *name;
248   char               title[PETSC_MAX_PATH_LEN];
249 
250   PetscFunctionBegin;
251   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
252   PetscCall(VecGetDM(v, &dm));
253   PetscCall(DMGetCoordinateDim(dm, &dim));
254   PetscCall(DMGetLocalSection(dm, &s));
255   PetscCall(PetscSectionGetNumFields(s, &Nf));
256   PetscCall(DMGetCoarsenLevel(dm, &level));
257   PetscCall(DMGetCoordinateDM(dm, &cdm));
258   PetscCall(DMGetLocalSection(cdm, &coordSection));
259   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
260   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
261   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
262 
263   PetscCall(PetscObjectGetName((PetscObject) v, &name));
264   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
265 
266   PetscCall(VecGetLocalSize(coordinates, &N));
267   PetscCall(VecGetArrayRead(coordinates, &coords));
268   for (c = 0; c < N; c += dim) {
269     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
270     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
271   }
272   PetscCall(VecRestoreArrayRead(coordinates, &coords));
273   PetscCall(PetscDrawClear(draw));
274 
275   /* Could implement something like DMDASelectFields() */
276   for (f = 0; f < Nf; ++f) {
277     DM   fdm = dm;
278     Vec  fv  = v;
279     IS   fis;
280     char prefix[PETSC_MAX_PATH_LEN];
281     const char *fname;
282 
283     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
284     PetscCall(PetscSectionGetFieldName(s, f, &fname));
285 
286     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix,sizeof(prefix)));
287     else               {prefix[0] = '\0';}
288     if (Nf > 1) {
289       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
290       PetscCall(VecGetSubVector(v, fis, &fv));
291       PetscCall(PetscStrlcat(prefix, fname,sizeof(prefix)));
292       PetscCall(PetscStrlcat(prefix, "_",sizeof(prefix)));
293     }
294     for (comp = 0; comp < Nc; ++comp, ++w) {
295       PetscInt nmax = 2;
296 
297       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
298       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
299       else        PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
300       PetscCall(PetscDrawSetTitle(draw, title));
301 
302       /* TODO Get max and min only for this component */
303       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
304       if (!flg) {
305         PetscCall(VecMin(fv, NULL, &vbound[0]));
306         PetscCall(VecMax(fv, NULL, &vbound[1]));
307         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
308       }
309       PetscCall(PetscDrawGetPopup(draw, &popup));
310       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
311       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
312 
313       PetscCall(VecGetArrayRead(fv, &array));
314       for (c = cStart; c < cEnd; ++c) {
315         PetscScalar *coords = NULL, *a = NULL;
316         PetscInt     numCoords, color[4] = {-1,-1,-1,-1};
317 
318         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
319         if (a) {
320           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
321           color[1] = color[2] = color[3] = color[0];
322         } else {
323           PetscScalar *vals = NULL;
324           PetscInt     numVals, va;
325 
326           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
327           PetscCheck(numVals % Nc == 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
328           switch (numVals/Nc) {
329           case 3: /* P1 Triangle */
330           case 4: /* P1 Quadrangle */
331             for (va = 0; va < numVals/Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp]), vbound[0], vbound[1]);
332             break;
333           case 6: /* P2 Triangle */
334           case 8: /* P2 Quadrangle */
335             for (va = 0; va < numVals/(Nc*2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp + numVals/(Nc*2)]), vbound[0], vbound[1]);
336             break;
337           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals/Nc);
338           }
339           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
340         }
341         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
342         switch (numCoords) {
343         case 6:
344         case 12: /* Localized triangle */
345           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
346           break;
347         case 8:
348         case 16: /* Localized quadrilateral */
349           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
350           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
351           break;
352         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
353         }
354         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
355       }
356       PetscCall(VecRestoreArrayRead(fv, &array));
357       PetscCall(PetscDrawFlush(draw));
358       PetscCall(PetscDrawPause(draw));
359       PetscCall(PetscDrawSave(draw));
360     }
361     if (Nf > 1) {
362       PetscCall(VecRestoreSubVector(v, fis, &fv));
363       PetscCall(ISDestroy(&fis));
364       PetscCall(DMDestroy(&fdm));
365     }
366   }
367   PetscFunctionReturn(0);
368 }
369 
370 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
371 {
372   DM        dm;
373   PetscDraw draw;
374   PetscInt  dim;
375   PetscBool isnull;
376 
377   PetscFunctionBegin;
378   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
379   PetscCall(PetscDrawIsNull(draw, &isnull));
380   if (isnull) PetscFunctionReturn(0);
381 
382   PetscCall(VecGetDM(v, &dm));
383   PetscCall(DMGetCoordinateDim(dm, &dim));
384   switch (dim) {
385   case 1: PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));break;
386   case 2: PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));break;
387   default: SETERRQ(PetscObjectComm((PetscObject) v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
388   }
389   PetscFunctionReturn(0);
390 }
391 
392 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
393 {
394   DM                      dm;
395   Vec                     locv;
396   const char              *name;
397   PetscSection            section;
398   PetscInt                pStart, pEnd;
399   PetscInt                numFields;
400   PetscViewerVTKFieldType ft;
401 
402   PetscFunctionBegin;
403   PetscCall(VecGetDM(v, &dm));
404   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
405   PetscCall(PetscObjectGetName((PetscObject) v, &name));
406   PetscCall(PetscObjectSetName((PetscObject) locv, name));
407   PetscCall(VecCopy(v, locv));
408   PetscCall(DMGetLocalSection(dm, &section));
409   PetscCall(PetscSectionGetNumFields(section, &numFields));
410   if (!numFields) {
411     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
412     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE,(PetscObject) locv));
413   } else {
414     PetscInt f;
415 
416     for (f = 0; f < numFields; f++) {
417       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
418       if (ft == PETSC_VTK_INVALID) continue;
419       PetscCall(PetscObjectReference((PetscObject)locv));
420       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE,(PetscObject) locv));
421     }
422     PetscCall(VecDestroy(&locv));
423   }
424   PetscFunctionReturn(0);
425 }
426 
427 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
428 {
429   DM             dm;
430   PetscBool      isvtk, ishdf5, isdraw, isglvis;
431 
432   PetscFunctionBegin;
433   PetscCall(VecGetDM(v, &dm));
434   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
435   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk));
436   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5));
437   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw));
438   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis));
439   if (isvtk || ishdf5 || isdraw || isglvis) {
440     PetscInt    i,numFields;
441     PetscObject fe;
442     PetscBool   fem = PETSC_FALSE;
443     Vec         locv = v;
444     const char  *name;
445     PetscInt    step;
446     PetscReal   time;
447 
448     PetscCall(DMGetNumFields(dm, &numFields));
449     for (i=0; i<numFields; i++) {
450       PetscCall(DMGetField(dm, i, NULL, &fe));
451       if (fe->classid == PETSCFE_CLASSID) { fem = PETSC_TRUE; break; }
452     }
453     if (fem) {
454       PetscObject isZero;
455 
456       PetscCall(DMGetLocalVector(dm, &locv));
457       PetscCall(PetscObjectGetName((PetscObject) v, &name));
458       PetscCall(PetscObjectSetName((PetscObject) locv, name));
459       PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
460       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
461       PetscCall(VecCopy(v, locv));
462       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
463       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
464     }
465     if (isvtk) {
466       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
467     } else if (ishdf5) {
468 #if defined(PETSC_HAVE_HDF5)
469       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
470 #else
471       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
472 #endif
473     } else if (isdraw) {
474       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
475     } else if (isglvis) {
476       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
477       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
478       PetscCall(VecView_GLVis(locv, viewer));
479     }
480     if (fem) {
481       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
482       PetscCall(DMRestoreLocalVector(dm, &locv));
483     }
484   } else {
485     PetscBool isseq;
486 
487     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
488     if (isseq) PetscCall(VecView_Seq(v, viewer));
489     else       PetscCall(VecView_MPI(v, viewer));
490   }
491   PetscFunctionReturn(0);
492 }
493 
494 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
495 {
496   DM        dm;
497   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii;
498 
499   PetscFunctionBegin;
500   PetscCall(VecGetDM(v, &dm));
501   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
502   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
503   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
504   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
505   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
506   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
507   if (isvtk || isdraw || isglvis) {
508     Vec         locv;
509     PetscObject isZero;
510     const char *name;
511 
512     PetscCall(DMGetLocalVector(dm, &locv));
513     PetscCall(PetscObjectGetName((PetscObject) v, &name));
514     PetscCall(PetscObjectSetName((PetscObject) locv, name));
515     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
516     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
517     PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
518     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
519     PetscCall(VecView_Plex_Local(locv, viewer));
520     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
521     PetscCall(DMRestoreLocalVector(dm, &locv));
522   } else if (ishdf5) {
523 #if defined(PETSC_HAVE_HDF5)
524     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
525 #else
526     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
527 #endif
528   } else if (isexodusii) {
529 #if defined(PETSC_HAVE_EXODUSII)
530     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
531 #else
532     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
533 #endif
534   } else {
535     PetscBool isseq;
536 
537     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
538     if (isseq) PetscCall(VecView_Seq(v, viewer));
539     else       PetscCall(VecView_MPI(v, viewer));
540   }
541   PetscFunctionReturn(0);
542 }
543 
544 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
545 {
546   DM                dm;
547   MPI_Comm          comm;
548   PetscViewerFormat format;
549   Vec               v;
550   PetscBool         isvtk, ishdf5;
551 
552   PetscFunctionBegin;
553   PetscCall(VecGetDM(originalv, &dm));
554   PetscCall(PetscObjectGetComm((PetscObject) originalv, &comm));
555   PetscCheck(dm,comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
556   PetscCall(PetscViewerGetFormat(viewer, &format));
557   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
558   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,  &isvtk));
559   if (format == PETSC_VIEWER_NATIVE) {
560     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
561     /* this need a better fix */
562     if (dm->useNatural) {
563       if (dm->sfNatural) {
564         const char *vecname;
565         PetscInt    n, nroots;
566 
567         PetscCall(VecGetLocalSize(originalv, &n));
568         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
569         if (n == nroots) {
570           PetscCall(DMGetGlobalVector(dm, &v));
571           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
572           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
573           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
574           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
575         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
576       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
577     } else v = originalv;
578   } else v = originalv;
579 
580   if (ishdf5) {
581 #if defined(PETSC_HAVE_HDF5)
582     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
583 #else
584     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
585 #endif
586   } else if (isvtk) {
587     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
588   } else {
589     PetscBool isseq;
590 
591     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
592     if (isseq) PetscCall(VecView_Seq(v, viewer));
593     else       PetscCall(VecView_MPI(v, viewer));
594   }
595   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
596   PetscFunctionReturn(0);
597 }
598 
599 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
600 {
601   DM             dm;
602   PetscBool      ishdf5;
603 
604   PetscFunctionBegin;
605   PetscCall(VecGetDM(v, &dm));
606   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
607   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
608   if (ishdf5) {
609     DM          dmBC;
610     Vec         gv;
611     const char *name;
612 
613     PetscCall(DMGetOutputDM(dm, &dmBC));
614     PetscCall(DMGetGlobalVector(dmBC, &gv));
615     PetscCall(PetscObjectGetName((PetscObject) v, &name));
616     PetscCall(PetscObjectSetName((PetscObject) gv, name));
617     PetscCall(VecLoad_Default(gv, viewer));
618     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
619     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
620     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
621   } else {
622     PetscCall(VecLoad_Default(v, viewer));
623   }
624   PetscFunctionReturn(0);
625 }
626 
627 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
628 {
629   DM             dm;
630   PetscBool      ishdf5,isexodusii;
631 
632   PetscFunctionBegin;
633   PetscCall(VecGetDM(v, &dm));
634   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
635   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
636   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
637   if (ishdf5) {
638 #if defined(PETSC_HAVE_HDF5)
639     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
640 #else
641     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
642 #endif
643   } else if (isexodusii) {
644 #if defined(PETSC_HAVE_EXODUSII)
645     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
646 #else
647     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
648 #endif
649   } else {
650     PetscCall(VecLoad_Default(v, viewer));
651   }
652   PetscFunctionReturn(0);
653 }
654 
655 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
656 {
657   DM                dm;
658   PetscViewerFormat format;
659   PetscBool         ishdf5;
660 
661   PetscFunctionBegin;
662   PetscCall(VecGetDM(originalv, &dm));
663   PetscCheck(dm,PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
664   PetscCall(PetscViewerGetFormat(viewer, &format));
665   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
666   if (format == PETSC_VIEWER_NATIVE) {
667     if (dm->useNatural) {
668       if (dm->sfNatural) {
669         if (ishdf5) {
670 #if defined(PETSC_HAVE_HDF5)
671           Vec         v;
672           const char *vecname;
673 
674           PetscCall(DMGetGlobalVector(dm, &v));
675           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
676           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
677           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
678           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
679           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
680           PetscCall(DMRestoreGlobalVector(dm, &v));
681 #else
682           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
683 #endif
684         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
685       }
686     } else {
687       PetscCall(VecLoad_Default(originalv, viewer));
688     }
689   }
690   PetscFunctionReturn(0);
691 }
692 
693 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
694 {
695   PetscSection       coordSection;
696   Vec                coordinates;
697   DMLabel            depthLabel, celltypeLabel;
698   const char        *name[4];
699   const PetscScalar *a;
700   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
701 
702   PetscFunctionBegin;
703   PetscCall(DMGetDimension(dm, &dim));
704   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
705   PetscCall(DMGetCoordinateSection(dm, &coordSection));
706   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
707   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
708   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
709   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
710   PetscCall(VecGetArrayRead(coordinates, &a));
711   name[0]     = "vertex";
712   name[1]     = "edge";
713   name[dim-1] = "face";
714   name[dim]   = "cell";
715   for (c = cStart; c < cEnd; ++c) {
716     PetscInt *closure = NULL;
717     PetscInt  closureSize, cl, ct;
718 
719     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
720     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
721     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
722     PetscCall(PetscViewerASCIIPushTab(viewer));
723     for (cl = 0; cl < closureSize*2; cl += 2) {
724       PetscInt point = closure[cl], depth, dof, off, d, p;
725 
726       if ((point < pStart) || (point >= pEnd)) continue;
727       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
728       if (!dof) continue;
729       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
730       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
731       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
732       for (p = 0; p < dof/dim; ++p) {
733         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
734         for (d = 0; d < dim; ++d) {
735           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
736           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d])));
737         }
738         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
739       }
740       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
741     }
742     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
743     PetscCall(PetscViewerASCIIPopTab(viewer));
744   }
745   PetscCall(VecRestoreArrayRead(coordinates, &a));
746   PetscFunctionReturn(0);
747 }
748 
749 typedef enum {CS_CARTESIAN, CS_POLAR, CS_CYLINDRICAL, CS_SPHERICAL} CoordSystem;
750 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
751 
752 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
753 {
754   PetscInt       i;
755 
756   PetscFunctionBegin;
757   if (dim > 3) {
758     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) PetscRealPart(x[i])));
759   } else {
760     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
761 
762     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
763     switch (cs) {
764       case CS_CARTESIAN: for (i = 0; i < dim; ++i) trcoords[i] = coords[i];break;
765       case CS_POLAR:
766         PetscCheck(dim == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
767         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
768         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
769         break;
770       case CS_CYLINDRICAL:
771         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
772         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
773         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
774         trcoords[2] = coords[2];
775         break;
776       case CS_SPHERICAL:
777         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
778         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
779         trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
780         trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
781         break;
782     }
783     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) trcoords[i]));
784   }
785   PetscFunctionReturn(0);
786 }
787 
788 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
789 {
790   DM_Plex          *mesh = (DM_Plex*) dm->data;
791   DM                cdm;
792   PetscSection      coordSection;
793   Vec               coordinates;
794   PetscViewerFormat format;
795 
796   PetscFunctionBegin;
797   PetscCall(DMGetCoordinateDM(dm, &cdm));
798   PetscCall(DMGetLocalSection(cdm, &coordSection));
799   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
800   PetscCall(PetscViewerGetFormat(viewer, &format));
801   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
802     const char *name;
803     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
804     PetscInt    pStart, pEnd, p, numLabels, l;
805     PetscMPIInt rank, size;
806 
807     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
808     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
809     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
810     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
811     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
812     PetscCall(DMGetDimension(dm, &dim));
813     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
814     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
815     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
816     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
817     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
818     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
819     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
820     for (p = pStart; p < pEnd; ++p) {
821       PetscInt dof, off, s;
822 
823       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
824       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
825       for (s = off; s < off+dof; ++s) {
826         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
827       }
828     }
829     PetscCall(PetscViewerFlush(viewer));
830     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
831     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
832     for (p = pStart; p < pEnd; ++p) {
833       PetscInt dof, off, c;
834 
835       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
836       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
837       for (c = off; c < off+dof; ++c) {
838         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
839       }
840     }
841     PetscCall(PetscViewerFlush(viewer));
842     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
843     if (coordSection && coordinates) {
844       CoordSystem        cs = CS_CARTESIAN;
845       const PetscScalar *array;
846       PetscInt           Nf, Nc, pStart, pEnd, p;
847       PetscMPIInt        rank;
848       const char        *name;
849 
850       PetscCall(PetscOptionsGetEnum(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *) &cs, NULL));
851       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
852       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
853       PetscCheck(Nf == 1,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
854       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
855       PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
856       PetscCall(PetscObjectGetName((PetscObject) coordinates, &name));
857       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
858       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
859       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
860 
861       PetscCall(VecGetArrayRead(coordinates, &array));
862       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
863       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
864       for (p = pStart; p < pEnd; ++p) {
865         PetscInt dof, off;
866 
867         PetscCall(PetscSectionGetDof(coordSection, p, &dof));
868         PetscCall(PetscSectionGetOffset(coordSection, p, &off));
869         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
870         PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
871         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
872       }
873       PetscCall(PetscViewerFlush(viewer));
874       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
875       PetscCall(VecRestoreArrayRead(coordinates, &array));
876     }
877     PetscCall(DMGetNumLabels(dm, &numLabels));
878     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
879     for (l = 0; l < numLabels; ++l) {
880       DMLabel     label;
881       PetscBool   isdepth;
882       const char *name;
883 
884       PetscCall(DMGetLabelName(dm, l, &name));
885       PetscCall(PetscStrcmp(name, "depth", &isdepth));
886       if (isdepth) continue;
887       PetscCall(DMGetLabel(dm, name, &label));
888       PetscCall(DMLabelView(label, viewer));
889     }
890     if (size > 1) {
891       PetscSF sf;
892 
893       PetscCall(DMGetPointSF(dm, &sf));
894       PetscCall(PetscSFView(sf, viewer));
895     }
896     PetscCall(PetscViewerFlush(viewer));
897   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
898     const char  *name, *color;
899     const char  *defcolors[3]  = {"gray", "orange", "green"};
900     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
901     char         lname[PETSC_MAX_PATH_LEN];
902     PetscReal    scale         = 2.0;
903     PetscReal    tikzscale     = 1.0;
904     PetscBool    useNumbers    = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
905     double       tcoords[3];
906     PetscScalar *coords;
907     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
908     PetscMPIInt  rank, size;
909     char         **names, **colors, **lcolors;
910     PetscBool    flg, lflg;
911     PetscBT      wp = NULL;
912     PetscInt     pEnd, pStart;
913 
914     PetscCall(DMGetDimension(dm, &dim));
915     PetscCall(DMPlexGetDepth(dm, &depth));
916     PetscCall(DMGetNumLabels(dm, &numLabels));
917     numLabels  = PetscMax(numLabels, 10);
918     numColors  = 10;
919     numLColors = 10;
920     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
921     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
922     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
923     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
924     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
925     for (d = 0; d < 4; ++d) drawColors[d]  = PETSC_TRUE;
926     n = 4;
927     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
928     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
929     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
930     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
931     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
932     if (!useLabels) numLabels = 0;
933     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
934     if (!useColors) {
935       numColors = 3;
936       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
937     }
938     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
939     if (!useColors) {
940       numLColors = 4;
941       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
942     }
943     PetscCall(PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
944     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
945     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
946     PetscCheck(!flg || !plotEdges || depth >= dim,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
947     if (depth < dim) plotEdges = PETSC_FALSE;
948     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
949 
950     /* filter points with labelvalue != labeldefaultvalue */
951     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
952     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
953     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
954     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
955     if (lflg) {
956       DMLabel lbl;
957 
958       PetscCall(DMGetLabel(dm, lname, &lbl));
959       if (lbl) {
960         PetscInt val, defval;
961 
962         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
963         PetscCall(PetscBTCreate(pEnd-pStart, &wp));
964         for (c = pStart;  c < pEnd; c++) {
965           PetscInt *closure = NULL;
966           PetscInt  closureSize;
967 
968           PetscCall(DMLabelGetValue(lbl, c, &val));
969           if (val == defval) continue;
970 
971           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
972           for (p = 0; p < closureSize*2; p += 2) {
973             PetscCall(PetscBTSet(wp, closure[p] - pStart));
974           }
975           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
976         }
977       }
978     }
979 
980     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
981     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
982     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
983     PetscCall(PetscViewerASCIIPrintf(viewer, "\
984 \\documentclass[tikz]{standalone}\n\n\
985 \\usepackage{pgflibraryshapes}\n\
986 \\usetikzlibrary{backgrounds}\n\
987 \\usetikzlibrary{arrows}\n\
988 \\begin{document}\n"));
989     if (size > 1) {
990       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
991       for (p = 0; p < size; ++p) {
992         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size-1) ? ", and " :  ", "));
993         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p%numColors], p));
994       }
995       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
996     }
997     if (drawHasse) {
998       PetscInt maxStratum = PetscMax(vEnd-vStart, PetscMax(eEnd-eStart, cEnd-cStart));
999 
1000       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1001       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd-1));
1002       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd-vStart));
1003       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum-(vEnd-vStart))/2.));
1004       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1005       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd-1));
1006       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum-(eEnd-eStart))/2.));
1007       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd-eStart));
1008       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1009       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd-1));
1010       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd-cStart));
1011       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum-(cEnd-cStart))/2.));
1012     }
1013     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale));
1014 
1015     /* Plot vertices */
1016     PetscCall(VecGetArray(coordinates, &coords));
1017     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1018     for (v = vStart; v < vEnd; ++v) {
1019       PetscInt  off, dof, d;
1020       PetscBool isLabeled = PETSC_FALSE;
1021 
1022       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
1023       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1024       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1025       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1026       PetscCheck(dof <= 3,PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3",v,dof);
1027       for (d = 0; d < dof; ++d) {
1028         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1029         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1030       }
1031       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1032       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1033       for (d = 0; d < dof; ++d) {
1034         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1035         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]));
1036       }
1037       if (drawHasse) color = colors[0%numColors];
1038       else           color = colors[rank%numColors];
1039       for (l = 0; l < numLabels; ++l) {
1040         PetscInt val;
1041         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1042         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1043       }
1044       if (drawNumbers[0]) {
1045         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1046       } else if (drawColors[0]) {
1047         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1048       } else {
1049         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1050       }
1051     }
1052     PetscCall(VecRestoreArray(coordinates, &coords));
1053     PetscCall(PetscViewerFlush(viewer));
1054     /* Plot edges */
1055     if (plotEdges) {
1056       PetscCall(VecGetArray(coordinates, &coords));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1058       for (e = eStart; e < eEnd; ++e) {
1059         const PetscInt *cone;
1060         PetscInt        coneSize, offA, offB, dof, d;
1061 
1062         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1063         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1064         PetscCheck(coneSize == 2,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1065         PetscCall(DMPlexGetCone(dm, e, &cone));
1066         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1067         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1068         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1069         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1070         for (d = 0; d < dof; ++d) {
1071           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
1072           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1073         }
1074         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1075         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1076         for (d = 0; d < dof; ++d) {
1077           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1078           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1079         }
1080         if (drawHasse) color = colors[1%numColors];
1081         else           color = colors[rank%numColors];
1082         for (l = 0; l < numLabels; ++l) {
1083           PetscInt val;
1084           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1085           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1086         }
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1088       }
1089       PetscCall(VecRestoreArray(coordinates, &coords));
1090       PetscCall(PetscViewerFlush(viewer));
1091       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1092     }
1093     /* Plot cells */
1094     if (dim == 3 || !drawNumbers[1]) {
1095       for (e = eStart; e < eEnd; ++e) {
1096         const PetscInt *cone;
1097 
1098         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1099         color = colors[rank%numColors];
1100         for (l = 0; l < numLabels; ++l) {
1101           PetscInt val;
1102           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1103           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1104         }
1105         PetscCall(DMPlexGetCone(dm, e, &cone));
1106         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1107       }
1108     } else {
1109        DMPolytopeType ct;
1110 
1111       /* Drawing a 2D polygon */
1112       for (c = cStart; c < cEnd; ++c) {
1113         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1114         PetscCall(DMPlexGetCellType(dm, c, &ct));
1115         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR ||
1116             ct == DM_POLYTOPE_TRI_PRISM_TENSOR ||
1117             ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1118           const PetscInt *cone;
1119           PetscInt        coneSize, e;
1120 
1121           PetscCall(DMPlexGetCone(dm, c, &cone));
1122           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1123           for (e = 0; e < coneSize; ++e) {
1124             const PetscInt *econe;
1125 
1126             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1127             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank%numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1128           }
1129         } else {
1130           PetscInt *closure = NULL;
1131           PetscInt  closureSize, Nv = 0, v;
1132 
1133           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1134           for (p = 0; p < closureSize*2; p += 2) {
1135             const PetscInt point = closure[p];
1136 
1137             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1138           }
1139           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]));
1140           for (v = 0; v <= Nv; ++v) {
1141             const PetscInt vertex = closure[v%Nv];
1142 
1143             if (v > 0) {
1144               if (plotEdges) {
1145                 const PetscInt *edge;
1146                 PetscInt        endpoints[2], ne;
1147 
1148                 endpoints[0] = closure[v-1]; endpoints[1] = vertex;
1149                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1150                 PetscCheck(ne == 1,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1151                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1152                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1153               } else {
1154                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1155               }
1156             }
1157             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1158           }
1159           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1160           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1161         }
1162       }
1163     }
1164     PetscCall(VecGetArray(coordinates, &coords));
1165     for (c = cStart; c < cEnd; ++c) {
1166       double    ccoords[3] = {0.0, 0.0, 0.0};
1167       PetscBool isLabeled  = PETSC_FALSE;
1168       PetscInt *closure    = NULL;
1169       PetscInt  closureSize, dof, d, n = 0;
1170 
1171       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
1172       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1173       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1174       for (p = 0; p < closureSize*2; p += 2) {
1175         const PetscInt point = closure[p];
1176         PetscInt       off;
1177 
1178         if ((point < vStart) || (point >= vEnd)) continue;
1179         PetscCall(PetscSectionGetDof(coordSection, point, &dof));
1180         PetscCall(PetscSectionGetOffset(coordSection, point, &off));
1181         for (d = 0; d < dof; ++d) {
1182           tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1183           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1184         }
1185         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1186         if (dof == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1187         for (d = 0; d < dof; ++d) {ccoords[d] += tcoords[d];}
1188         ++n;
1189       }
1190       for (d = 0; d < dof; ++d) {ccoords[d] /= n;}
1191       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1192       for (d = 0; d < dof; ++d) {
1193         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1194         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]));
1195       }
1196       if (drawHasse) color = colors[depth%numColors];
1197       else           color = colors[rank%numColors];
1198       for (l = 0; l < numLabels; ++l) {
1199         PetscInt val;
1200         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1201         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1202       }
1203       if (drawNumbers[dim]) {
1204         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1205       } else if (drawColors[dim]) {
1206         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1207       } else {
1208         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1209       }
1210     }
1211     PetscCall(VecRestoreArray(coordinates, &coords));
1212     if (drawHasse) {
1213       color = colors[depth%numColors];
1214       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1215       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1216       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1217       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1218       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1219 
1220       color = colors[1%numColors];
1221       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1222       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1223       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1224       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1225       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1226 
1227       color = colors[0%numColors];
1228       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1229       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1230       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1231       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1232       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1233 
1234       for (p = pStart; p < pEnd; ++p) {
1235         const PetscInt *cone;
1236         PetscInt        coneSize, cp;
1237 
1238         PetscCall(DMPlexGetCone(dm, p, &cone));
1239         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1240         for (cp = 0; cp < coneSize; ++cp) {
1241           PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1242         }
1243       }
1244     }
1245     PetscCall(PetscViewerFlush(viewer));
1246     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1247     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1248     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1249     for (l = 0; l < numLabels;  ++l) PetscCall(PetscFree(names[l]));
1250     for (c = 0; c < numColors;  ++c) PetscCall(PetscFree(colors[c]));
1251     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1252     PetscCall(PetscFree3(names, colors, lcolors));
1253     PetscCall(PetscBTDestroy(&wp));
1254   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1255     Vec                    cown,acown;
1256     VecScatter             sct;
1257     ISLocalToGlobalMapping g2l;
1258     IS                     gid,acis;
1259     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
1260     MPI_Group              ggroup,ngroup;
1261     PetscScalar            *array,nid;
1262     const PetscInt         *idxs;
1263     PetscInt               *idxs2,*start,*adjacency,*work;
1264     PetscInt64             lm[3],gm[3];
1265     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
1266     PetscMPIInt            d1,d2,rank;
1267 
1268     PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
1269     PetscCallMPI(MPI_Comm_rank(comm,&rank));
1270 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1271     PetscCallMPI(MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm));
1272 #endif
1273     if (ncomm != MPI_COMM_NULL) {
1274       PetscCallMPI(MPI_Comm_group(comm,&ggroup));
1275       PetscCallMPI(MPI_Comm_group(ncomm,&ngroup));
1276       d1   = 0;
1277       PetscCallMPI(MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2));
1278       nid  = d2;
1279       PetscCallMPI(MPI_Group_free(&ggroup));
1280       PetscCallMPI(MPI_Group_free(&ngroup));
1281       PetscCallMPI(MPI_Comm_free(&ncomm));
1282     } else nid = 0.0;
1283 
1284     /* Get connectivity */
1285     PetscCall(DMPlexGetVTKCellHeight(dm,&cellHeight));
1286     PetscCall(DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid));
1287 
1288     /* filter overlapped local cells */
1289     PetscCall(DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd));
1290     PetscCall(ISGetIndices(gid,&idxs));
1291     PetscCall(ISGetLocalSize(gid,&cum));
1292     PetscCall(PetscMalloc1(cum,&idxs2));
1293     for (c = cStart, cum = 0; c < cEnd; c++) {
1294       if (idxs[c-cStart] < 0) continue;
1295       idxs2[cum++] = idxs[c-cStart];
1296     }
1297     PetscCall(ISRestoreIndices(gid,&idxs));
1298     PetscCheck(numVertices == cum,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %" PetscInt_FMT " != %" PetscInt_FMT,numVertices,cum);
1299     PetscCall(ISDestroy(&gid));
1300     PetscCall(ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid));
1301 
1302     /* support for node-aware cell locality */
1303     PetscCall(ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis));
1304     PetscCall(VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown));
1305     PetscCall(VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown));
1306     PetscCall(VecGetArray(cown,&array));
1307     for (c = 0; c < numVertices; c++) array[c] = nid;
1308     PetscCall(VecRestoreArray(cown,&array));
1309     PetscCall(VecScatterCreate(cown,acis,acown,NULL,&sct));
1310     PetscCall(VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1311     PetscCall(VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1312     PetscCall(ISDestroy(&acis));
1313     PetscCall(VecScatterDestroy(&sct));
1314     PetscCall(VecDestroy(&cown));
1315 
1316     /* compute edgeCut */
1317     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
1318     PetscCall(PetscMalloc1(cum,&work));
1319     PetscCall(ISLocalToGlobalMappingCreateIS(gid,&g2l));
1320     PetscCall(ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH));
1321     PetscCall(ISDestroy(&gid));
1322     PetscCall(VecGetArray(acown,&array));
1323     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1324       PetscInt totl;
1325 
1326       totl = start[c+1]-start[c];
1327       PetscCall(ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work));
1328       for (i = 0; i < totl; i++) {
1329         if (work[i] < 0) {
1330           ect  += 1;
1331           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1332         }
1333       }
1334     }
1335     PetscCall(PetscFree(work));
1336     PetscCall(VecRestoreArray(acown,&array));
1337     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1338     lm[1] = -numVertices;
1339     PetscCall(MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm));
1340     PetscCall(PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT,-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]));
1341     lm[0] = ect; /* edgeCut */
1342     lm[1] = ectn; /* node-aware edgeCut */
1343     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1344     PetscCall(MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm));
1345     PetscCall(PetscViewerASCIIPrintf(viewer,", empty %" PetscInt_FMT ")\n",(PetscInt)gm[2]));
1346 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1347     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),gm[0] ? ((double)(gm[1]))/((double)gm[0]) : 1.));
1348 #else
1349     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0));
1350 #endif
1351     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1352     PetscCall(PetscFree(start));
1353     PetscCall(PetscFree(adjacency));
1354     PetscCall(VecDestroy(&acown));
1355   } else {
1356     const char    *name;
1357     PetscInt      *sizes, *hybsizes, *ghostsizes;
1358     PetscInt       locDepth, depth, cellHeight, dim, d;
1359     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1360     PetscInt       numLabels, l, maxSize = 17;
1361     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1362     MPI_Comm       comm;
1363     PetscMPIInt    size, rank;
1364 
1365     PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
1366     PetscCallMPI(MPI_Comm_size(comm, &size));
1367     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1368     PetscCall(DMGetDimension(dm, &dim));
1369     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1370     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1371     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1372     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1373     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1374     PetscCall(DMPlexGetDepth(dm, &locDepth));
1375     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1376     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1377     gcNum = gcEnd - gcStart;
1378     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1379     else                PetscCall(PetscCalloc3(3,    &sizes, 3,    &hybsizes, 3,    &ghostsizes));
1380     for (d = 0; d <= depth; d++) {
1381       PetscInt Nc[2] = {0, 0}, ict;
1382 
1383       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1384       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1385       ict  = ct0;
1386       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1387       ct0  = (DMPolytopeType) ict;
1388       for (p = pStart; p < pEnd; ++p) {
1389         DMPolytopeType ct;
1390 
1391         PetscCall(DMPlexGetCellType(dm, p, &ct));
1392         if (ct == ct0) ++Nc[0];
1393         else           ++Nc[1];
1394       }
1395       if (size < maxSize) {
1396         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm));
1397         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1398         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1399         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1400         for (p = 0; p < size; ++p) {
1401           if (rank == 0) {
1402             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p]+hybsizes[p]));
1403             if (hybsizes[p]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1404             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1405           }
1406         }
1407       } else {
1408         PetscInt locMinMax[2];
1409 
1410         locMinMax[0] = Nc[0]+Nc[1]; locMinMax[1] = Nc[0]+Nc[1];
1411         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1412         locMinMax[0] = Nc[1]; locMinMax[1] = Nc[1];
1413         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1414         if (d == depth) {
1415           locMinMax[0] = gcNum; locMinMax[1] = gcNum;
1416           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1417         }
1418         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1419         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1420         if (hybsizes[0]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1421         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1422       }
1423       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1424     }
1425     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1426     {
1427       const PetscReal      *maxCell;
1428       const PetscReal      *L;
1429       const DMBoundaryType *bd;
1430       PetscBool             per, localized;
1431 
1432       PetscCall(DMGetPeriodicity(dm, &per, &maxCell, &L, &bd));
1433       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1434       if (per) {
1435         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh ("));
1436         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1437         for (d = 0; d < dim; ++d) {
1438           if (bd && d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1439           if (bd)    PetscCall(PetscViewerASCIIPrintf(viewer, "%s", DMBoundaryTypes[bd[d]]));
1440         }
1441         PetscCall(PetscViewerASCIIPrintf(viewer, ") coordinates %s\n", localized ? "localized" : "not localized"));
1442         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1443       }
1444     }
1445     PetscCall(DMGetNumLabels(dm, &numLabels));
1446     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1447     for (l = 0; l < numLabels; ++l) {
1448       DMLabel         label;
1449       const char     *name;
1450       IS              valueIS;
1451       const PetscInt *values;
1452       PetscInt        numValues, v;
1453 
1454       PetscCall(DMGetLabelName(dm, l, &name));
1455       PetscCall(DMGetLabel(dm, name, &label));
1456       PetscCall(DMLabelGetNumValues(label, &numValues));
1457       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1458       PetscCall(DMLabelGetValueIS(label, &valueIS));
1459       PetscCall(ISGetIndices(valueIS, &values));
1460       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1461       for (v = 0; v < numValues; ++v) {
1462         PetscInt size;
1463 
1464         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1465         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1466         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1467       }
1468       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1469       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1470       PetscCall(ISRestoreIndices(valueIS, &values));
1471       PetscCall(ISDestroy(&valueIS));
1472     }
1473     {
1474       char    **labelNames;
1475       PetscInt  Nl = numLabels;
1476       PetscBool flg;
1477 
1478       PetscCall(PetscMalloc1(Nl, &labelNames));
1479       PetscCall(PetscOptionsGetStringArray(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1480       for (l = 0; l < Nl; ++l) {
1481         DMLabel label;
1482 
1483         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1484         if (flg) {
1485           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1486           PetscCall(DMLabelView(label, viewer));
1487         }
1488         PetscCall(PetscFree(labelNames[l]));
1489       }
1490       PetscCall(PetscFree(labelNames));
1491     }
1492     /* If no fields are specified, people do not want to see adjacency */
1493     if (dm->Nf) {
1494       PetscInt f;
1495 
1496       for (f = 0; f < dm->Nf; ++f) {
1497         const char *name;
1498 
1499         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1500         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1501         PetscCall(PetscViewerASCIIPushTab(viewer));
1502         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1503         if (dm->fields[f].adjacency[0]) {
1504           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1505           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1506         } else {
1507           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1508           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1509         }
1510         PetscCall(PetscViewerASCIIPopTab(viewer));
1511       }
1512     }
1513     PetscCall(DMGetCoarseDM(dm, &cdm));
1514     if (cdm) {
1515       PetscCall(PetscViewerASCIIPushTab(viewer));
1516       PetscCall(DMPlexView_Ascii(cdm, viewer));
1517       PetscCall(PetscViewerASCIIPopTab(viewer));
1518     }
1519   }
1520   PetscFunctionReturn(0);
1521 }
1522 
1523 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1524 {
1525   DMPolytopeType ct;
1526   PetscMPIInt    rank;
1527   PetscInt       cdim;
1528 
1529   PetscFunctionBegin;
1530   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1531   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1532   PetscCall(DMGetCoordinateDim(dm, &cdim));
1533   switch (ct) {
1534   case DM_POLYTOPE_SEGMENT:
1535   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1536     switch (cdim) {
1537     case 1:
1538     {
1539       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1540       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1541 
1542       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y,    PetscRealPart(coords[1]), y,    PETSC_DRAW_BLACK));
1543       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y+dy, PetscRealPart(coords[0]), y-dy, PETSC_DRAW_BLACK));
1544       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y+dy, PetscRealPart(coords[1]), y-dy, PETSC_DRAW_BLACK));
1545     }
1546     break;
1547     case 2:
1548     {
1549       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1550       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1551       const PetscReal l  = 0.1/PetscSqrtReal(dx*dx + dy*dy);
1552 
1553       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1554       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0])+l*dx, PetscRealPart(coords[1])+l*dy, PetscRealPart(coords[0])-l*dx, PetscRealPart(coords[1])-l*dy, PETSC_DRAW_BLACK));
1555       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2])+l*dx, PetscRealPart(coords[3])+l*dy, PetscRealPart(coords[2])-l*dx, PetscRealPart(coords[3])-l*dy, PETSC_DRAW_BLACK));
1556     }
1557     break;
1558     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1559     }
1560     break;
1561   case DM_POLYTOPE_TRIANGLE:
1562     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1563                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1564                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1565                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1566     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1567     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1568     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1569     break;
1570   case DM_POLYTOPE_QUADRILATERAL:
1571     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1572                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1573                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1574                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1575     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1576                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1577                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1578                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1579     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1580     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1581     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1582     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1583     break;
1584   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1585   }
1586   PetscFunctionReturn(0);
1587 }
1588 
1589 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1590 {
1591   DMPolytopeType ct;
1592   PetscReal      centroid[2] = {0., 0.};
1593   PetscMPIInt    rank;
1594   PetscInt       fillColor, v, e, d;
1595 
1596   PetscFunctionBegin;
1597   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1598   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1599   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2;
1600   switch (ct) {
1601   case DM_POLYTOPE_TRIANGLE:
1602     {
1603       PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1604 
1605       for (v = 0; v < 3; ++v) {centroid[0] += PetscRealPart(coords[v*2+0])/3.;centroid[1] += PetscRealPart(coords[v*2+1])/3.;}
1606       for (e = 0; e < 3; ++e) {
1607         refCoords[0] = refVertices[e*2+0];
1608         refCoords[1] = refVertices[e*2+1];
1609         for (d = 1; d <= edgeDiv; ++d) {
1610           refCoords[d*2+0] = refCoords[0] + (refVertices[(e+1)%3 * 2 + 0] - refCoords[0])*d/edgeDiv;
1611           refCoords[d*2+1] = refCoords[1] + (refVertices[(e+1)%3 * 2 + 1] - refCoords[1])*d/edgeDiv;
1612         }
1613         PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv+1, refCoords, edgeCoords));
1614         for (d = 0; d < edgeDiv; ++d) {
1615           PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], fillColor, fillColor, fillColor));
1616           PetscCall(PetscDrawLine(draw, edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], PETSC_DRAW_BLACK));
1617         }
1618       }
1619     }
1620     break;
1621   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1622   }
1623   PetscFunctionReturn(0);
1624 }
1625 
1626 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1627 {
1628   PetscDraw          draw;
1629   DM                 cdm;
1630   PetscSection       coordSection;
1631   Vec                coordinates;
1632   const PetscScalar *coords;
1633   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1634   PetscReal         *refCoords, *edgeCoords;
1635   PetscBool          isnull, drawAffine = PETSC_TRUE;
1636   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1637 
1638   PetscFunctionBegin;
1639   PetscCall(DMGetCoordinateDim(dm, &dim));
1640   PetscCheck(dim <= 2,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1641   PetscCall(PetscOptionsGetBool(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1642   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv+1)*dim, &refCoords, (edgeDiv+1)*dim, &edgeCoords));
1643   PetscCall(DMGetCoordinateDM(dm, &cdm));
1644   PetscCall(DMGetLocalSection(cdm, &coordSection));
1645   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1646   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1647   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1648 
1649   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1650   PetscCall(PetscDrawIsNull(draw, &isnull));
1651   if (isnull) PetscFunctionReturn(0);
1652   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1653 
1654   PetscCall(VecGetLocalSize(coordinates, &N));
1655   PetscCall(VecGetArrayRead(coordinates, &coords));
1656   for (c = 0; c < N; c += dim) {
1657     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1658     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1659   }
1660   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1661   PetscCall(MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm)));
1662   PetscCall(MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm)));
1663   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1664   PetscCall(PetscDrawClear(draw));
1665 
1666   for (c = cStart; c < cEnd; ++c) {
1667     PetscScalar *coords = NULL;
1668     PetscInt     numCoords;
1669 
1670     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1671     if (drawAffine) {
1672       PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1673     } else {
1674       PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1675     }
1676     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1677   }
1678   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1679   PetscCall(PetscDrawFlush(draw));
1680   PetscCall(PetscDrawPause(draw));
1681   PetscCall(PetscDrawSave(draw));
1682   PetscFunctionReturn(0);
1683 }
1684 
1685 #if defined(PETSC_HAVE_EXODUSII)
1686 #include <exodusII.h>
1687 #include <petscviewerexodusii.h>
1688 #endif
1689 
1690 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1691 {
1692   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus;
1693   char           name[PETSC_MAX_PATH_LEN];
1694 
1695   PetscFunctionBegin;
1696   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1697   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1698   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii));
1699   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
1700   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
1701   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
1702   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
1703   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus));
1704   if (iascii) {
1705     PetscViewerFormat format;
1706     PetscCall(PetscViewerGetFormat(viewer, &format));
1707     if (format == PETSC_VIEWER_ASCII_GLVIS) {
1708       PetscCall(DMPlexView_GLVis(dm, viewer));
1709     } else {
1710       PetscCall(DMPlexView_Ascii(dm, viewer));
1711     }
1712   } else if (ishdf5) {
1713 #if defined(PETSC_HAVE_HDF5)
1714     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1715 #else
1716     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1717 #endif
1718   } else if (isvtk) {
1719     PetscCall(DMPlexVTKWriteAll((PetscObject) dm,viewer));
1720   } else if (isdraw) {
1721     PetscCall(DMPlexView_Draw(dm, viewer));
1722   } else if (isglvis) {
1723     PetscCall(DMPlexView_GLVis(dm, viewer));
1724 #if defined(PETSC_HAVE_EXODUSII)
1725   } else if (isexodus) {
1726 /*
1727       exodusII requires that all sets be part of exactly one cell set.
1728       If the dm does not have a "Cell Sets" label defined, we create one
1729       with ID 1, containig all cells.
1730       Note that if the Cell Sets label is defined but does not cover all cells,
1731       we may still have a problem. This should probably be checked here or in the viewer;
1732     */
1733     PetscInt numCS;
1734     PetscCall(DMGetLabelSize(dm,"Cell Sets",&numCS));
1735     if (!numCS) {
1736       PetscInt cStart, cEnd, c;
1737       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1738       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1739       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1740     }
1741     PetscCall(DMView_PlexExodusII(dm, viewer));
1742 #endif
1743   } else {
1744     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1745   }
1746   /* Optionally view the partition */
1747   PetscCall(PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg));
1748   if (flg) {
1749     Vec ranks;
1750     PetscCall(DMPlexCreateRankField(dm, &ranks));
1751     PetscCall(VecView(ranks, viewer));
1752     PetscCall(VecDestroy(&ranks));
1753   }
1754   /* Optionally view a label */
1755   PetscCall(PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1756   if (flg) {
1757     DMLabel label;
1758     Vec     val;
1759 
1760     PetscCall(DMGetLabel(dm, name, &label));
1761     PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1762     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1763     PetscCall(VecView(val, viewer));
1764     PetscCall(VecDestroy(&val));
1765   }
1766   PetscFunctionReturn(0);
1767 }
1768 
1769 /*@
1770   DMPlexTopologyView - Saves a DMPlex topology into a file
1771 
1772   Collective on DM
1773 
1774   Input Parameters:
1775 + dm     - The DM whose topology is to be saved
1776 - viewer - The PetscViewer for saving
1777 
1778   Level: advanced
1779 
1780 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1781 @*/
1782 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1783 {
1784   PetscBool      ishdf5;
1785 
1786   PetscFunctionBegin;
1787   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1788   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1789   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1790   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView,viewer,0,0,0));
1791   if (ishdf5) {
1792 #if defined(PETSC_HAVE_HDF5)
1793     PetscViewerFormat format;
1794     PetscCall(PetscViewerGetFormat(viewer, &format));
1795     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1796       IS globalPointNumbering;
1797 
1798       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1799       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1800       PetscCall(ISDestroy(&globalPointNumbering));
1801     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1802 #else
1803     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1804 #endif
1805   }
1806   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView,viewer,0,0,0));
1807   PetscFunctionReturn(0);
1808 }
1809 
1810 /*@
1811   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1812 
1813   Collective on DM
1814 
1815   Input Parameters:
1816 + dm     - The DM whose coordinates are to be saved
1817 - viewer - The PetscViewer for saving
1818 
1819   Level: advanced
1820 
1821 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1822 @*/
1823 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1824 {
1825   PetscBool      ishdf5;
1826 
1827   PetscFunctionBegin;
1828   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1829   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1830   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1831   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView,viewer,0,0,0));
1832   if (ishdf5) {
1833 #if defined(PETSC_HAVE_HDF5)
1834     PetscViewerFormat format;
1835     PetscCall(PetscViewerGetFormat(viewer, &format));
1836     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1837       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1838     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1839 #else
1840     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1841 #endif
1842   }
1843   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView,viewer,0,0,0));
1844   PetscFunctionReturn(0);
1845 }
1846 
1847 /*@
1848   DMPlexLabelsView - Saves DMPlex labels into a file
1849 
1850   Collective on DM
1851 
1852   Input Parameters:
1853 + dm     - The DM whose labels are to be saved
1854 - viewer - The PetscViewer for saving
1855 
1856   Level: advanced
1857 
1858 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1859 @*/
1860 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1861 {
1862   PetscBool      ishdf5;
1863 
1864   PetscFunctionBegin;
1865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1866   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1867   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1868   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView,viewer,0,0,0));
1869   if (ishdf5) {
1870 #if defined(PETSC_HAVE_HDF5)
1871     IS                globalPointNumbering;
1872     PetscViewerFormat format;
1873 
1874     PetscCall(PetscViewerGetFormat(viewer, &format));
1875     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1876       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1877       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1878       PetscCall(ISDestroy(&globalPointNumbering));
1879     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1880 #else
1881     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1882 #endif
1883   }
1884   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView,viewer,0,0,0));
1885   PetscFunctionReturn(0);
1886 }
1887 
1888 /*@
1889   DMPlexSectionView - Saves a section associated with a DMPlex
1890 
1891   Collective on DM
1892 
1893   Input Parameters:
1894 + dm         - The DM that contains the topology on which the section to be saved is defined
1895 . viewer     - The PetscViewer for saving
1896 - sectiondm  - The DM that contains the section to be saved
1897 
1898   Level: advanced
1899 
1900   Notes:
1901   This function is a wrapper around PetscSectionView(); in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with DMPlexTopologyLoad() and DMPlexSectionLoad(), respectively, this information is used to match section points with topology points.
1902 
1903   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1904 
1905 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1906 @*/
1907 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1908 {
1909   PetscBool      ishdf5;
1910 
1911   PetscFunctionBegin;
1912   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1913   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1914   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1915   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
1916   PetscCall(PetscLogEventBegin(DMPLEX_SectionView,viewer,0,0,0));
1917   if (ishdf5) {
1918 #if defined(PETSC_HAVE_HDF5)
1919     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1920 #else
1921     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1922 #endif
1923   }
1924   PetscCall(PetscLogEventEnd(DMPLEX_SectionView,viewer,0,0,0));
1925   PetscFunctionReturn(0);
1926 }
1927 
1928 /*@
1929   DMPlexGlobalVectorView - Saves a global vector
1930 
1931   Collective on DM
1932 
1933   Input Parameters:
1934 + dm        - The DM that represents the topology
1935 . viewer    - The PetscViewer to save data with
1936 . sectiondm - The DM that contains the global section on which vec is defined
1937 - vec       - The global vector to be saved
1938 
1939   Level: advanced
1940 
1941   Notes:
1942   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1943 
1944   Typical calling sequence
1945 $       DMCreate(PETSC_COMM_WORLD, &dm);
1946 $       DMSetType(dm, DMPLEX);
1947 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1948 $       DMClone(dm, &sectiondm);
1949 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1950 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1951 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1952 $       PetscSectionSetChart(section, pStart, pEnd);
1953 $       PetscSectionSetUp(section);
1954 $       DMSetLocalSection(sectiondm, section);
1955 $       PetscSectionDestroy(&section);
1956 $       DMGetGlobalVector(sectiondm, &vec);
1957 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1958 $       DMPlexTopologyView(dm, viewer);
1959 $       DMPlexSectionView(dm, viewer, sectiondm);
1960 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1961 $       DMRestoreGlobalVector(sectiondm, &vec);
1962 $       DMDestroy(&sectiondm);
1963 $       DMDestroy(&dm);
1964 
1965 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1966 @*/
1967 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
1968 {
1969   PetscBool       ishdf5;
1970 
1971   PetscFunctionBegin;
1972   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1973   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1974   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1975   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1976   /* Check consistency */
1977   {
1978     PetscSection  section;
1979     PetscBool     includesConstraints;
1980     PetscInt      m, m1;
1981 
1982     PetscCall(VecGetLocalSize(vec, &m1));
1983     PetscCall(DMGetGlobalSection(sectiondm, &section));
1984     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1985     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
1986     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
1987     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
1988   }
1989   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1990   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView,viewer,0,0,0));
1991   if (ishdf5) {
1992 #if defined(PETSC_HAVE_HDF5)
1993     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
1994 #else
1995     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1996 #endif
1997   }
1998   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView,viewer,0,0,0));
1999   PetscFunctionReturn(0);
2000 }
2001 
2002 /*@
2003   DMPlexLocalVectorView - Saves a local vector
2004 
2005   Collective on DM
2006 
2007   Input Parameters:
2008 + dm        - The DM that represents the topology
2009 . viewer    - The PetscViewer to save data with
2010 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2011 - vec       - The local vector to be saved
2012 
2013   Level: advanced
2014 
2015   Notes:
2016   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2017 
2018   Typical calling sequence
2019 $       DMCreate(PETSC_COMM_WORLD, &dm);
2020 $       DMSetType(dm, DMPLEX);
2021 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2022 $       DMClone(dm, &sectiondm);
2023 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2024 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2025 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2026 $       PetscSectionSetChart(section, pStart, pEnd);
2027 $       PetscSectionSetUp(section);
2028 $       DMSetLocalSection(sectiondm, section);
2029 $       DMGetLocalVector(sectiondm, &vec);
2030 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2031 $       DMPlexTopologyView(dm, viewer);
2032 $       DMPlexSectionView(dm, viewer, sectiondm);
2033 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2034 $       DMRestoreLocalVector(sectiondm, &vec);
2035 $       DMDestroy(&sectiondm);
2036 $       DMDestroy(&dm);
2037 
2038 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2039 @*/
2040 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2041 {
2042   PetscBool       ishdf5;
2043 
2044   PetscFunctionBegin;
2045   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2046   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2047   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2048   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2049   /* Check consistency */
2050   {
2051     PetscSection  section;
2052     PetscBool     includesConstraints;
2053     PetscInt      m, m1;
2054 
2055     PetscCall(VecGetLocalSize(vec, &m1));
2056     PetscCall(DMGetLocalSection(sectiondm, &section));
2057     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2058     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2059     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2060     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2061   }
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2063   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView,viewer,0,0,0));
2064   if (ishdf5) {
2065 #if defined(PETSC_HAVE_HDF5)
2066     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2067 #else
2068     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2069 #endif
2070   }
2071   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView,viewer,0,0,0));
2072   PetscFunctionReturn(0);
2073 }
2074 
2075 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2076 {
2077   PetscBool      ishdf5;
2078 
2079   PetscFunctionBegin;
2080   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2081   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2082   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5));
2083   if (ishdf5) {
2084 #if defined(PETSC_HAVE_HDF5)
2085     PetscViewerFormat format;
2086     PetscCall(PetscViewerGetFormat(viewer, &format));
2087     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2088       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2089     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2090       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2091     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2092     PetscFunctionReturn(0);
2093 #else
2094     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2095 #endif
2096   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2097 }
2098 
2099 /*@
2100   DMPlexTopologyLoad - Loads a topology into a DMPlex
2101 
2102   Collective on DM
2103 
2104   Input Parameters:
2105 + dm     - The DM into which the topology is loaded
2106 - viewer - The PetscViewer for the saved topology
2107 
2108   Output Parameters:
2109 . globalToLocalPointSF - The PetscSF that pushes points in [0, N) to the associated points in the loaded plex, where N is the global number of points; NULL if unneeded
2110 
2111   Level: advanced
2112 
2113 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2114 @*/
2115 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2116 {
2117   PetscBool      ishdf5;
2118 
2119   PetscFunctionBegin;
2120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2121   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2122   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2123   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2124   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad,viewer,0,0,0));
2125   if (ishdf5) {
2126 #if defined(PETSC_HAVE_HDF5)
2127     PetscViewerFormat format;
2128     PetscCall(PetscViewerGetFormat(viewer, &format));
2129     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2130       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2131     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2132 #else
2133     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2134 #endif
2135   }
2136   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad,viewer,0,0,0));
2137   PetscFunctionReturn(0);
2138 }
2139 
2140 /*@
2141   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2142 
2143   Collective on DM
2144 
2145   Input Parameters:
2146 + dm     - The DM into which the coordinates are loaded
2147 . viewer - The PetscViewer for the saved coordinates
2148 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2149 
2150   Level: advanced
2151 
2152 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2153 @*/
2154 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2155 {
2156   PetscBool      ishdf5;
2157 
2158   PetscFunctionBegin;
2159   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2160   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2161   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2162   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2163   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2164   if (ishdf5) {
2165 #if defined(PETSC_HAVE_HDF5)
2166     PetscViewerFormat format;
2167     PetscCall(PetscViewerGetFormat(viewer, &format));
2168     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2169       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2170     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2171 #else
2172     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2173 #endif
2174   }
2175   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2176   PetscFunctionReturn(0);
2177 }
2178 
2179 /*@
2180   DMPlexLabelsLoad - Loads labels into a DMPlex
2181 
2182   Collective on DM
2183 
2184   Input Parameters:
2185 + dm     - The DM into which the labels are loaded
2186 . viewer - The PetscViewer for the saved labels
2187 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2188 
2189   Level: advanced
2190 
2191   Notes:
2192   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2193 
2194 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2195 @*/
2196 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2197 {
2198   PetscBool      ishdf5;
2199 
2200   PetscFunctionBegin;
2201   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2202   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2203   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2204   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2205   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad,viewer,0,0,0));
2206   if (ishdf5) {
2207 #if defined(PETSC_HAVE_HDF5)
2208     PetscViewerFormat format;
2209 
2210     PetscCall(PetscViewerGetFormat(viewer, &format));
2211     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2212       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2213     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2214 #else
2215     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2216 #endif
2217   }
2218   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad,viewer,0,0,0));
2219   PetscFunctionReturn(0);
2220 }
2221 
2222 /*@
2223   DMPlexSectionLoad - Loads section into a DMPlex
2224 
2225   Collective on DM
2226 
2227   Input Parameters:
2228 + dm          - The DM that represents the topology
2229 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2230 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2231 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2232 
2233   Output Parameters
2234 + globalDofSF - The SF that migrates any on-disk Vec data associated with sectionA into a global Vec associated with the sectiondm's global section (NULL if not needed)
2235 - localDofSF  - The SF that migrates any on-disk Vec data associated with sectionA into a local Vec associated with the sectiondm's local section (NULL if not needed)
2236 
2237   Level: advanced
2238 
2239   Notes:
2240   This function is a wrapper around PetscSectionLoad(); it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in dm. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2241 
2242   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2243 
2244   The output parameter, globalDofSF (localDofSF), can later be used with DMPlexGlobalVectorLoad() (DMPlexLocalVectorLoad()) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2245 
2246   Example using 2 processes:
2247 $  NX (number of points on dm): 4
2248 $  sectionA                   : the on-disk section
2249 $  vecA                       : a vector associated with sectionA
2250 $  sectionB                   : sectiondm's local section constructed in this function
2251 $  vecB (local)               : a vector associated with sectiondm's local section
2252 $  vecB (global)              : a vector associated with sectiondm's global section
2253 $
2254 $                                     rank 0    rank 1
2255 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2256 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2257 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2258 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2259 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2260 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2261 $  sectionB->atlasDof             :     1 0 1 | 1 3
2262 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2263 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2264 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2265 $
2266 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2267 
2268 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2269 @*/
2270 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2271 {
2272   PetscBool      ishdf5;
2273 
2274   PetscFunctionBegin;
2275   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2276   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2277   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2278   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2279   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2280   if (localDofSF) PetscValidPointer(localDofSF, 6);
2281   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2282   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad,viewer,0,0,0));
2283   if (ishdf5) {
2284 #if defined(PETSC_HAVE_HDF5)
2285     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2286 #else
2287     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2288 #endif
2289   }
2290   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad,viewer,0,0,0));
2291   PetscFunctionReturn(0);
2292 }
2293 
2294 /*@
2295   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2296 
2297   Collective on DM
2298 
2299   Input Parameters:
2300 + dm        - The DM that represents the topology
2301 . viewer    - The PetscViewer that represents the on-disk vector data
2302 . sectiondm - The DM that contains the global section on which vec is defined
2303 . sf        - The SF that migrates the on-disk vector data into vec
2304 - vec       - The global vector to set values of
2305 
2306   Level: advanced
2307 
2308   Notes:
2309   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2310 
2311   Typical calling sequence
2312 $       DMCreate(PETSC_COMM_WORLD, &dm);
2313 $       DMSetType(dm, DMPLEX);
2314 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2315 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2316 $       DMClone(dm, &sectiondm);
2317 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2318 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2319 $       DMGetGlobalVector(sectiondm, &vec);
2320 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2321 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2322 $       DMRestoreGlobalVector(sectiondm, &vec);
2323 $       PetscSFDestroy(&gsf);
2324 $       PetscSFDestroy(&sfX);
2325 $       DMDestroy(&sectiondm);
2326 $       DMDestroy(&dm);
2327 
2328 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2329 @*/
2330 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2331 {
2332   PetscBool       ishdf5;
2333 
2334   PetscFunctionBegin;
2335   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2336   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2337   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2338   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2339   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2340   /* Check consistency */
2341   {
2342     PetscSection  section;
2343     PetscBool     includesConstraints;
2344     PetscInt      m, m1;
2345 
2346     PetscCall(VecGetLocalSize(vec, &m1));
2347     PetscCall(DMGetGlobalSection(sectiondm, &section));
2348     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2349     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2350     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2351     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2352   }
2353   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2354   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2355   if (ishdf5) {
2356 #if defined(PETSC_HAVE_HDF5)
2357     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2358 #else
2359     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2360 #endif
2361   }
2362   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2363   PetscFunctionReturn(0);
2364 }
2365 
2366 /*@
2367   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2368 
2369   Collective on DM
2370 
2371   Input Parameters:
2372 + dm        - The DM that represents the topology
2373 . viewer    - The PetscViewer that represents the on-disk vector data
2374 . sectiondm - The DM that contains the local section on which vec is defined
2375 . sf        - The SF that migrates the on-disk vector data into vec
2376 - vec       - The local vector to set values of
2377 
2378   Level: advanced
2379 
2380   Notes:
2381   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2382 
2383   Typical calling sequence
2384 $       DMCreate(PETSC_COMM_WORLD, &dm);
2385 $       DMSetType(dm, DMPLEX);
2386 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2387 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2388 $       DMClone(dm, &sectiondm);
2389 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2390 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2391 $       DMGetLocalVector(sectiondm, &vec);
2392 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2393 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2394 $       DMRestoreLocalVector(sectiondm, &vec);
2395 $       PetscSFDestroy(&lsf);
2396 $       PetscSFDestroy(&sfX);
2397 $       DMDestroy(&sectiondm);
2398 $       DMDestroy(&dm);
2399 
2400 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2401 @*/
2402 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2403 {
2404   PetscBool       ishdf5;
2405 
2406   PetscFunctionBegin;
2407   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2408   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2409   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2410   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2411   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2412   /* Check consistency */
2413   {
2414     PetscSection  section;
2415     PetscBool     includesConstraints;
2416     PetscInt      m, m1;
2417 
2418     PetscCall(VecGetLocalSize(vec, &m1));
2419     PetscCall(DMGetLocalSection(sectiondm, &section));
2420     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2421     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2422     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2423     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2424   }
2425   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2426   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2427   if (ishdf5) {
2428 #if defined(PETSC_HAVE_HDF5)
2429     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2430 #else
2431     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2432 #endif
2433   }
2434   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2435   PetscFunctionReturn(0);
2436 }
2437 
2438 PetscErrorCode DMDestroy_Plex(DM dm)
2439 {
2440   DM_Plex       *mesh = (DM_Plex*) dm->data;
2441 
2442   PetscFunctionBegin;
2443   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL));
2444   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL));
2445   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL));
2446   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMInterpolateSolution_C", NULL));
2447   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2448   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C", NULL));
2449   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeGetDefault_C", NULL));
2450   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeSetDefault_C", NULL));
2451   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"MatComputeNeumannOverlap_C",NULL));
2452   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderGetDefault_C", NULL));
2453   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderSetDefault_C", NULL));
2454   if (--mesh->refct > 0) PetscFunctionReturn(0);
2455   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2456   PetscCall(PetscFree(mesh->cones));
2457   PetscCall(PetscFree(mesh->coneOrientations));
2458   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2459   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2460   PetscCall(PetscFree(mesh->supports));
2461   PetscCall(PetscFree(mesh->facesTmp));
2462   PetscCall(PetscFree(mesh->tetgenOpts));
2463   PetscCall(PetscFree(mesh->triangleOpts));
2464   PetscCall(PetscFree(mesh->transformType));
2465   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2466   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2467   PetscCall(ISDestroy(&mesh->subpointIS));
2468   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2469   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2470   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2471   PetscCall(ISDestroy(&mesh->anchorIS));
2472   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2473   PetscCall(PetscFree(mesh->parents));
2474   PetscCall(PetscFree(mesh->childIDs));
2475   PetscCall(PetscSectionDestroy(&mesh->childSection));
2476   PetscCall(PetscFree(mesh->children));
2477   PetscCall(DMDestroy(&mesh->referenceTree));
2478   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2479   PetscCall(PetscFree(mesh->neighbors));
2480   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2481   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2482   PetscCall(PetscFree(mesh));
2483   PetscFunctionReturn(0);
2484 }
2485 
2486 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2487 {
2488   PetscSection           sectionGlobal;
2489   PetscInt               bs = -1, mbs;
2490   PetscInt               localSize;
2491   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2492   MatType                mtype;
2493   ISLocalToGlobalMapping ltog;
2494 
2495   PetscFunctionBegin;
2496   PetscCall(MatInitializePackage());
2497   mtype = dm->mattype;
2498   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2499   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2500   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2501   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2502   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2503   PetscCall(MatSetType(*J, mtype));
2504   PetscCall(MatSetFromOptions(*J));
2505   PetscCall(MatGetBlockSize(*J, &mbs));
2506   if (mbs > 1) bs = mbs;
2507   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2508   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2509   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2510   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2511   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2512   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2513   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2514   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2515   if (!isShell) {
2516     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2517     PetscInt  *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2];
2518     PetscInt  pStart, pEnd, p, dof, cdof;
2519 
2520     PetscCall(DMGetLocalToGlobalMapping(dm,&ltog));
2521     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2522     for (p = pStart; p < pEnd; ++p) {
2523       PetscInt bdof;
2524 
2525       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2526       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2527       dof  = dof < 0 ? -(dof+1) : dof;
2528       bdof = cdof && (dof-cdof) ? 1 : dof;
2529       if (dof) {
2530         if (bs < 0)          {bs = bdof;}
2531         else if (bs != bdof) {bs = 1; break;}
2532       }
2533     }
2534     /* Must have same blocksize on all procs (some might have no points) */
2535     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2536     bsLocal[1] = bs;
2537     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
2538     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2539     else bs = bsMinMax[0];
2540     bs = PetscMax(1,bs);
2541     PetscCall(MatSetLocalToGlobalMapping(*J,ltog,ltog));
2542     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2543       PetscCall(MatSetBlockSize(*J, bs));
2544       PetscCall(MatSetUp(*J));
2545     } else {
2546       PetscCall(PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu));
2547       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2548       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2549     }
2550   }
2551   PetscCall(MatSetDM(*J, dm));
2552   PetscFunctionReturn(0);
2553 }
2554 
2555 /*@
2556   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2557 
2558   Not collective
2559 
2560   Input Parameter:
2561 . mesh - The DMPlex
2562 
2563   Output Parameters:
2564 . subsection - The subdomain section
2565 
2566   Level: developer
2567 
2568 .seealso:
2569 @*/
2570 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2571 {
2572   DM_Plex       *mesh = (DM_Plex*) dm->data;
2573 
2574   PetscFunctionBegin;
2575   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2576   if (!mesh->subdomainSection) {
2577     PetscSection section;
2578     PetscSF      sf;
2579 
2580     PetscCall(PetscSFCreate(PETSC_COMM_SELF,&sf));
2581     PetscCall(DMGetLocalSection(dm,&section));
2582     PetscCall(PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection));
2583     PetscCall(PetscSFDestroy(&sf));
2584   }
2585   *subsection = mesh->subdomainSection;
2586   PetscFunctionReturn(0);
2587 }
2588 
2589 /*@
2590   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2591 
2592   Not collective
2593 
2594   Input Parameter:
2595 . mesh - The DMPlex
2596 
2597   Output Parameters:
2598 + pStart - The first mesh point
2599 - pEnd   - The upper bound for mesh points
2600 
2601   Level: beginner
2602 
2603 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2604 @*/
2605 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2606 {
2607   DM_Plex       *mesh = (DM_Plex*) dm->data;
2608 
2609   PetscFunctionBegin;
2610   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2611   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2612   PetscFunctionReturn(0);
2613 }
2614 
2615 /*@
2616   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2617 
2618   Not collective
2619 
2620   Input Parameters:
2621 + mesh - The DMPlex
2622 . pStart - The first mesh point
2623 - pEnd   - The upper bound for mesh points
2624 
2625   Output Parameters:
2626 
2627   Level: beginner
2628 
2629 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2630 @*/
2631 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2632 {
2633   DM_Plex       *mesh = (DM_Plex*) dm->data;
2634 
2635   PetscFunctionBegin;
2636   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2637   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2638   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2639   PetscFunctionReturn(0);
2640 }
2641 
2642 /*@
2643   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2644 
2645   Not collective
2646 
2647   Input Parameters:
2648 + mesh - The DMPlex
2649 - p - The point, which must lie in the chart set with DMPlexSetChart()
2650 
2651   Output Parameter:
2652 . size - The cone size for point p
2653 
2654   Level: beginner
2655 
2656 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2657 @*/
2658 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2659 {
2660   DM_Plex       *mesh = (DM_Plex*) dm->data;
2661 
2662   PetscFunctionBegin;
2663   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2664   PetscValidIntPointer(size, 3);
2665   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2666   PetscFunctionReturn(0);
2667 }
2668 
2669 /*@
2670   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2671 
2672   Not collective
2673 
2674   Input Parameters:
2675 + mesh - The DMPlex
2676 . p - The point, which must lie in the chart set with DMPlexSetChart()
2677 - size - The cone size for point p
2678 
2679   Output Parameter:
2680 
2681   Note:
2682   This should be called after DMPlexSetChart().
2683 
2684   Level: beginner
2685 
2686 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2687 @*/
2688 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2689 {
2690   DM_Plex       *mesh = (DM_Plex*) dm->data;
2691 
2692   PetscFunctionBegin;
2693   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2694   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2695   PetscFunctionReturn(0);
2696 }
2697 
2698 /*@
2699   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2700 
2701   Not collective
2702 
2703   Input Parameters:
2704 + mesh - The DMPlex
2705 . p - The point, which must lie in the chart set with DMPlexSetChart()
2706 - size - The additional cone size for point p
2707 
2708   Output Parameter:
2709 
2710   Note:
2711   This should be called after DMPlexSetChart().
2712 
2713   Level: beginner
2714 
2715 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2716 @*/
2717 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2718 {
2719   DM_Plex       *mesh = (DM_Plex*) dm->data;
2720   PetscFunctionBegin;
2721   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2722   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2723   PetscFunctionReturn(0);
2724 }
2725 
2726 /*@C
2727   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2728 
2729   Not collective
2730 
2731   Input Parameters:
2732 + dm - The DMPlex
2733 - p - The point, which must lie in the chart set with DMPlexSetChart()
2734 
2735   Output Parameter:
2736 . cone - An array of points which are on the in-edges for point p
2737 
2738   Level: beginner
2739 
2740   Fortran Notes:
2741   Since it returns an array, this routine is only available in Fortran 90, and you must
2742   include petsc.h90 in your code.
2743   You must also call DMPlexRestoreCone() after you finish using the returned array.
2744   DMPlexRestoreCone() is not needed/available in C.
2745 
2746 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2747 @*/
2748 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2749 {
2750   DM_Plex       *mesh = (DM_Plex*) dm->data;
2751   PetscInt       off;
2752 
2753   PetscFunctionBegin;
2754   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2755   PetscValidPointer(cone, 3);
2756   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2757   *cone = &mesh->cones[off];
2758   PetscFunctionReturn(0);
2759 }
2760 
2761 /*@C
2762   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2763 
2764   Not collective
2765 
2766   Input Parameters:
2767 + dm - The DMPlex
2768 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2769 
2770   Output Parameters:
2771 + pConesSection - PetscSection describing the layout of pCones
2772 - pCones - An array of points which are on the in-edges for the point set p
2773 
2774   Level: intermediate
2775 
2776 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2777 @*/
2778 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2779 {
2780   PetscSection        cs, newcs;
2781   PetscInt            *cones;
2782   PetscInt            *newarr=NULL;
2783   PetscInt            n;
2784 
2785   PetscFunctionBegin;
2786   PetscCall(DMPlexGetCones(dm, &cones));
2787   PetscCall(DMPlexGetConeSection(dm, &cs));
2788   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL));
2789   if (pConesSection) *pConesSection = newcs;
2790   if (pCones) {
2791     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2792     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2793   }
2794   PetscFunctionReturn(0);
2795 }
2796 
2797 /*@
2798   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2799 
2800   Not collective
2801 
2802   Input Parameters:
2803 + dm - The DMPlex
2804 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2805 
2806   Output Parameter:
2807 . expandedPoints - An array of vertices recursively expanded from input points
2808 
2809   Level: advanced
2810 
2811   Notes:
2812   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2813   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2814 
2815 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2816 @*/
2817 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2818 {
2819   IS                  *expandedPointsAll;
2820   PetscInt            depth;
2821 
2822   PetscFunctionBegin;
2823   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2824   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2825   PetscValidPointer(expandedPoints, 3);
2826   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2827   *expandedPoints = expandedPointsAll[0];
2828   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2829   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2830   PetscFunctionReturn(0);
2831 }
2832 
2833 /*@
2834   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).
2835 
2836   Not collective
2837 
2838   Input Parameters:
2839 + dm - The DMPlex
2840 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2841 
2842   Output Parameters:
2843 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2844 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2845 - sections - (optional) An array of sections which describe mappings from points to their cone points
2846 
2847   Level: advanced
2848 
2849   Notes:
2850   Like DMPlexGetConeTuple() but recursive.
2851 
2852   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.
2853   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2854 
2855   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:
2856   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2857   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2858 
2859 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2860 @*/
2861 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2862 {
2863   const PetscInt      *arr0=NULL, *cone=NULL;
2864   PetscInt            *arr=NULL, *newarr=NULL;
2865   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
2866   IS                  *expandedPoints_;
2867   PetscSection        *sections_;
2868 
2869   PetscFunctionBegin;
2870   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2871   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2872   if (depth) PetscValidIntPointer(depth, 3);
2873   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2874   if (sections) PetscValidPointer(sections, 5);
2875   PetscCall(ISGetLocalSize(points, &n));
2876   PetscCall(ISGetIndices(points, &arr0));
2877   PetscCall(DMPlexGetDepth(dm, &depth_));
2878   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2879   PetscCall(PetscCalloc1(depth_, &sections_));
2880   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
2881   for (d=depth_-1; d>=0; d--) {
2882     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2883     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2884     for (i=0; i<n; i++) {
2885       PetscCall(DMPlexGetDepthStratum(dm, d+1, &start, &end));
2886       if (arr[i] >= start && arr[i] < end) {
2887         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2888         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2889       } else {
2890         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2891       }
2892     }
2893     PetscCall(PetscSectionSetUp(sections_[d]));
2894     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2895     PetscCall(PetscMalloc1(newn, &newarr));
2896     for (i=0; i<n; i++) {
2897       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2898       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2899       if (cn > 1) {
2900         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2901         PetscCall(PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt)));
2902       } else {
2903         newarr[co] = arr[i];
2904       }
2905     }
2906     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2907     arr = newarr;
2908     n = newn;
2909   }
2910   PetscCall(ISRestoreIndices(points, &arr0));
2911   *depth = depth_;
2912   if (expandedPoints) *expandedPoints = expandedPoints_;
2913   else {
2914     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2915     PetscCall(PetscFree(expandedPoints_));
2916   }
2917   if (sections) *sections = sections_;
2918   else {
2919     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2920     PetscCall(PetscFree(sections_));
2921   }
2922   PetscFunctionReturn(0);
2923 }
2924 
2925 /*@
2926   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2927 
2928   Not collective
2929 
2930   Input Parameters:
2931 + dm - The DMPlex
2932 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2933 
2934   Output Parameters:
2935 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2936 . expandedPoints - (optional) An array of recursively expanded cones
2937 - sections - (optional) An array of sections which describe mappings from points to their cone points
2938 
2939   Level: advanced
2940 
2941   Notes:
2942   See DMPlexGetConeRecursive() for details.
2943 
2944 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2945 @*/
2946 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2947 {
2948   PetscInt            d, depth_;
2949 
2950   PetscFunctionBegin;
2951   PetscCall(DMPlexGetDepth(dm, &depth_));
2952   PetscCheck(!depth || *depth == depth_,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2953   if (depth) *depth = 0;
2954   if (expandedPoints) {
2955     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2956     PetscCall(PetscFree(*expandedPoints));
2957   }
2958   if (sections)  {
2959     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2960     PetscCall(PetscFree(*sections));
2961   }
2962   PetscFunctionReturn(0);
2963 }
2964 
2965 /*@
2966   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
2967 
2968   Not collective
2969 
2970   Input Parameters:
2971 + mesh - The DMPlex
2972 . p - The point, which must lie in the chart set with DMPlexSetChart()
2973 - cone - An array of points which are on the in-edges for point p
2974 
2975   Output Parameter:
2976 
2977   Note:
2978   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
2979 
2980   Level: beginner
2981 
2982 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
2983 @*/
2984 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
2985 {
2986   DM_Plex       *mesh = (DM_Plex*) dm->data;
2987   PetscInt       pStart, pEnd;
2988   PetscInt       dof, off, c;
2989 
2990   PetscFunctionBegin;
2991   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2992   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
2993   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
2994   if (dof) PetscValidIntPointer(cone, 3);
2995   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2996   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
2997   for (c = 0; c < dof; ++c) {
2998     PetscCheck(!(cone[c] < pStart) && !(cone[c] >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", cone[c], pStart, pEnd);
2999     mesh->cones[off+c] = cone[c];
3000   }
3001   PetscFunctionReturn(0);
3002 }
3003 
3004 /*@C
3005   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3006 
3007   Not collective
3008 
3009   Input Parameters:
3010 + mesh - The DMPlex
3011 - p - The point, which must lie in the chart set with DMPlexSetChart()
3012 
3013   Output Parameter:
3014 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3015                     integer giving the prescription for cone traversal.
3016 
3017   Level: beginner
3018 
3019   Notes:
3020   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3021   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3022   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3023   with the identity.
3024 
3025   Fortran Notes:
3026   Since it returns an array, this routine is only available in Fortran 90, and you must
3027   include petsc.h90 in your code.
3028   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3029   DMPlexRestoreConeOrientation() is not needed/available in C.
3030 
3031 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3032 @*/
3033 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3034 {
3035   DM_Plex       *mesh = (DM_Plex*) dm->data;
3036   PetscInt       off;
3037 
3038   PetscFunctionBegin;
3039   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3040   if (PetscDefined(USE_DEBUG)) {
3041     PetscInt dof;
3042     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3043     if (dof) PetscValidPointer(coneOrientation, 3);
3044   }
3045   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3046 
3047   *coneOrientation = &mesh->coneOrientations[off];
3048   PetscFunctionReturn(0);
3049 }
3050 
3051 /*@
3052   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3053 
3054   Not collective
3055 
3056   Input Parameters:
3057 + mesh - The DMPlex
3058 . p - The point, which must lie in the chart set with DMPlexSetChart()
3059 - coneOrientation - An array of orientations
3060   Output Parameter:
3061 
3062   Notes:
3063   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3064 
3065   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3066 
3067   Level: beginner
3068 
3069 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3070 @*/
3071 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3072 {
3073   DM_Plex       *mesh = (DM_Plex*) dm->data;
3074   PetscInt       pStart, pEnd;
3075   PetscInt       dof, off, c;
3076 
3077   PetscFunctionBegin;
3078   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3079   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3080   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3081   if (dof) PetscValidIntPointer(coneOrientation, 3);
3082   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3083   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3084   for (c = 0; c < dof; ++c) {
3085     PetscInt cdof, o = coneOrientation[c];
3086 
3087     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof));
3088     PetscCheck(!o || (o >= -(cdof+1) && o < cdof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ". %" PetscInt_FMT ")", o, -(cdof+1), cdof);
3089     mesh->coneOrientations[off+c] = o;
3090   }
3091   PetscFunctionReturn(0);
3092 }
3093 
3094 /*@
3095   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3096 
3097   Not collective
3098 
3099   Input Parameters:
3100 + mesh - The DMPlex
3101 . p - The point, which must lie in the chart set with DMPlexSetChart()
3102 . conePos - The local index in the cone where the point should be put
3103 - conePoint - The mesh point to insert
3104 
3105   Level: beginner
3106 
3107 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3108 @*/
3109 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3110 {
3111   DM_Plex       *mesh = (DM_Plex*) dm->data;
3112   PetscInt       pStart, pEnd;
3113   PetscInt       dof, off;
3114 
3115   PetscFunctionBegin;
3116   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3117   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3118   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3119   PetscCheck(!(conePoint < pStart) && !(conePoint >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", conePoint, pStart, pEnd);
3120   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3121   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3122   PetscCheck(!(conePos < 0) && !(conePos >= dof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3123   mesh->cones[off+conePos] = conePoint;
3124   PetscFunctionReturn(0);
3125 }
3126 
3127 /*@
3128   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3129 
3130   Not collective
3131 
3132   Input Parameters:
3133 + mesh - The DMPlex
3134 . p - The point, which must lie in the chart set with DMPlexSetChart()
3135 . conePos - The local index in the cone where the point should be put
3136 - coneOrientation - The point orientation to insert
3137 
3138   Level: beginner
3139 
3140   Notes:
3141   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3142 
3143 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3144 @*/
3145 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3146 {
3147   DM_Plex       *mesh = (DM_Plex*) dm->data;
3148   PetscInt       pStart, pEnd;
3149   PetscInt       dof, off;
3150 
3151   PetscFunctionBegin;
3152   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3153   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3154   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3155   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3156   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3157   PetscCheck(!(conePos < 0) && !(conePos >= dof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3158   mesh->coneOrientations[off+conePos] = coneOrientation;
3159   PetscFunctionReturn(0);
3160 }
3161 
3162 /*@
3163   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3164 
3165   Not collective
3166 
3167   Input Parameters:
3168 + mesh - The DMPlex
3169 - p - The point, which must lie in the chart set with DMPlexSetChart()
3170 
3171   Output Parameter:
3172 . size - The support size for point p
3173 
3174   Level: beginner
3175 
3176 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3177 @*/
3178 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3179 {
3180   DM_Plex       *mesh = (DM_Plex*) dm->data;
3181 
3182   PetscFunctionBegin;
3183   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3184   PetscValidIntPointer(size, 3);
3185   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3186   PetscFunctionReturn(0);
3187 }
3188 
3189 /*@
3190   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3191 
3192   Not collective
3193 
3194   Input Parameters:
3195 + mesh - The DMPlex
3196 . p - The point, which must lie in the chart set with DMPlexSetChart()
3197 - size - The support size for point p
3198 
3199   Output Parameter:
3200 
3201   Note:
3202   This should be called after DMPlexSetChart().
3203 
3204   Level: beginner
3205 
3206 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3207 @*/
3208 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3209 {
3210   DM_Plex       *mesh = (DM_Plex*) dm->data;
3211 
3212   PetscFunctionBegin;
3213   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3214   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3215   PetscFunctionReturn(0);
3216 }
3217 
3218 /*@C
3219   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3220 
3221   Not collective
3222 
3223   Input Parameters:
3224 + mesh - The DMPlex
3225 - p - The point, which must lie in the chart set with DMPlexSetChart()
3226 
3227   Output Parameter:
3228 . support - An array of points which are on the out-edges for point p
3229 
3230   Level: beginner
3231 
3232   Fortran Notes:
3233   Since it returns an array, this routine is only available in Fortran 90, and you must
3234   include petsc.h90 in your code.
3235   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3236   DMPlexRestoreSupport() is not needed/available in C.
3237 
3238 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3239 @*/
3240 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3241 {
3242   DM_Plex       *mesh = (DM_Plex*) dm->data;
3243   PetscInt       off;
3244 
3245   PetscFunctionBegin;
3246   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3247   PetscValidPointer(support, 3);
3248   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3249   *support = &mesh->supports[off];
3250   PetscFunctionReturn(0);
3251 }
3252 
3253 /*@
3254   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3255 
3256   Not collective
3257 
3258   Input Parameters:
3259 + mesh - The DMPlex
3260 . p - The point, which must lie in the chart set with DMPlexSetChart()
3261 - support - An array of points which are on the out-edges for point p
3262 
3263   Output Parameter:
3264 
3265   Note:
3266   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3267 
3268   Level: beginner
3269 
3270 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3271 @*/
3272 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3273 {
3274   DM_Plex       *mesh = (DM_Plex*) dm->data;
3275   PetscInt       pStart, pEnd;
3276   PetscInt       dof, off, c;
3277 
3278   PetscFunctionBegin;
3279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3280   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3281   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3282   if (dof) PetscValidIntPointer(support, 3);
3283   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3284   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3285   for (c = 0; c < dof; ++c) {
3286     PetscCheck(!(support[c] < pStart) && !(support[c] >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", support[c], pStart, pEnd);
3287     mesh->supports[off+c] = support[c];
3288   }
3289   PetscFunctionReturn(0);
3290 }
3291 
3292 /*@
3293   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3294 
3295   Not collective
3296 
3297   Input Parameters:
3298 + mesh - The DMPlex
3299 . p - The point, which must lie in the chart set with DMPlexSetChart()
3300 . supportPos - The local index in the cone where the point should be put
3301 - supportPoint - The mesh point to insert
3302 
3303   Level: beginner
3304 
3305 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3306 @*/
3307 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3308 {
3309   DM_Plex       *mesh = (DM_Plex*) dm->data;
3310   PetscInt       pStart, pEnd;
3311   PetscInt       dof, off;
3312 
3313   PetscFunctionBegin;
3314   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3315   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3316   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3317   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3318   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3319   PetscCheck(!(supportPoint < pStart) && !(supportPoint >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", supportPoint, pStart, pEnd);
3320   PetscCheck(supportPos < dof,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", supportPos, p, dof);
3321   mesh->supports[off+supportPos] = supportPoint;
3322   PetscFunctionReturn(0);
3323 }
3324 
3325 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3326 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3327 {
3328   switch (ct) {
3329     case DM_POLYTOPE_SEGMENT:
3330       if (o == -1) return -2;
3331       break;
3332     case DM_POLYTOPE_TRIANGLE:
3333       if (o == -3) return -1;
3334       if (o == -2) return -3;
3335       if (o == -1) return -2;
3336       break;
3337     case DM_POLYTOPE_QUADRILATERAL:
3338       if (o == -4) return -2;
3339       if (o == -3) return -1;
3340       if (o == -2) return -4;
3341       if (o == -1) return -3;
3342       break;
3343     default: return o;
3344   }
3345   return o;
3346 }
3347 
3348 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3349 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3350 {
3351   switch (ct) {
3352     case DM_POLYTOPE_SEGMENT:
3353       if ((o == -2) || (o == 1)) return -1;
3354       if (o == -1) return 0;
3355       break;
3356     case DM_POLYTOPE_TRIANGLE:
3357       if (o == -3) return -2;
3358       if (o == -2) return -1;
3359       if (o == -1) return -3;
3360       break;
3361     case DM_POLYTOPE_QUADRILATERAL:
3362       if (o == -4) return -2;
3363       if (o == -3) return -1;
3364       if (o == -2) return -4;
3365       if (o == -1) return -3;
3366       break;
3367     default: return o;
3368   }
3369   return o;
3370 }
3371 
3372 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3373 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3374 {
3375   PetscInt       pStart, pEnd, p;
3376 
3377   PetscFunctionBegin;
3378   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3379   for (p = pStart; p < pEnd; ++p) {
3380     const PetscInt *cone, *ornt;
3381     PetscInt        coneSize, c;
3382 
3383     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3384     PetscCall(DMPlexGetCone(dm, p, &cone));
3385     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3386     for (c = 0; c < coneSize; ++c) {
3387       DMPolytopeType ct;
3388       const PetscInt o = ornt[c];
3389 
3390       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3391       switch (ct) {
3392         case DM_POLYTOPE_SEGMENT:
3393           if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3394           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3395           break;
3396         case DM_POLYTOPE_TRIANGLE:
3397           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3398           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3399           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3400           break;
3401         case DM_POLYTOPE_QUADRILATERAL:
3402           if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3403           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3404           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3405           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3406           break;
3407         default: break;
3408       }
3409     }
3410   }
3411   PetscFunctionReturn(0);
3412 }
3413 
3414 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3415 {
3416   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3417   PetscInt       *closure;
3418   const PetscInt *tmp = NULL, *tmpO = NULL;
3419   PetscInt        off = 0, tmpSize, t;
3420 
3421   PetscFunctionBeginHot;
3422   if (ornt) {
3423     PetscCall(DMPlexGetCellType(dm, p, &ct));
3424     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3425   }
3426   if (*points) {
3427     closure = *points;
3428   } else {
3429     PetscInt maxConeSize, maxSupportSize;
3430     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3431     PetscCall(DMGetWorkArray(dm, 2*(PetscMax(maxConeSize, maxSupportSize)+1), MPIU_INT, &closure));
3432   }
3433   if (useCone) {
3434     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3435     PetscCall(DMPlexGetCone(dm, p, &tmp));
3436     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3437   } else {
3438     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3439     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3440   }
3441   if (ct == DM_POLYTOPE_UNKNOWN) {
3442     closure[off++] = p;
3443     closure[off++] = 0;
3444     for (t = 0; t < tmpSize; ++t) {
3445       closure[off++] = tmp[t];
3446       closure[off++] = tmpO ? tmpO[t] : 0;
3447     }
3448   } else {
3449     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3450 
3451     /* We assume that cells with a valid type have faces with a valid type */
3452     closure[off++] = p;
3453     closure[off++] = ornt;
3454     for (t = 0; t < tmpSize; ++t) {
3455       DMPolytopeType ft;
3456 
3457       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3458       closure[off++] = tmp[arr[t]];
3459       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3460     }
3461   }
3462   if (numPoints) *numPoints = tmpSize+1;
3463   if (points)    *points    = closure;
3464   PetscFunctionReturn(0);
3465 }
3466 
3467 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3468 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3469 {
3470   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3471   const PetscInt *cone, *ornt;
3472   PetscInt       *pts,  *closure = NULL;
3473   DMPolytopeType  ft;
3474   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3475   PetscInt        dim, coneSize, c, d, clSize, cl;
3476 
3477   PetscFunctionBeginHot;
3478   PetscCall(DMGetDimension(dm, &dim));
3479   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3480   PetscCall(DMPlexGetCone(dm, point, &cone));
3481   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3482   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3483   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    dim+1)-1)/(maxConeSize-1))    : dim+1;
3484   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim+1)-1)/(maxSupportSize-1)) : dim+1;
3485   maxSize       = PetscMax(coneSeries, supportSeries);
3486   if (*points) {pts  = *points;}
3487   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &pts));
3488   c    = 0;
3489   pts[c++] = point;
3490   pts[c++] = o;
3491   PetscCall(DMPlexGetCellType(dm, cone[arr[0*2+0]], &ft));
3492   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[0*2+1], ornt[0]), useCone, &clSize, &closure));
3493   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3494   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[1*2+1], ornt[1]), useCone, &clSize, &closure));
3495   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3496   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3497   for (d = 2; d < coneSize; ++d) {
3498     PetscCall(DMPlexGetCellType(dm, cone[arr[d*2+0]], &ft));
3499     pts[c++] = cone[arr[d*2+0]];
3500     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]);
3501   }
3502   if (dim >= 3) {
3503     for (d = 2; d < coneSize; ++d) {
3504       const PetscInt  fpoint = cone[arr[d*2+0]];
3505       const PetscInt *fcone, *fornt;
3506       PetscInt        fconeSize, fc, i;
3507 
3508       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3509       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]));
3510       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3511       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3512       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3513       for (fc = 0; fc < fconeSize; ++fc) {
3514         const PetscInt cp = fcone[farr[fc*2+0]];
3515         const PetscInt co = farr[fc*2+1];
3516 
3517         for (i = 0; i < c; i += 2) if (pts[i] == cp) break;
3518         if (i == c) {
3519           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3520           pts[c++] = cp;
3521           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc*2+0]]);
3522         }
3523       }
3524     }
3525   }
3526   *numPoints = c/2;
3527   *points    = pts;
3528   PetscFunctionReturn(0);
3529 }
3530 
3531 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3532 {
3533   DMPolytopeType ct;
3534   PetscInt      *closure, *fifo;
3535   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3536   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3537   PetscInt       depth, maxSize;
3538 
3539   PetscFunctionBeginHot;
3540   PetscCall(DMPlexGetDepth(dm, &depth));
3541   if (depth == 1) {
3542     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3543     PetscFunctionReturn(0);
3544   }
3545   PetscCall(DMPlexGetCellType(dm, p, &ct));
3546   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3547   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3548     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3549     PetscFunctionReturn(0);
3550   }
3551   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3552   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    depth+1)-1)/(maxConeSize-1))    : depth+1;
3553   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth+1)-1)/(maxSupportSize-1)) : depth+1;
3554   maxSize       = PetscMax(coneSeries, supportSeries);
3555   PetscCall(DMGetWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3556   if (*points) {closure = *points;}
3557   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &closure));
3558   closure[closureSize++] = p;
3559   closure[closureSize++] = ornt;
3560   fifo[fifoSize++]       = p;
3561   fifo[fifoSize++]       = ornt;
3562   fifo[fifoSize++]       = ct;
3563   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3564   while (fifoSize - fifoStart) {
3565     const PetscInt       q    = fifo[fifoStart++];
3566     const PetscInt       o    = fifo[fifoStart++];
3567     const DMPolytopeType qt   = (DMPolytopeType) fifo[fifoStart++];
3568     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3569     const PetscInt      *tmp, *tmpO;
3570     PetscInt             tmpSize, t;
3571 
3572     if (PetscDefined(USE_DEBUG)) {
3573       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt)/2;
3574       PetscCheck(!o || !(o >= nO || o < -nO),PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid orientation %" PetscInt_FMT " not in [%" PetscInt_FMT ",%" PetscInt_FMT ") for %s %" PetscInt_FMT, o, -nO, nO, DMPolytopeTypes[qt], q);
3575     }
3576     if (useCone) {
3577       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3578       PetscCall(DMPlexGetCone(dm, q, &tmp));
3579       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3580     } else {
3581       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3582       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3583       tmpO = NULL;
3584     }
3585     for (t = 0; t < tmpSize; ++t) {
3586       const PetscInt ip = useCone && qarr ? qarr[t*2]   : t;
3587       const PetscInt io = useCone && qarr ? qarr[t*2+1] : 0;
3588       const PetscInt cp = tmp[ip];
3589       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3590       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3591       PetscInt       c;
3592 
3593       /* Check for duplicate */
3594       for (c = 0; c < closureSize; c += 2) {
3595         if (closure[c] == cp) break;
3596       }
3597       if (c == closureSize) {
3598         closure[closureSize++] = cp;
3599         closure[closureSize++] = co;
3600         fifo[fifoSize++]       = cp;
3601         fifo[fifoSize++]       = co;
3602         fifo[fifoSize++]       = ct;
3603       }
3604     }
3605   }
3606   PetscCall(DMRestoreWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3607   if (numPoints) *numPoints = closureSize/2;
3608   if (points)    *points    = closure;
3609   PetscFunctionReturn(0);
3610 }
3611 
3612 /*@C
3613   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3614 
3615   Not collective
3616 
3617   Input Parameters:
3618 + dm      - The DMPlex
3619 . p       - The mesh point
3620 - useCone - PETSC_TRUE for the closure, otherwise return the star
3621 
3622   Input/Output Parameter:
3623 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3624            if NULL on input, internal storage will be returned, otherwise the provided array is used
3625 
3626   Output Parameter:
3627 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3628 
3629   Note:
3630   If using internal storage (points is NULL on input), each call overwrites the last output.
3631 
3632   Fortran Notes:
3633   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3634 
3635   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3636 
3637   Level: beginner
3638 
3639 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3640 @*/
3641 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3642 {
3643   PetscFunctionBeginHot;
3644   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3645   if (numPoints) PetscValidIntPointer(numPoints, 4);
3646   if (points)    PetscValidPointer(points, 5);
3647   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3648   PetscFunctionReturn(0);
3649 }
3650 
3651 /*@C
3652   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3653 
3654   Not collective
3655 
3656   Input Parameters:
3657 + dm        - The DMPlex
3658 . p         - The mesh point
3659 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3660 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3661 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3662 
3663   Note:
3664   If not using internal storage (points is not NULL on input), this call is unnecessary
3665 
3666   Fortran Notes:
3667   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3668 
3669   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3670 
3671   Level: beginner
3672 
3673 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3674 @*/
3675 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3676 {
3677   PetscFunctionBeginHot;
3678   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3679   if (numPoints) *numPoints = 0;
3680   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3681   PetscFunctionReturn(0);
3682 }
3683 
3684 /*@
3685   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3686 
3687   Not collective
3688 
3689   Input Parameter:
3690 . mesh - The DMPlex
3691 
3692   Output Parameters:
3693 + maxConeSize - The maximum number of in-edges
3694 - maxSupportSize - The maximum number of out-edges
3695 
3696   Level: beginner
3697 
3698 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3699 @*/
3700 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3701 {
3702   DM_Plex *mesh = (DM_Plex*) dm->data;
3703 
3704   PetscFunctionBegin;
3705   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3706   if (maxConeSize) {
3707     PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3708   }
3709   if (maxSupportSize) {
3710     PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3711   }
3712   PetscFunctionReturn(0);
3713 }
3714 
3715 PetscErrorCode DMSetUp_Plex(DM dm)
3716 {
3717   DM_Plex       *mesh = (DM_Plex*) dm->data;
3718   PetscInt       size, maxSupportSize;
3719 
3720   PetscFunctionBegin;
3721   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3722   PetscCall(PetscSectionSetUp(mesh->coneSection));
3723   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3724   PetscCall(PetscMalloc1(size, &mesh->cones));
3725   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3726   PetscCall(PetscLogObjectMemory((PetscObject) dm, size*2*sizeof(PetscInt)));
3727   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3728   if (maxSupportSize) {
3729     PetscCall(PetscSectionSetUp(mesh->supportSection));
3730     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3731     PetscCall(PetscMalloc1(size, &mesh->supports));
3732     PetscCall(PetscLogObjectMemory((PetscObject) dm, size*sizeof(PetscInt)));
3733   }
3734   PetscFunctionReturn(0);
3735 }
3736 
3737 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3738 {
3739   PetscFunctionBegin;
3740   if (subdm) PetscCall(DMClone(dm, subdm));
3741   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3742   if (subdm) {(*subdm)->useNatural = dm->useNatural;}
3743   if (dm->useNatural && dm->sfMigration) {
3744     PetscSF        sfMigrationInv,sfNatural;
3745     PetscSection   section, sectionSeq;
3746 
3747     (*subdm)->sfMigration = dm->sfMigration;
3748     PetscCall(PetscObjectReference((PetscObject) dm->sfMigration));
3749     PetscCall(DMGetLocalSection((*subdm), &section));
3750     PetscCall(PetscSFCreateInverseSF((*subdm)->sfMigration, &sfMigrationInv));
3751     PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*subdm)), &sectionSeq));
3752     PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3753 
3754     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, sectionSeq, (*subdm)->sfMigration, &sfNatural));
3755     (*subdm)->sfNatural = sfNatural;
3756     PetscCall(PetscSectionDestroy(&sectionSeq));
3757     PetscCall(PetscSFDestroy(&sfMigrationInv));
3758   }
3759   PetscFunctionReturn(0);
3760 }
3761 
3762 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3763 {
3764   PetscInt       i = 0;
3765 
3766   PetscFunctionBegin;
3767   PetscCall(DMClone(dms[0], superdm));
3768   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3769   (*superdm)->useNatural = PETSC_FALSE;
3770   for (i = 0; i < len; i++) {
3771     if (dms[i]->useNatural && dms[i]->sfMigration) {
3772       PetscSF        sfMigrationInv,sfNatural;
3773       PetscSection   section, sectionSeq;
3774 
3775       (*superdm)->sfMigration = dms[i]->sfMigration;
3776       PetscCall(PetscObjectReference((PetscObject) dms[i]->sfMigration));
3777       (*superdm)->useNatural = PETSC_TRUE;
3778       PetscCall(DMGetLocalSection((*superdm), &section));
3779       PetscCall(PetscSFCreateInverseSF((*superdm)->sfMigration, &sfMigrationInv));
3780       PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*superdm)), &sectionSeq));
3781       PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3782 
3783       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, sectionSeq, (*superdm)->sfMigration, &sfNatural));
3784       (*superdm)->sfNatural = sfNatural;
3785       PetscCall(PetscSectionDestroy(&sectionSeq));
3786       PetscCall(PetscSFDestroy(&sfMigrationInv));
3787       break;
3788     }
3789   }
3790   PetscFunctionReturn(0);
3791 }
3792 
3793 /*@
3794   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3795 
3796   Not collective
3797 
3798   Input Parameter:
3799 . mesh - The DMPlex
3800 
3801   Output Parameter:
3802 
3803   Note:
3804   This should be called after all calls to DMPlexSetCone()
3805 
3806   Level: beginner
3807 
3808 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3809 @*/
3810 PetscErrorCode DMPlexSymmetrize(DM dm)
3811 {
3812   DM_Plex       *mesh = (DM_Plex*) dm->data;
3813   PetscInt      *offsets;
3814   PetscInt       supportSize;
3815   PetscInt       pStart, pEnd, p;
3816 
3817   PetscFunctionBegin;
3818   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3819   PetscCheck(!mesh->supports,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3820   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0));
3821   /* Calculate support sizes */
3822   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3823   for (p = pStart; p < pEnd; ++p) {
3824     PetscInt dof, off, c;
3825 
3826     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3827     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3828     for (c = off; c < off+dof; ++c) {
3829       PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3830     }
3831   }
3832   PetscCall(PetscSectionSetUp(mesh->supportSection));
3833   /* Calculate supports */
3834   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3835   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3836   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3837   for (p = pStart; p < pEnd; ++p) {
3838     PetscInt dof, off, c;
3839 
3840     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3841     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3842     for (c = off; c < off+dof; ++c) {
3843       const PetscInt q = mesh->cones[c];
3844       PetscInt       offS;
3845 
3846       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3847 
3848       mesh->supports[offS+offsets[q]] = p;
3849       ++offsets[q];
3850     }
3851   }
3852   PetscCall(PetscFree(offsets));
3853   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0));
3854   PetscFunctionReturn(0);
3855 }
3856 
3857 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3858 {
3859   IS             stratumIS;
3860 
3861   PetscFunctionBegin;
3862   if (pStart >= pEnd) PetscFunctionReturn(0);
3863   if (PetscDefined(USE_DEBUG)) {
3864     PetscInt  qStart, qEnd, numLevels, level;
3865     PetscBool overlap = PETSC_FALSE;
3866     PetscCall(DMLabelGetNumValues(label, &numLevels));
3867     for (level = 0; level < numLevels; level++) {
3868       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3869       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
3870     }
3871     PetscCheck(!overlap,PETSC_COMM_SELF, PETSC_ERR_PLIB, "New depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ") overlaps with depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ")", depth, pStart, pEnd, level, qStart, qEnd);
3872   }
3873   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS));
3874   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3875   PetscCall(ISDestroy(&stratumIS));
3876   PetscFunctionReturn(0);
3877 }
3878 
3879 /*@
3880   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3881   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3882   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3883   the DAG.
3884 
3885   Collective on dm
3886 
3887   Input Parameter:
3888 . mesh - The DMPlex
3889 
3890   Output Parameter:
3891 
3892   Notes:
3893   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3894   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3895   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3896   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3897   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3898 
3899   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3900   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3901   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
3902   to interpolate only that one (e0), so that
3903 $  cone(c0) = {e0, v2}
3904 $  cone(e0) = {v0, v1}
3905   If DMPlexStratify() is run on this mesh, it will give depths
3906 $  depth 0 = {v0, v1, v2}
3907 $  depth 1 = {e0, c0}
3908   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3909 
3910   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3911 
3912   Level: beginner
3913 
3914 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3915 @*/
3916 PetscErrorCode DMPlexStratify(DM dm)
3917 {
3918   DM_Plex       *mesh = (DM_Plex*) dm->data;
3919   DMLabel        label;
3920   PetscInt       pStart, pEnd, p;
3921   PetscInt       numRoots = 0, numLeaves = 0;
3922 
3923   PetscFunctionBegin;
3924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3925   PetscCall(PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0));
3926 
3927   /* Create depth label */
3928   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3929   PetscCall(DMCreateLabel(dm, "depth"));
3930   PetscCall(DMPlexGetDepthLabel(dm, &label));
3931 
3932   {
3933     /* Initialize roots and count leaves */
3934     PetscInt sMin = PETSC_MAX_INT;
3935     PetscInt sMax = PETSC_MIN_INT;
3936     PetscInt coneSize, supportSize;
3937 
3938     for (p = pStart; p < pEnd; ++p) {
3939       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3940       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3941       if (!coneSize && supportSize) {
3942         sMin = PetscMin(p, sMin);
3943         sMax = PetscMax(p, sMax);
3944         ++numRoots;
3945       } else if (!supportSize && coneSize) {
3946         ++numLeaves;
3947       } else if (!supportSize && !coneSize) {
3948         /* Isolated points */
3949         sMin = PetscMin(p, sMin);
3950         sMax = PetscMax(p, sMax);
3951       }
3952     }
3953     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1));
3954   }
3955 
3956   if (numRoots + numLeaves == (pEnd - pStart)) {
3957     PetscInt sMin = PETSC_MAX_INT;
3958     PetscInt sMax = PETSC_MIN_INT;
3959     PetscInt coneSize, supportSize;
3960 
3961     for (p = pStart; p < pEnd; ++p) {
3962       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3963       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3964       if (!supportSize && coneSize) {
3965         sMin = PetscMin(p, sMin);
3966         sMax = PetscMax(p, sMax);
3967       }
3968     }
3969     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1));
3970   } else {
3971     PetscInt level = 0;
3972     PetscInt qStart, qEnd, q;
3973 
3974     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3975     while (qEnd > qStart) {
3976       PetscInt sMin = PETSC_MAX_INT;
3977       PetscInt sMax = PETSC_MIN_INT;
3978 
3979       for (q = qStart; q < qEnd; ++q) {
3980         const PetscInt *support;
3981         PetscInt        supportSize, s;
3982 
3983         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
3984         PetscCall(DMPlexGetSupport(dm, q, &support));
3985         for (s = 0; s < supportSize; ++s) {
3986           sMin = PetscMin(support[s], sMin);
3987           sMax = PetscMax(support[s], sMax);
3988         }
3989       }
3990       PetscCall(DMLabelGetNumValues(label, &level));
3991       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1));
3992       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3993     }
3994   }
3995   { /* just in case there is an empty process */
3996     PetscInt numValues, maxValues = 0, v;
3997 
3998     PetscCall(DMLabelGetNumValues(label, &numValues));
3999     PetscCallMPI(MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm)));
4000     for (v = numValues; v < maxValues; v++) {
4001       PetscCall(DMLabelAddStratum(label, v));
4002     }
4003   }
4004   PetscCall(PetscObjectStateGet((PetscObject) label, &mesh->depthState));
4005   PetscCall(PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0));
4006   PetscFunctionReturn(0);
4007 }
4008 
4009 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4010 {
4011   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4012   PetscInt       dim, depth, pheight, coneSize;
4013 
4014   PetscFunctionBeginHot;
4015   PetscCall(DMGetDimension(dm, &dim));
4016   PetscCall(DMPlexGetDepth(dm, &depth));
4017   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4018   pheight = depth - pdepth;
4019   if (depth <= 1) {
4020     switch (pdepth) {
4021       case 0: ct = DM_POLYTOPE_POINT;break;
4022       case 1:
4023         switch (coneSize) {
4024           case 2: ct = DM_POLYTOPE_SEGMENT;break;
4025           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4026           case 4:
4027           switch (dim) {
4028             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
4029             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
4030             default: break;
4031           }
4032           break;
4033         case 5: ct = DM_POLYTOPE_PYRAMID;break;
4034         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4035         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
4036         default: break;
4037       }
4038     }
4039   } else {
4040     if (pdepth == 0) {
4041       ct = DM_POLYTOPE_POINT;
4042     } else if (pheight == 0) {
4043       switch (dim) {
4044         case 1:
4045           switch (coneSize) {
4046             case 2: ct = DM_POLYTOPE_SEGMENT;break;
4047             default: break;
4048           }
4049           break;
4050         case 2:
4051           switch (coneSize) {
4052             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4053             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4054             default: break;
4055           }
4056           break;
4057         case 3:
4058           switch (coneSize) {
4059             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
4060             case 5:
4061             {
4062               const PetscInt *cone;
4063               PetscInt        faceConeSize;
4064 
4065               PetscCall(DMPlexGetCone(dm, p, &cone));
4066               PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4067               switch (faceConeSize) {
4068                 case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4069                 case 4: ct = DM_POLYTOPE_PYRAMID;break;
4070               }
4071             }
4072             break;
4073             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
4074             default: break;
4075           }
4076           break;
4077         default: break;
4078       }
4079     } else if (pheight > 0) {
4080       switch (coneSize) {
4081         case 2: ct = DM_POLYTOPE_SEGMENT;break;
4082         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4083         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4084         default: break;
4085       }
4086     }
4087   }
4088   *pt = ct;
4089   PetscFunctionReturn(0);
4090 }
4091 
4092 /*@
4093   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4094 
4095   Collective on dm
4096 
4097   Input Parameter:
4098 . mesh - The DMPlex
4099 
4100   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4101 
4102   Level: developer
4103 
4104   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4105   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4106   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4107 
4108 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4109 @*/
4110 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4111 {
4112   DM_Plex       *mesh;
4113   DMLabel        ctLabel;
4114   PetscInt       pStart, pEnd, p;
4115 
4116   PetscFunctionBegin;
4117   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4118   mesh = (DM_Plex *) dm->data;
4119   PetscCall(DMCreateLabel(dm, "celltype"));
4120   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4121   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4122   for (p = pStart; p < pEnd; ++p) {
4123     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4124     PetscInt       pdepth;
4125 
4126     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4127     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4128     PetscCheck(ct != DM_POLYTOPE_UNKNOWN,PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4129     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4130   }
4131   PetscCall(PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState));
4132   PetscCall(PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view"));
4133   PetscFunctionReturn(0);
4134 }
4135 
4136 /*@C
4137   DMPlexGetJoin - Get an array for the join of the set of points
4138 
4139   Not Collective
4140 
4141   Input Parameters:
4142 + dm - The DMPlex object
4143 . numPoints - The number of input points for the join
4144 - points - The input points
4145 
4146   Output Parameters:
4147 + numCoveredPoints - The number of points in the join
4148 - coveredPoints - The points in the join
4149 
4150   Level: intermediate
4151 
4152   Note: Currently, this is restricted to a single level join
4153 
4154   Fortran Notes:
4155   Since it returns an array, this routine is only available in Fortran 90, and you must
4156   include petsc.h90 in your code.
4157 
4158   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4159 
4160 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4161 @*/
4162 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4163 {
4164   DM_Plex       *mesh = (DM_Plex*) dm->data;
4165   PetscInt      *join[2];
4166   PetscInt       joinSize, i = 0;
4167   PetscInt       dof, off, p, c, m;
4168   PetscInt       maxSupportSize;
4169 
4170   PetscFunctionBegin;
4171   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4172   PetscValidIntPointer(points, 3);
4173   PetscValidIntPointer(numCoveredPoints, 4);
4174   PetscValidPointer(coveredPoints, 5);
4175   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4176   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4177   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4178   /* Copy in support of first point */
4179   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4180   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4181   for (joinSize = 0; joinSize < dof; ++joinSize) {
4182     join[i][joinSize] = mesh->supports[off+joinSize];
4183   }
4184   /* Check each successive support */
4185   for (p = 1; p < numPoints; ++p) {
4186     PetscInt newJoinSize = 0;
4187 
4188     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4189     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4190     for (c = 0; c < dof; ++c) {
4191       const PetscInt point = mesh->supports[off+c];
4192 
4193       for (m = 0; m < joinSize; ++m) {
4194         if (point == join[i][m]) {
4195           join[1-i][newJoinSize++] = point;
4196           break;
4197         }
4198       }
4199     }
4200     joinSize = newJoinSize;
4201     i        = 1-i;
4202   }
4203   *numCoveredPoints = joinSize;
4204   *coveredPoints    = join[i];
4205   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1-i]));
4206   PetscFunctionReturn(0);
4207 }
4208 
4209 /*@C
4210   DMPlexRestoreJoin - Restore an array for the join of the set of points
4211 
4212   Not Collective
4213 
4214   Input Parameters:
4215 + dm - The DMPlex object
4216 . numPoints - The number of input points for the join
4217 - points - The input points
4218 
4219   Output Parameters:
4220 + numCoveredPoints - The number of points in the join
4221 - coveredPoints - The points in the join
4222 
4223   Fortran Notes:
4224   Since it returns an array, this routine is only available in Fortran 90, and you must
4225   include petsc.h90 in your code.
4226 
4227   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4228 
4229   Level: intermediate
4230 
4231 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4232 @*/
4233 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4234 {
4235   PetscFunctionBegin;
4236   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4237   if (points) PetscValidIntPointer(points,3);
4238   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4239   PetscValidPointer(coveredPoints, 5);
4240   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4241   if (numCoveredPoints) *numCoveredPoints = 0;
4242   PetscFunctionReturn(0);
4243 }
4244 
4245 /*@C
4246   DMPlexGetFullJoin - Get an array for the join of the set of points
4247 
4248   Not Collective
4249 
4250   Input Parameters:
4251 + dm - The DMPlex object
4252 . numPoints - The number of input points for the join
4253 - points - The input points
4254 
4255   Output Parameters:
4256 + numCoveredPoints - The number of points in the join
4257 - coveredPoints - The points in the join
4258 
4259   Fortran Notes:
4260   Since it returns an array, this routine is only available in Fortran 90, and you must
4261   include petsc.h90 in your code.
4262 
4263   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4264 
4265   Level: intermediate
4266 
4267 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4268 @*/
4269 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4270 {
4271   PetscInt      *offsets, **closures;
4272   PetscInt      *join[2];
4273   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
4274   PetscInt       p, d, c, m, ms;
4275 
4276   PetscFunctionBegin;
4277   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4278   PetscValidIntPointer(points, 3);
4279   PetscValidIntPointer(numCoveredPoints, 4);
4280   PetscValidPointer(coveredPoints, 5);
4281 
4282   PetscCall(DMPlexGetDepth(dm, &depth));
4283   PetscCall(PetscCalloc1(numPoints, &closures));
4284   PetscCall(DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4285   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4286   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
4287   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4288   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4289 
4290   for (p = 0; p < numPoints; ++p) {
4291     PetscInt closureSize;
4292 
4293     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4294 
4295     offsets[p*(depth+2)+0] = 0;
4296     for (d = 0; d < depth+1; ++d) {
4297       PetscInt pStart, pEnd, i;
4298 
4299       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4300       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
4301         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4302           offsets[p*(depth+2)+d+1] = i;
4303           break;
4304         }
4305       }
4306       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
4307     }
4308     PetscCheck(offsets[p*(depth+2)+depth+1] == closureSize,PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p*(depth+2)+depth+1], closureSize);
4309   }
4310   for (d = 0; d < depth+1; ++d) {
4311     PetscInt dof;
4312 
4313     /* Copy in support of first point */
4314     dof = offsets[d+1] - offsets[d];
4315     for (joinSize = 0; joinSize < dof; ++joinSize) {
4316       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
4317     }
4318     /* Check each successive cone */
4319     for (p = 1; p < numPoints && joinSize; ++p) {
4320       PetscInt newJoinSize = 0;
4321 
4322       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
4323       for (c = 0; c < dof; ++c) {
4324         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
4325 
4326         for (m = 0; m < joinSize; ++m) {
4327           if (point == join[i][m]) {
4328             join[1-i][newJoinSize++] = point;
4329             break;
4330           }
4331         }
4332       }
4333       joinSize = newJoinSize;
4334       i        = 1-i;
4335     }
4336     if (joinSize) break;
4337   }
4338   *numCoveredPoints = joinSize;
4339   *coveredPoints    = join[i];
4340   for (p = 0; p < numPoints; ++p) {
4341     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4342   }
4343   PetscCall(PetscFree(closures));
4344   PetscCall(DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4345   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1-i]));
4346   PetscFunctionReturn(0);
4347 }
4348 
4349 /*@C
4350   DMPlexGetMeet - Get an array for the meet of the set of points
4351 
4352   Not Collective
4353 
4354   Input Parameters:
4355 + dm - The DMPlex object
4356 . numPoints - The number of input points for the meet
4357 - points - The input points
4358 
4359   Output Parameters:
4360 + numCoveredPoints - The number of points in the meet
4361 - coveredPoints - The points in the meet
4362 
4363   Level: intermediate
4364 
4365   Note: Currently, this is restricted to a single level meet
4366 
4367   Fortran Notes:
4368   Since it returns an array, this routine is only available in Fortran 90, and you must
4369   include petsc.h90 in your code.
4370 
4371   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4372 
4373 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4374 @*/
4375 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4376 {
4377   DM_Plex       *mesh = (DM_Plex*) dm->data;
4378   PetscInt      *meet[2];
4379   PetscInt       meetSize, i = 0;
4380   PetscInt       dof, off, p, c, m;
4381   PetscInt       maxConeSize;
4382 
4383   PetscFunctionBegin;
4384   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4385   PetscValidIntPointer(points, 3);
4386   PetscValidIntPointer(numCoveringPoints, 4);
4387   PetscValidPointer(coveringPoints, 5);
4388   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4389   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4390   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4391   /* Copy in cone of first point */
4392   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4393   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4394   for (meetSize = 0; meetSize < dof; ++meetSize) {
4395     meet[i][meetSize] = mesh->cones[off+meetSize];
4396   }
4397   /* Check each successive cone */
4398   for (p = 1; p < numPoints; ++p) {
4399     PetscInt newMeetSize = 0;
4400 
4401     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4402     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4403     for (c = 0; c < dof; ++c) {
4404       const PetscInt point = mesh->cones[off+c];
4405 
4406       for (m = 0; m < meetSize; ++m) {
4407         if (point == meet[i][m]) {
4408           meet[1-i][newMeetSize++] = point;
4409           break;
4410         }
4411       }
4412     }
4413     meetSize = newMeetSize;
4414     i        = 1-i;
4415   }
4416   *numCoveringPoints = meetSize;
4417   *coveringPoints    = meet[i];
4418   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1-i]));
4419   PetscFunctionReturn(0);
4420 }
4421 
4422 /*@C
4423   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4424 
4425   Not Collective
4426 
4427   Input Parameters:
4428 + dm - The DMPlex object
4429 . numPoints - The number of input points for the meet
4430 - points - The input points
4431 
4432   Output Parameters:
4433 + numCoveredPoints - The number of points in the meet
4434 - coveredPoints - The points in the meet
4435 
4436   Level: intermediate
4437 
4438   Fortran Notes:
4439   Since it returns an array, this routine is only available in Fortran 90, and you must
4440   include petsc.h90 in your code.
4441 
4442   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4443 
4444 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4445 @*/
4446 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4447 {
4448   PetscFunctionBegin;
4449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4450   if (points) PetscValidIntPointer(points,3);
4451   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4452   PetscValidPointer(coveredPoints,5);
4453   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4454   if (numCoveredPoints) *numCoveredPoints = 0;
4455   PetscFunctionReturn(0);
4456 }
4457 
4458 /*@C
4459   DMPlexGetFullMeet - Get an array for the meet of the set of points
4460 
4461   Not Collective
4462 
4463   Input Parameters:
4464 + dm - The DMPlex object
4465 . numPoints - The number of input points for the meet
4466 - points - The input points
4467 
4468   Output Parameters:
4469 + numCoveredPoints - The number of points in the meet
4470 - coveredPoints - The points in the meet
4471 
4472   Level: intermediate
4473 
4474   Fortran Notes:
4475   Since it returns an array, this routine is only available in Fortran 90, and you must
4476   include petsc.h90 in your code.
4477 
4478   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4479 
4480 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4481 @*/
4482 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4483 {
4484   PetscInt      *offsets, **closures;
4485   PetscInt      *meet[2];
4486   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
4487   PetscInt       p, h, c, m, mc;
4488 
4489   PetscFunctionBegin;
4490   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4491   PetscValidIntPointer(points, 3);
4492   PetscValidIntPointer(numCoveredPoints, 4);
4493   PetscValidPointer(coveredPoints, 5);
4494 
4495   PetscCall(DMPlexGetDepth(dm, &height));
4496   PetscCall(PetscMalloc1(numPoints, &closures));
4497   PetscCall(DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4498   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4499   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
4500   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4501   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4502 
4503   for (p = 0; p < numPoints; ++p) {
4504     PetscInt closureSize;
4505 
4506     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4507 
4508     offsets[p*(height+2)+0] = 0;
4509     for (h = 0; h < height+1; ++h) {
4510       PetscInt pStart, pEnd, i;
4511 
4512       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4513       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
4514         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4515           offsets[p*(height+2)+h+1] = i;
4516           break;
4517         }
4518       }
4519       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
4520     }
4521     PetscCheck(offsets[p*(height+2)+height+1] == closureSize,PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p*(height+2)+height+1], closureSize);
4522   }
4523   for (h = 0; h < height+1; ++h) {
4524     PetscInt dof;
4525 
4526     /* Copy in cone of first point */
4527     dof = offsets[h+1] - offsets[h];
4528     for (meetSize = 0; meetSize < dof; ++meetSize) {
4529       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
4530     }
4531     /* Check each successive cone */
4532     for (p = 1; p < numPoints && meetSize; ++p) {
4533       PetscInt newMeetSize = 0;
4534 
4535       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
4536       for (c = 0; c < dof; ++c) {
4537         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
4538 
4539         for (m = 0; m < meetSize; ++m) {
4540           if (point == meet[i][m]) {
4541             meet[1-i][newMeetSize++] = point;
4542             break;
4543           }
4544         }
4545       }
4546       meetSize = newMeetSize;
4547       i        = 1-i;
4548     }
4549     if (meetSize) break;
4550   }
4551   *numCoveredPoints = meetSize;
4552   *coveredPoints    = meet[i];
4553   for (p = 0; p < numPoints; ++p) {
4554     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4555   }
4556   PetscCall(PetscFree(closures));
4557   PetscCall(DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4558   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1-i]));
4559   PetscFunctionReturn(0);
4560 }
4561 
4562 /*@C
4563   DMPlexEqual - Determine if two DMs have the same topology
4564 
4565   Not Collective
4566 
4567   Input Parameters:
4568 + dmA - A DMPlex object
4569 - dmB - A DMPlex object
4570 
4571   Output Parameters:
4572 . equal - PETSC_TRUE if the topologies are identical
4573 
4574   Level: intermediate
4575 
4576   Notes:
4577   We are not solving graph isomorphism, so we do not permutation.
4578 
4579 .seealso: `DMPlexGetCone()`
4580 @*/
4581 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4582 {
4583   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4584 
4585   PetscFunctionBegin;
4586   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4587   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4588   PetscValidBoolPointer(equal, 3);
4589 
4590   *equal = PETSC_FALSE;
4591   PetscCall(DMPlexGetDepth(dmA, &depth));
4592   PetscCall(DMPlexGetDepth(dmB, &depthB));
4593   if (depth != depthB) PetscFunctionReturn(0);
4594   PetscCall(DMPlexGetChart(dmA, &pStart,  &pEnd));
4595   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4596   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4597   for (p = pStart; p < pEnd; ++p) {
4598     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4599     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4600 
4601     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4602     PetscCall(DMPlexGetCone(dmA, p, &cone));
4603     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4604     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4605     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4606     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4607     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4608     for (c = 0; c < coneSize; ++c) {
4609       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4610       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4611     }
4612     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4613     PetscCall(DMPlexGetSupport(dmA, p, &support));
4614     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4615     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4616     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4617     for (s = 0; s < supportSize; ++s) {
4618       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4619     }
4620   }
4621   *equal = PETSC_TRUE;
4622   PetscFunctionReturn(0);
4623 }
4624 
4625 /*@C
4626   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4627 
4628   Not Collective
4629 
4630   Input Parameters:
4631 + dm         - The DMPlex
4632 . cellDim    - The cell dimension
4633 - numCorners - The number of vertices on a cell
4634 
4635   Output Parameters:
4636 . numFaceVertices - The number of vertices on a face
4637 
4638   Level: developer
4639 
4640   Notes:
4641   Of course this can only work for a restricted set of symmetric shapes
4642 
4643 .seealso: `DMPlexGetCone()`
4644 @*/
4645 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4646 {
4647   MPI_Comm       comm;
4648 
4649   PetscFunctionBegin;
4650   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
4651   PetscValidIntPointer(numFaceVertices,4);
4652   switch (cellDim) {
4653   case 0:
4654     *numFaceVertices = 0;
4655     break;
4656   case 1:
4657     *numFaceVertices = 1;
4658     break;
4659   case 2:
4660     switch (numCorners) {
4661     case 3: /* triangle */
4662       *numFaceVertices = 2; /* Edge has 2 vertices */
4663       break;
4664     case 4: /* quadrilateral */
4665       *numFaceVertices = 2; /* Edge has 2 vertices */
4666       break;
4667     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
4668       *numFaceVertices = 3; /* Edge has 3 vertices */
4669       break;
4670     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4671       *numFaceVertices = 3; /* Edge has 3 vertices */
4672       break;
4673     default:
4674       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4675     }
4676     break;
4677   case 3:
4678     switch (numCorners) {
4679     case 4: /* tetradehdron */
4680       *numFaceVertices = 3; /* Face has 3 vertices */
4681       break;
4682     case 6: /* tet cohesive cells */
4683       *numFaceVertices = 4; /* Face has 4 vertices */
4684       break;
4685     case 8: /* hexahedron */
4686       *numFaceVertices = 4; /* Face has 4 vertices */
4687       break;
4688     case 9: /* tet cohesive Lagrange cells */
4689       *numFaceVertices = 6; /* Face has 6 vertices */
4690       break;
4691     case 10: /* quadratic tetrahedron */
4692       *numFaceVertices = 6; /* Face has 6 vertices */
4693       break;
4694     case 12: /* hex cohesive Lagrange cells */
4695       *numFaceVertices = 6; /* Face has 6 vertices */
4696       break;
4697     case 18: /* quadratic tet cohesive Lagrange cells */
4698       *numFaceVertices = 6; /* Face has 6 vertices */
4699       break;
4700     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4701       *numFaceVertices = 9; /* Face has 9 vertices */
4702       break;
4703     default:
4704       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4705     }
4706     break;
4707   default:
4708     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4709   }
4710   PetscFunctionReturn(0);
4711 }
4712 
4713 /*@
4714   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4715 
4716   Not Collective
4717 
4718   Input Parameter:
4719 . dm    - The DMPlex object
4720 
4721   Output Parameter:
4722 . depthLabel - The DMLabel recording point depth
4723 
4724   Level: developer
4725 
4726 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4727 @*/
4728 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4729 {
4730   PetscFunctionBegin;
4731   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4732   PetscValidPointer(depthLabel, 2);
4733   *depthLabel = dm->depthLabel;
4734   PetscFunctionReturn(0);
4735 }
4736 
4737 /*@
4738   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4739 
4740   Not Collective
4741 
4742   Input Parameter:
4743 . dm    - The DMPlex object
4744 
4745   Output Parameter:
4746 . depth - The number of strata (breadth first levels) in the DAG
4747 
4748   Level: developer
4749 
4750   Notes:
4751   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4752   The point depth is described more in detail in DMPlexGetDepthStratum().
4753   An empty mesh gives -1.
4754 
4755 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4756 @*/
4757 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4758 {
4759   DMLabel        label;
4760   PetscInt       d = 0;
4761 
4762   PetscFunctionBegin;
4763   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4764   PetscValidIntPointer(depth, 2);
4765   PetscCall(DMPlexGetDepthLabel(dm, &label));
4766   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4767   *depth = d-1;
4768   PetscFunctionReturn(0);
4769 }
4770 
4771 /*@
4772   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4773 
4774   Not Collective
4775 
4776   Input Parameters:
4777 + dm    - The DMPlex object
4778 - depth - The requested depth
4779 
4780   Output Parameters:
4781 + start - The first point at this depth
4782 - end   - One beyond the last point at this depth
4783 
4784   Notes:
4785   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4786   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4787   higher dimension, e.g., "edges".
4788 
4789   Level: developer
4790 
4791 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4792 @*/
4793 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4794 {
4795   DMLabel        label;
4796   PetscInt       pStart, pEnd;
4797 
4798   PetscFunctionBegin;
4799   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4800   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4801   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4802   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4803   if (pStart == pEnd) PetscFunctionReturn(0);
4804   if (depth < 0) {
4805     if (start) *start = pStart;
4806     if (end)   *end   = pEnd;
4807     PetscFunctionReturn(0);
4808   }
4809   PetscCall(DMPlexGetDepthLabel(dm, &label));
4810   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4811   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4812   PetscFunctionReturn(0);
4813 }
4814 
4815 /*@
4816   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4817 
4818   Not Collective
4819 
4820   Input Parameters:
4821 + dm     - The DMPlex object
4822 - height - The requested height
4823 
4824   Output Parameters:
4825 + start - The first point at this height
4826 - end   - One beyond the last point at this height
4827 
4828   Notes:
4829   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4830   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4831   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4832 
4833   Level: developer
4834 
4835 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4836 @*/
4837 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4838 {
4839   DMLabel        label;
4840   PetscInt       depth, pStart, pEnd;
4841 
4842   PetscFunctionBegin;
4843   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4844   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4845   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4846   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4847   if (pStart == pEnd) PetscFunctionReturn(0);
4848   if (height < 0) {
4849     if (start) *start = pStart;
4850     if (end)   *end   = pEnd;
4851     PetscFunctionReturn(0);
4852   }
4853   PetscCall(DMPlexGetDepthLabel(dm, &label));
4854   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4855   PetscCall(DMLabelGetNumValues(label, &depth));
4856   PetscCall(DMLabelGetStratumBounds(label, depth-1-height, start, end));
4857   PetscFunctionReturn(0);
4858 }
4859 
4860 /*@
4861   DMPlexGetPointDepth - Get the depth of a given point
4862 
4863   Not Collective
4864 
4865   Input Parameters:
4866 + dm    - The DMPlex object
4867 - point - The point
4868 
4869   Output Parameter:
4870 . depth - The depth of the point
4871 
4872   Level: intermediate
4873 
4874 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4875 @*/
4876 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4877 {
4878   PetscFunctionBegin;
4879   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4880   PetscValidIntPointer(depth, 3);
4881   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4882   PetscFunctionReturn(0);
4883 }
4884 
4885 /*@
4886   DMPlexGetPointHeight - Get the height of a given point
4887 
4888   Not Collective
4889 
4890   Input Parameters:
4891 + dm    - The DMPlex object
4892 - point - The point
4893 
4894   Output Parameter:
4895 . height - The height of the point
4896 
4897   Level: intermediate
4898 
4899 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4900 @*/
4901 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4902 {
4903   PetscInt       n, pDepth;
4904 
4905   PetscFunctionBegin;
4906   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4907   PetscValidIntPointer(height, 3);
4908   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4909   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4910   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
4911   PetscFunctionReturn(0);
4912 }
4913 
4914 /*@
4915   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4916 
4917   Not Collective
4918 
4919   Input Parameter:
4920 . dm - The DMPlex object
4921 
4922   Output Parameter:
4923 . celltypeLabel - The DMLabel recording cell polytope type
4924 
4925   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4926   DMCreateLabel(dm, "celltype") beforehand.
4927 
4928   Level: developer
4929 
4930 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4931 @*/
4932 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
4933 {
4934   PetscFunctionBegin;
4935   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4936   PetscValidPointer(celltypeLabel, 2);
4937   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4938   *celltypeLabel = dm->celltypeLabel;
4939   PetscFunctionReturn(0);
4940 }
4941 
4942 /*@
4943   DMPlexGetCellType - Get the polytope type of a given cell
4944 
4945   Not Collective
4946 
4947   Input Parameters:
4948 + dm   - The DMPlex object
4949 - cell - The cell
4950 
4951   Output Parameter:
4952 . celltype - The polytope type of the cell
4953 
4954   Level: intermediate
4955 
4956 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4957 @*/
4958 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
4959 {
4960   DMLabel        label;
4961   PetscInt       ct;
4962 
4963   PetscFunctionBegin;
4964   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4965   PetscValidPointer(celltype, 3);
4966   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4967   PetscCall(DMLabelGetValue(label, cell, &ct));
4968   PetscCheck(ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4969   *celltype = (DMPolytopeType) ct;
4970   PetscFunctionReturn(0);
4971 }
4972 
4973 /*@
4974   DMPlexSetCellType - Set the polytope type of a given cell
4975 
4976   Not Collective
4977 
4978   Input Parameters:
4979 + dm   - The DMPlex object
4980 . cell - The cell
4981 - celltype - The polytope type of the cell
4982 
4983   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
4984   is executed. This function will override the computed type. However, if automatic classification will not succeed
4985   and a user wants to manually specify all types, the classification must be disabled by calling
4986   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
4987 
4988   Level: advanced
4989 
4990 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
4991 @*/
4992 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
4993 {
4994   DMLabel        label;
4995 
4996   PetscFunctionBegin;
4997   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4998   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4999   PetscCall(DMLabelSetValue(label, cell, celltype));
5000   PetscFunctionReturn(0);
5001 }
5002 
5003 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5004 {
5005   PetscSection   section, s;
5006   Mat            m;
5007   PetscInt       maxHeight;
5008 
5009   PetscFunctionBegin;
5010   PetscCall(DMClone(dm, cdm));
5011   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5012   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5013   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5014   PetscCall(DMSetLocalSection(*cdm, section));
5015   PetscCall(PetscSectionDestroy(&section));
5016   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5017   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5018   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5019   PetscCall(PetscSectionDestroy(&s));
5020   PetscCall(MatDestroy(&m));
5021 
5022   PetscCall(DMSetNumFields(*cdm, 1));
5023   PetscCall(DMCreateDS(*cdm));
5024   PetscFunctionReturn(0);
5025 }
5026 
5027 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5028 {
5029   Vec            coordsLocal;
5030   DM             coordsDM;
5031 
5032   PetscFunctionBegin;
5033   *field = NULL;
5034   PetscCall(DMGetCoordinatesLocal(dm,&coordsLocal));
5035   PetscCall(DMGetCoordinateDM(dm,&coordsDM));
5036   if (coordsLocal && coordsDM) {
5037     PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5038   }
5039   PetscFunctionReturn(0);
5040 }
5041 
5042 /*@C
5043   DMPlexGetConeSection - Return a section which describes the layout of cone data
5044 
5045   Not Collective
5046 
5047   Input Parameters:
5048 . dm        - The DMPlex object
5049 
5050   Output Parameter:
5051 . section - The PetscSection object
5052 
5053   Level: developer
5054 
5055 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5056 @*/
5057 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5058 {
5059   DM_Plex *mesh = (DM_Plex*) dm->data;
5060 
5061   PetscFunctionBegin;
5062   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5063   if (section) *section = mesh->coneSection;
5064   PetscFunctionReturn(0);
5065 }
5066 
5067 /*@C
5068   DMPlexGetSupportSection - Return a section which describes the layout of support data
5069 
5070   Not Collective
5071 
5072   Input Parameters:
5073 . dm        - The DMPlex object
5074 
5075   Output Parameter:
5076 . section - The PetscSection object
5077 
5078   Level: developer
5079 
5080 .seealso: `DMPlexGetConeSection()`
5081 @*/
5082 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5083 {
5084   DM_Plex *mesh = (DM_Plex*) dm->data;
5085 
5086   PetscFunctionBegin;
5087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5088   if (section) *section = mesh->supportSection;
5089   PetscFunctionReturn(0);
5090 }
5091 
5092 /*@C
5093   DMPlexGetCones - Return cone data
5094 
5095   Not Collective
5096 
5097   Input Parameters:
5098 . dm        - The DMPlex object
5099 
5100   Output Parameter:
5101 . cones - The cone for each point
5102 
5103   Level: developer
5104 
5105 .seealso: `DMPlexGetConeSection()`
5106 @*/
5107 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5108 {
5109   DM_Plex *mesh = (DM_Plex*) dm->data;
5110 
5111   PetscFunctionBegin;
5112   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5113   if (cones) *cones = mesh->cones;
5114   PetscFunctionReturn(0);
5115 }
5116 
5117 /*@C
5118   DMPlexGetConeOrientations - Return cone orientation data
5119 
5120   Not Collective
5121 
5122   Input Parameters:
5123 . dm        - The DMPlex object
5124 
5125   Output Parameter:
5126 . coneOrientations - The array of cone orientations for all points
5127 
5128   Level: developer
5129 
5130   Notes:
5131   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5132 
5133   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5134 
5135 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5136 @*/
5137 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5138 {
5139   DM_Plex *mesh = (DM_Plex*) dm->data;
5140 
5141   PetscFunctionBegin;
5142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5143   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5144   PetscFunctionReturn(0);
5145 }
5146 
5147 /******************************** FEM Support **********************************/
5148 
5149 /*
5150  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5151  representing a line in the section.
5152 */
5153 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
5154 {
5155   PetscFunctionBeginHot;
5156   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5157   if (line < 0) {
5158     *k = 0;
5159     *Nc = 0;
5160   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
5161     *k = 1;
5162   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
5163     /* An order k SEM disc has k-1 dofs on an edge */
5164     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5165     *k = *k / *Nc + 1;
5166   }
5167   PetscFunctionReturn(0);
5168 }
5169 
5170 /*@
5171 
5172   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5173   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5174   section provided (or the section of the DM).
5175 
5176   Input Parameters:
5177 + dm      - The DM
5178 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5179 - section - The PetscSection to reorder, or NULL for the default section
5180 
5181   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5182   degree of the basis.
5183 
5184   Example:
5185   A typical interpolated single-quad mesh might order points as
5186 .vb
5187   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5188 
5189   v4 -- e6 -- v3
5190   |           |
5191   e7    c0    e8
5192   |           |
5193   v1 -- e5 -- v2
5194 .ve
5195 
5196   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5197   dofs in the order of points, e.g.,
5198 .vb
5199     c0 -> [0,1,2,3]
5200     v1 -> [4]
5201     ...
5202     e5 -> [8, 9]
5203 .ve
5204 
5205   which corresponds to the dofs
5206 .vb
5207     6   10  11  7
5208     13  2   3   15
5209     12  0   1   14
5210     4   8   9   5
5211 .ve
5212 
5213   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5214 .vb
5215   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5216 .ve
5217 
5218   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5219 .vb
5220    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5221 .ve
5222 
5223   Level: developer
5224 
5225 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5226 @*/
5227 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5228 {
5229   DMLabel        label;
5230   PetscInt       dim, depth = -1, eStart = -1, Nf;
5231   PetscBool      vertexchart;
5232 
5233   PetscFunctionBegin;
5234   PetscCall(DMGetDimension(dm, &dim));
5235   if (dim < 1) PetscFunctionReturn(0);
5236   if (point < 0) {
5237     PetscInt sStart,sEnd;
5238 
5239     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5240     point = sEnd-sStart ? sStart : point;
5241   }
5242   PetscCall(DMPlexGetDepthLabel(dm, &label));
5243   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5244   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5245   if (depth == 1) {eStart = point;}
5246   else if  (depth == dim) {
5247     const PetscInt *cone;
5248 
5249     PetscCall(DMPlexGetCone(dm, point, &cone));
5250     if (dim == 2) eStart = cone[0];
5251     else if (dim == 3) {
5252       const PetscInt *cone2;
5253       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5254       eStart = cone2[0];
5255     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " of depth %" PetscInt_FMT " cannot be used to bootstrap spectral ordering for dim %" PetscInt_FMT, point, depth, dim);
5256   } else PetscCheck(depth < 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " of depth %" PetscInt_FMT " cannot be used to bootstrap spectral ordering for dim %" PetscInt_FMT, point, depth, dim);
5257   {                             /* Determine whether the chart covers all points or just vertices. */
5258     PetscInt pStart,pEnd,cStart,cEnd;
5259     PetscCall(DMPlexGetDepthStratum(dm,0,&pStart,&pEnd));
5260     PetscCall(PetscSectionGetChart(section,&cStart,&cEnd));
5261     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5262     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5263     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5264   }
5265   PetscCall(PetscSectionGetNumFields(section, &Nf));
5266   for (PetscInt d=1; d<=dim; d++) {
5267     PetscInt k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5268     PetscInt *perm;
5269 
5270     for (f = 0; f < Nf; ++f) {
5271       PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5272       size += PetscPowInt(k+1, d)*Nc;
5273     }
5274     PetscCall(PetscMalloc1(size, &perm));
5275     for (f = 0; f < Nf; ++f) {
5276       switch (d) {
5277       case 1:
5278         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5279         /*
5280          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5281          We want              [ vtx0; edge of length k-1; vtx1 ]
5282          */
5283         for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
5284         for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
5285         for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
5286         foffset = offset;
5287         break;
5288       case 2:
5289         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5290         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5291         /* The SEM order is
5292 
5293          v_lb, {e_b}, v_rb,
5294          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5295          v_lt, reverse {e_t}, v_rt
5296          */
5297         {
5298           const PetscInt of   = 0;
5299           const PetscInt oeb  = of   + PetscSqr(k-1);
5300           const PetscInt oer  = oeb  + (k-1);
5301           const PetscInt oet  = oer  + (k-1);
5302           const PetscInt oel  = oet  + (k-1);
5303           const PetscInt ovlb = oel  + (k-1);
5304           const PetscInt ovrb = ovlb + 1;
5305           const PetscInt ovrt = ovrb + 1;
5306           const PetscInt ovlt = ovrt + 1;
5307           PetscInt       o;
5308 
5309           /* bottom */
5310           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
5311           for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5312           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
5313           /* middle */
5314           for (i = 0; i < k-1; ++i) {
5315             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
5316             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;
5317             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
5318           }
5319           /* top */
5320           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
5321           for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5322           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
5323           foffset = offset;
5324         }
5325         break;
5326       case 3:
5327         /* The original hex closure is
5328 
5329          {c,
5330          f_b, f_t, f_f, f_b, f_r, f_l,
5331          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5332          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5333          */
5334         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5335         /* The SEM order is
5336          Bottom Slice
5337          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5338          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5339          v_blb, {e_bb}, v_brb,
5340 
5341          Middle Slice (j)
5342          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5343          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5344          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5345 
5346          Top Slice
5347          v_tlf, {e_tf}, v_trf,
5348          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5349          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5350          */
5351         {
5352           const PetscInt oc    = 0;
5353           const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
5354           const PetscInt oft   = ofb   + PetscSqr(k-1);
5355           const PetscInt off   = oft   + PetscSqr(k-1);
5356           const PetscInt ofk   = off   + PetscSqr(k-1);
5357           const PetscInt ofr   = ofk   + PetscSqr(k-1);
5358           const PetscInt ofl   = ofr   + PetscSqr(k-1);
5359           const PetscInt oebl  = ofl   + PetscSqr(k-1);
5360           const PetscInt oebb  = oebl  + (k-1);
5361           const PetscInt oebr  = oebb  + (k-1);
5362           const PetscInt oebf  = oebr  + (k-1);
5363           const PetscInt oetf  = oebf  + (k-1);
5364           const PetscInt oetr  = oetf  + (k-1);
5365           const PetscInt oetb  = oetr  + (k-1);
5366           const PetscInt oetl  = oetb  + (k-1);
5367           const PetscInt oerf  = oetl  + (k-1);
5368           const PetscInt oelf  = oerf  + (k-1);
5369           const PetscInt oelb  = oelf  + (k-1);
5370           const PetscInt oerb  = oelb  + (k-1);
5371           const PetscInt ovblf = oerb  + (k-1);
5372           const PetscInt ovblb = ovblf + 1;
5373           const PetscInt ovbrb = ovblb + 1;
5374           const PetscInt ovbrf = ovbrb + 1;
5375           const PetscInt ovtlf = ovbrf + 1;
5376           const PetscInt ovtrf = ovtlf + 1;
5377           const PetscInt ovtrb = ovtrf + 1;
5378           const PetscInt ovtlb = ovtrb + 1;
5379           PetscInt       o, n;
5380 
5381           /* Bottom Slice */
5382           /*   bottom */
5383           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
5384           for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5385           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
5386           /*   middle */
5387           for (i = 0; i < k-1; ++i) {
5388             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
5389             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;}
5390             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
5391           }
5392           /*   top */
5393           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
5394           for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5395           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
5396 
5397           /* Middle Slice */
5398           for (j = 0; j < k-1; ++j) {
5399             /*   bottom */
5400             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
5401             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;
5402             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
5403             /*   middle */
5404             for (i = 0; i < k-1; ++i) {
5405               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
5406               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;
5407               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
5408             }
5409             /*   top */
5410             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
5411             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;
5412             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
5413           }
5414 
5415           /* Top Slice */
5416           /*   bottom */
5417           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
5418           for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5419           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
5420           /*   middle */
5421           for (i = 0; i < k-1; ++i) {
5422             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
5423             for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
5424             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
5425           }
5426           /*   top */
5427           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
5428           for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5429           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
5430 
5431           foffset = offset;
5432         }
5433         break;
5434       default: SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5435       }
5436     }
5437     PetscCheck(offset == size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5438     /* Check permutation */
5439     {
5440       PetscInt *check;
5441 
5442       PetscCall(PetscMalloc1(size, &check));
5443       for (i = 0; i < size; ++i) {
5444         check[i] = -1;
5445         PetscCheck(perm[i] >= 0 && perm[i] < size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5446       }
5447       for (i = 0; i < size; ++i) check[perm[i]] = i;
5448       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5449       PetscCall(PetscFree(check));
5450     }
5451     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size, PETSC_OWN_POINTER, perm));
5452     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5453       PetscInt *loc_perm;
5454       PetscCall(PetscMalloc1(size*2, &loc_perm));
5455       for (PetscInt i=0; i<size; i++) {
5456         loc_perm[i] = perm[i];
5457         loc_perm[size+i] = size + perm[i];
5458       }
5459       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size*2, PETSC_OWN_POINTER, loc_perm));
5460     }
5461   }
5462   PetscFunctionReturn(0);
5463 }
5464 
5465 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5466 {
5467   PetscDS        prob;
5468   PetscInt       depth, Nf, h;
5469   DMLabel        label;
5470 
5471   PetscFunctionBeginHot;
5472   PetscCall(DMGetDS(dm, &prob));
5473   Nf      = prob->Nf;
5474   label   = dm->depthLabel;
5475   *dspace = NULL;
5476   if (field < Nf) {
5477     PetscObject disc = prob->disc[field];
5478 
5479     if (disc->classid == PETSCFE_CLASSID) {
5480       PetscDualSpace dsp;
5481 
5482       PetscCall(PetscFEGetDualSpace((PetscFE)disc,&dsp));
5483       PetscCall(DMLabelGetNumValues(label,&depth));
5484       PetscCall(DMLabelGetValue(label,point,&h));
5485       h    = depth - 1 - h;
5486       if (h) {
5487         PetscCall(PetscDualSpaceGetHeightSubspace(dsp,h,dspace));
5488       } else {
5489         *dspace = dsp;
5490       }
5491     }
5492   }
5493   PetscFunctionReturn(0);
5494 }
5495 
5496 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5497 {
5498   PetscScalar    *array, *vArray;
5499   const PetscInt *cone, *coneO;
5500   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
5501 
5502   PetscFunctionBeginHot;
5503   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5504   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5505   PetscCall(DMPlexGetCone(dm, point, &cone));
5506   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5507   if (!values || !*values) {
5508     if ((point >= pStart) && (point < pEnd)) {
5509       PetscInt dof;
5510 
5511       PetscCall(PetscSectionGetDof(section, point, &dof));
5512       size += dof;
5513     }
5514     for (p = 0; p < numPoints; ++p) {
5515       const PetscInt cp = cone[p];
5516       PetscInt       dof;
5517 
5518       if ((cp < pStart) || (cp >= pEnd)) continue;
5519       PetscCall(PetscSectionGetDof(section, cp, &dof));
5520       size += dof;
5521     }
5522     if (!values) {
5523       if (csize) *csize = size;
5524       PetscFunctionReturn(0);
5525     }
5526     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5527   } else {
5528     array = *values;
5529   }
5530   size = 0;
5531   PetscCall(VecGetArray(v, &vArray));
5532   if ((point >= pStart) && (point < pEnd)) {
5533     PetscInt     dof, off, d;
5534     PetscScalar *varr;
5535 
5536     PetscCall(PetscSectionGetDof(section, point, &dof));
5537     PetscCall(PetscSectionGetOffset(section, point, &off));
5538     varr = &vArray[off];
5539     for (d = 0; d < dof; ++d, ++offset) {
5540       array[offset] = varr[d];
5541     }
5542     size += dof;
5543   }
5544   for (p = 0; p < numPoints; ++p) {
5545     const PetscInt cp = cone[p];
5546     PetscInt       o  = coneO[p];
5547     PetscInt       dof, off, d;
5548     PetscScalar   *varr;
5549 
5550     if ((cp < pStart) || (cp >= pEnd)) continue;
5551     PetscCall(PetscSectionGetDof(section, cp, &dof));
5552     PetscCall(PetscSectionGetOffset(section, cp, &off));
5553     varr = &vArray[off];
5554     if (o >= 0) {
5555       for (d = 0; d < dof; ++d, ++offset) {
5556         array[offset] = varr[d];
5557       }
5558     } else {
5559       for (d = dof-1; d >= 0; --d, ++offset) {
5560         array[offset] = varr[d];
5561       }
5562     }
5563     size += dof;
5564   }
5565   PetscCall(VecRestoreArray(v, &vArray));
5566   if (!*values) {
5567     if (csize) *csize = size;
5568     *values = array;
5569   } else {
5570     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5571     *csize = size;
5572   }
5573   PetscFunctionReturn(0);
5574 }
5575 
5576 /* Compress out points not in the section */
5577 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5578 {
5579   const PetscInt np = *numPoints;
5580   PetscInt       pStart, pEnd, p, q;
5581 
5582   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5583   for (p = 0, q = 0; p < np; ++p) {
5584     const PetscInt r = points[p*2];
5585     if ((r >= pStart) && (r < pEnd)) {
5586       points[q*2]   = r;
5587       points[q*2+1] = points[p*2+1];
5588       ++q;
5589     }
5590   }
5591   *numPoints = q;
5592   return 0;
5593 }
5594 
5595 /* Compressed closure does not apply closure permutation */
5596 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5597 {
5598   const PetscInt *cla = NULL;
5599   PetscInt       np, *pts = NULL;
5600 
5601   PetscFunctionBeginHot;
5602   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints));
5603   if (*clPoints) {
5604     PetscInt dof, off;
5605 
5606     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5607     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5608     PetscCall(ISGetIndices(*clPoints, &cla));
5609     np   = dof/2;
5610     pts  = (PetscInt *) &cla[off];
5611   } else {
5612     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5613     PetscCall(CompressPoints_Private(section, &np, pts));
5614   }
5615   *numPoints = np;
5616   *points    = pts;
5617   *clp       = cla;
5618   PetscFunctionReturn(0);
5619 }
5620 
5621 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5622 {
5623   PetscFunctionBeginHot;
5624   if (!*clPoints) {
5625     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5626   } else {
5627     PetscCall(ISRestoreIndices(*clPoints, clp));
5628   }
5629   *numPoints = 0;
5630   *points    = NULL;
5631   *clSec     = NULL;
5632   *clPoints  = NULL;
5633   *clp       = NULL;
5634   PetscFunctionReturn(0);
5635 }
5636 
5637 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5638 {
5639   PetscInt          offset = 0, p;
5640   const PetscInt    **perms = NULL;
5641   const PetscScalar **flips = NULL;
5642 
5643   PetscFunctionBeginHot;
5644   *size = 0;
5645   PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
5646   for (p = 0; p < numPoints; p++) {
5647     const PetscInt    point = points[2*p];
5648     const PetscInt    *perm = perms ? perms[p] : NULL;
5649     const PetscScalar *flip = flips ? flips[p] : NULL;
5650     PetscInt          dof, off, d;
5651     const PetscScalar *varr;
5652 
5653     PetscCall(PetscSectionGetDof(section, point, &dof));
5654     PetscCall(PetscSectionGetOffset(section, point, &off));
5655     varr = &vArray[off];
5656     if (clperm) {
5657       if (perm) {
5658         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5659       } else {
5660         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5661       }
5662       if (flip) {
5663         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5664       }
5665     } else {
5666       if (perm) {
5667         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5668       } else {
5669         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5670       }
5671       if (flip) {
5672         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5673       }
5674     }
5675     offset += dof;
5676   }
5677   PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
5678   *size = offset;
5679   PetscFunctionReturn(0);
5680 }
5681 
5682 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[])
5683 {
5684   PetscInt          offset = 0, f;
5685 
5686   PetscFunctionBeginHot;
5687   *size = 0;
5688   for (f = 0; f < numFields; ++f) {
5689     PetscInt          p;
5690     const PetscInt    **perms = NULL;
5691     const PetscScalar **flips = NULL;
5692 
5693     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5694     for (p = 0; p < numPoints; p++) {
5695       const PetscInt    point = points[2*p];
5696       PetscInt          fdof, foff, b;
5697       const PetscScalar *varr;
5698       const PetscInt    *perm = perms ? perms[p] : NULL;
5699       const PetscScalar *flip = flips ? flips[p] : NULL;
5700 
5701       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5702       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5703       varr = &vArray[foff];
5704       if (clperm) {
5705         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5706         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5707         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5708       } else {
5709         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5710         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5711         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5712       }
5713       offset += fdof;
5714     }
5715     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5716   }
5717   *size = offset;
5718   PetscFunctionReturn(0);
5719 }
5720 
5721 /*@C
5722   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5723 
5724   Not collective
5725 
5726   Input Parameters:
5727 + dm - The DM
5728 . section - The section describing the layout in v, or NULL to use the default section
5729 . v - The local vector
5730 - point - The point in the DM
5731 
5732   Input/Output Parameters:
5733 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5734 - values - An array to use for the values, or NULL to have it allocated automatically;
5735            if the user provided NULL, it is a borrowed array and should not be freed
5736 
5737 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5738 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5739 $ assembly function, and a user may already have allocated storage for this operation.
5740 $
5741 $ A typical use could be
5742 $
5743 $  values = NULL;
5744 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5745 $  for (cl = 0; cl < clSize; ++cl) {
5746 $    <Compute on closure>
5747 $  }
5748 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5749 $
5750 $ or
5751 $
5752 $  PetscMalloc1(clMaxSize, &values);
5753 $  for (p = pStart; p < pEnd; ++p) {
5754 $    clSize = clMaxSize;
5755 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5756 $    for (cl = 0; cl < clSize; ++cl) {
5757 $      <Compute on closure>
5758 $    }
5759 $  }
5760 $  PetscFree(values);
5761 
5762   Fortran Notes:
5763   Since it returns an array, this routine is only available in Fortran 90, and you must
5764   include petsc.h90 in your code.
5765 
5766   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5767 
5768   Level: intermediate
5769 
5770 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5771 @*/
5772 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5773 {
5774   PetscSection       clSection;
5775   IS                 clPoints;
5776   PetscInt          *points = NULL;
5777   const PetscInt    *clp, *perm;
5778   PetscInt           depth, numFields, numPoints, asize;
5779 
5780   PetscFunctionBeginHot;
5781   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5782   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5783   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5784   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5785   PetscCall(DMPlexGetDepth(dm, &depth));
5786   PetscCall(PetscSectionGetNumFields(section, &numFields));
5787   if (depth == 1 && numFields < 2) {
5788     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5789     PetscFunctionReturn(0);
5790   }
5791   /* Get points */
5792   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5793   /* Get sizes */
5794   asize = 0;
5795   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5796     PetscInt dof;
5797     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5798     asize += dof;
5799   }
5800   if (values) {
5801     const PetscScalar *vArray;
5802     PetscInt          size;
5803 
5804     if (*values) {
5805       PetscCheck(*csize >= asize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Provided array size %" PetscInt_FMT " not sufficient to hold closure size %" PetscInt_FMT, *csize, asize);
5806     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5807     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm));
5808     PetscCall(VecGetArrayRead(v, &vArray));
5809     /* Get values */
5810     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5811     else               PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5812     PetscCheck(asize == size,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5813     /* Cleanup array */
5814     PetscCall(VecRestoreArrayRead(v, &vArray));
5815   }
5816   if (csize) *csize = asize;
5817   /* Cleanup points */
5818   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5819   PetscFunctionReturn(0);
5820 }
5821 
5822 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5823 {
5824   DMLabel            depthLabel;
5825   PetscSection       clSection;
5826   IS                 clPoints;
5827   PetscScalar       *array;
5828   const PetscScalar *vArray;
5829   PetscInt          *points = NULL;
5830   const PetscInt    *clp, *perm = NULL;
5831   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5832 
5833   PetscFunctionBeginHot;
5834   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5835   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5836   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5837   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5838   PetscCall(DMPlexGetDepth(dm, &mdepth));
5839   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5840   PetscCall(PetscSectionGetNumFields(section, &numFields));
5841   if (mdepth == 1 && numFields < 2) {
5842     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5843     PetscFunctionReturn(0);
5844   }
5845   /* Get points */
5846   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5847   for (clsize=0,p=0; p<Np; p++) {
5848     PetscInt dof;
5849     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
5850     clsize += dof;
5851   }
5852   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm));
5853   /* Filter points */
5854   for (p = 0; p < numPoints*2; p += 2) {
5855     PetscInt dep;
5856 
5857     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5858     if (dep != depth) continue;
5859     points[Np*2+0] = points[p];
5860     points[Np*2+1] = points[p+1];
5861     ++Np;
5862   }
5863   /* Get array */
5864   if (!values || !*values) {
5865     PetscInt asize = 0, dof;
5866 
5867     for (p = 0; p < Np*2; p += 2) {
5868       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5869       asize += dof;
5870     }
5871     if (!values) {
5872       PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5873       if (csize) *csize = asize;
5874       PetscFunctionReturn(0);
5875     }
5876     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5877   } else {
5878     array = *values;
5879   }
5880   PetscCall(VecGetArrayRead(v, &vArray));
5881   /* Get values */
5882   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5883   else               PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5884   /* Cleanup points */
5885   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5886   /* Cleanup array */
5887   PetscCall(VecRestoreArrayRead(v, &vArray));
5888   if (!*values) {
5889     if (csize) *csize = size;
5890     *values = array;
5891   } else {
5892     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5893     *csize = size;
5894   }
5895   PetscFunctionReturn(0);
5896 }
5897 
5898 /*@C
5899   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5900 
5901   Not collective
5902 
5903   Input Parameters:
5904 + dm - The DM
5905 . section - The section describing the layout in v, or NULL to use the default section
5906 . v - The local vector
5907 . point - The point in the DM
5908 . csize - The number of values in the closure, or NULL
5909 - values - The array of values, which is a borrowed array and should not be freed
5910 
5911   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5912 
5913   Fortran Notes:
5914   Since it returns an array, this routine is only available in Fortran 90, and you must
5915   include petsc.h90 in your code.
5916 
5917   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5918 
5919   Level: intermediate
5920 
5921 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5922 @*/
5923 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5924 {
5925   PetscInt       size = 0;
5926 
5927   PetscFunctionBegin;
5928   /* Should work without recalculating size */
5929   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values));
5930   *values = NULL;
5931   PetscFunctionReturn(0);
5932 }
5933 
5934 static inline void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5935 static inline void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5936 
5937 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[])
5938 {
5939   PetscInt        cdof;   /* The number of constraints on this point */
5940   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5941   PetscScalar    *a;
5942   PetscInt        off, cind = 0, k;
5943 
5944   PetscFunctionBegin;
5945   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5946   PetscCall(PetscSectionGetOffset(section, point, &off));
5947   a    = &array[off];
5948   if (!cdof || setBC) {
5949     if (clperm) {
5950       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5951       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5952     } else {
5953       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5954       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5955     }
5956   } else {
5957     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5958     if (clperm) {
5959       if (perm) {for (k = 0; k < dof; ++k) {
5960           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5961           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5962         }
5963       } else {
5964         for (k = 0; k < dof; ++k) {
5965           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5966           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5967         }
5968       }
5969     } else {
5970       if (perm) {
5971         for (k = 0; k < dof; ++k) {
5972           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5973           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
5974         }
5975       } else {
5976         for (k = 0; k < dof; ++k) {
5977           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5978           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
5979         }
5980       }
5981     }
5982   }
5983   PetscFunctionReturn(0);
5984 }
5985 
5986 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[])
5987 {
5988   PetscInt        cdof;   /* The number of constraints on this point */
5989   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5990   PetscScalar    *a;
5991   PetscInt        off, cind = 0, k;
5992 
5993   PetscFunctionBegin;
5994   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5995   PetscCall(PetscSectionGetOffset(section, point, &off));
5996   a    = &array[off];
5997   if (cdof) {
5998     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5999     if (clperm) {
6000       if (perm) {
6001         for (k = 0; k < dof; ++k) {
6002           if ((cind < cdof) && (k == cdofs[cind])) {
6003             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
6004             cind++;
6005           }
6006         }
6007       } else {
6008         for (k = 0; k < dof; ++k) {
6009           if ((cind < cdof) && (k == cdofs[cind])) {
6010             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
6011             cind++;
6012           }
6013         }
6014       }
6015     } else {
6016       if (perm) {
6017         for (k = 0; k < dof; ++k) {
6018           if ((cind < cdof) && (k == cdofs[cind])) {
6019             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6020             cind++;
6021           }
6022         }
6023       } else {
6024         for (k = 0; k < dof; ++k) {
6025           if ((cind < cdof) && (k == cdofs[cind])) {
6026             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6027             cind++;
6028           }
6029         }
6030       }
6031     }
6032   }
6033   PetscFunctionReturn(0);
6034 }
6035 
6036 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[])
6037 {
6038   PetscScalar    *a;
6039   PetscInt        fdof, foff, fcdof, foffset = *offset;
6040   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6041   PetscInt        cind = 0, b;
6042 
6043   PetscFunctionBegin;
6044   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6045   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6046   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6047   a    = &array[foff];
6048   if (!fcdof || setBC) {
6049     if (clperm) {
6050       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
6051       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
6052     } else {
6053       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
6054       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
6055     }
6056   } else {
6057     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6058     if (clperm) {
6059       if (perm) {
6060         for (b = 0; b < fdof; b++) {
6061           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6062           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6063         }
6064       } else {
6065         for (b = 0; b < fdof; b++) {
6066           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6067           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6068         }
6069       }
6070     } else {
6071       if (perm) {
6072         for (b = 0; b < fdof; b++) {
6073           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6074           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6075         }
6076       } else {
6077         for (b = 0; b < fdof; b++) {
6078           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6079           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6080         }
6081       }
6082     }
6083   }
6084   *offset += fdof;
6085   PetscFunctionReturn(0);
6086 }
6087 
6088 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[])
6089 {
6090   PetscScalar    *a;
6091   PetscInt        fdof, foff, fcdof, foffset = *offset;
6092   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6093   PetscInt        Nc, cind = 0, ncind = 0, b;
6094   PetscBool       ncSet, fcSet;
6095 
6096   PetscFunctionBegin;
6097   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6098   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6099   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6100   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6101   a    = &array[foff];
6102   if (fcdof) {
6103     /* We just override fcdof and fcdofs with Ncc and comps */
6104     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6105     if (clperm) {
6106       if (perm) {
6107         if (comps) {
6108           for (b = 0; b < fdof; b++) {
6109             ncSet = fcSet = PETSC_FALSE;
6110             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6111             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6112             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
6113           }
6114         } else {
6115           for (b = 0; b < fdof; b++) {
6116             if ((cind < fcdof) && (b == fcdofs[cind])) {
6117               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6118               ++cind;
6119             }
6120           }
6121         }
6122       } else {
6123         if (comps) {
6124           for (b = 0; b < fdof; b++) {
6125             ncSet = fcSet = PETSC_FALSE;
6126             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6127             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6128             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
6129           }
6130         } else {
6131           for (b = 0; b < fdof; b++) {
6132             if ((cind < fcdof) && (b == fcdofs[cind])) {
6133               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6134               ++cind;
6135             }
6136           }
6137         }
6138       }
6139     } else {
6140       if (perm) {
6141         if (comps) {
6142           for (b = 0; b < fdof; b++) {
6143             ncSet = fcSet = PETSC_FALSE;
6144             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6145             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6146             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
6147           }
6148         } else {
6149           for (b = 0; b < fdof; b++) {
6150             if ((cind < fcdof) && (b == fcdofs[cind])) {
6151               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6152               ++cind;
6153             }
6154           }
6155         }
6156       } else {
6157         if (comps) {
6158           for (b = 0; b < fdof; b++) {
6159             ncSet = fcSet = PETSC_FALSE;
6160             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6161             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6162             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
6163           }
6164         } else {
6165           for (b = 0; b < fdof; b++) {
6166             if ((cind < fcdof) && (b == fcdofs[cind])) {
6167               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6168               ++cind;
6169             }
6170           }
6171         }
6172       }
6173     }
6174   }
6175   *offset += fdof;
6176   PetscFunctionReturn(0);
6177 }
6178 
6179 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6180 {
6181   PetscScalar    *array;
6182   const PetscInt *cone, *coneO;
6183   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6184 
6185   PetscFunctionBeginHot;
6186   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6187   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6188   PetscCall(DMPlexGetCone(dm, point, &cone));
6189   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6190   PetscCall(VecGetArray(v, &array));
6191   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6192     const PetscInt cp = !p ? point : cone[p-1];
6193     const PetscInt o  = !p ? 0     : coneO[p-1];
6194 
6195     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
6196     PetscCall(PetscSectionGetDof(section, cp, &dof));
6197     /* ADD_VALUES */
6198     {
6199       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6200       PetscScalar    *a;
6201       PetscInt        cdof, coff, cind = 0, k;
6202 
6203       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6204       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6205       a    = &array[coff];
6206       if (!cdof) {
6207         if (o >= 0) {
6208           for (k = 0; k < dof; ++k) {
6209             a[k] += values[off+k];
6210           }
6211         } else {
6212           for (k = 0; k < dof; ++k) {
6213             a[k] += values[off+dof-k-1];
6214           }
6215         }
6216       } else {
6217         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6218         if (o >= 0) {
6219           for (k = 0; k < dof; ++k) {
6220             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6221             a[k] += values[off+k];
6222           }
6223         } else {
6224           for (k = 0; k < dof; ++k) {
6225             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6226             a[k] += values[off+dof-k-1];
6227           }
6228         }
6229       }
6230     }
6231   }
6232   PetscCall(VecRestoreArray(v, &array));
6233   PetscFunctionReturn(0);
6234 }
6235 
6236 /*@C
6237   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6238 
6239   Not collective
6240 
6241   Input Parameters:
6242 + dm - The DM
6243 . section - The section describing the layout in v, or NULL to use the default section
6244 . v - The local vector
6245 . point - The point in the DM
6246 . values - The array of values
6247 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6248          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6249 
6250   Fortran Notes:
6251   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6252 
6253   Level: intermediate
6254 
6255 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6256 @*/
6257 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6258 {
6259   PetscSection    clSection;
6260   IS              clPoints;
6261   PetscScalar    *array;
6262   PetscInt       *points = NULL;
6263   const PetscInt *clp, *clperm = NULL;
6264   PetscInt        depth, numFields, numPoints, p, clsize;
6265 
6266   PetscFunctionBeginHot;
6267   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6268   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6269   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6270   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6271   PetscCall(DMPlexGetDepth(dm, &depth));
6272   PetscCall(PetscSectionGetNumFields(section, &numFields));
6273   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6274     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6275     PetscFunctionReturn(0);
6276   }
6277   /* Get points */
6278   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6279   for (clsize=0,p=0; p<numPoints; p++) {
6280     PetscInt dof;
6281     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
6282     clsize += dof;
6283   }
6284   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
6285   /* Get array */
6286   PetscCall(VecGetArray(v, &array));
6287   /* Get values */
6288   if (numFields > 0) {
6289     PetscInt offset = 0, f;
6290     for (f = 0; f < numFields; ++f) {
6291       const PetscInt    **perms = NULL;
6292       const PetscScalar **flips = NULL;
6293 
6294       PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6295       switch (mode) {
6296       case INSERT_VALUES:
6297         for (p = 0; p < numPoints; p++) {
6298           const PetscInt    point = points[2*p];
6299           const PetscInt    *perm = perms ? perms[p] : NULL;
6300           const PetscScalar *flip = flips ? flips[p] : NULL;
6301           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6302         } break;
6303       case INSERT_ALL_VALUES:
6304         for (p = 0; p < numPoints; p++) {
6305           const PetscInt    point = points[2*p];
6306           const PetscInt    *perm = perms ? perms[p] : NULL;
6307           const PetscScalar *flip = flips ? flips[p] : NULL;
6308           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6309         } break;
6310       case INSERT_BC_VALUES:
6311         for (p = 0; p < numPoints; p++) {
6312           const PetscInt    point = points[2*p];
6313           const PetscInt    *perm = perms ? perms[p] : NULL;
6314           const PetscScalar *flip = flips ? flips[p] : NULL;
6315           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6316         } break;
6317       case ADD_VALUES:
6318         for (p = 0; p < numPoints; p++) {
6319           const PetscInt    point = points[2*p];
6320           const PetscInt    *perm = perms ? perms[p] : NULL;
6321           const PetscScalar *flip = flips ? flips[p] : NULL;
6322           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6323         } break;
6324       case ADD_ALL_VALUES:
6325         for (p = 0; p < numPoints; p++) {
6326           const PetscInt    point = points[2*p];
6327           const PetscInt    *perm = perms ? perms[p] : NULL;
6328           const PetscScalar *flip = flips ? flips[p] : NULL;
6329           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6330         } break;
6331       case ADD_BC_VALUES:
6332         for (p = 0; p < numPoints; p++) {
6333           const PetscInt    point = points[2*p];
6334           const PetscInt    *perm = perms ? perms[p] : NULL;
6335           const PetscScalar *flip = flips ? flips[p] : NULL;
6336           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6337         } break;
6338       default:
6339         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6340       }
6341       PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6342     }
6343   } else {
6344     PetscInt dof, off;
6345     const PetscInt    **perms = NULL;
6346     const PetscScalar **flips = NULL;
6347 
6348     PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
6349     switch (mode) {
6350     case INSERT_VALUES:
6351       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6352         const PetscInt    point = points[2*p];
6353         const PetscInt    *perm = perms ? perms[p] : NULL;
6354         const PetscScalar *flip = flips ? flips[p] : NULL;
6355         PetscCall(PetscSectionGetDof(section, point, &dof));
6356         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6357       } break;
6358     case INSERT_ALL_VALUES:
6359       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6360         const PetscInt    point = points[2*p];
6361         const PetscInt    *perm = perms ? perms[p] : NULL;
6362         const PetscScalar *flip = flips ? flips[p] : NULL;
6363         PetscCall(PetscSectionGetDof(section, point, &dof));
6364         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
6365       } break;
6366     case INSERT_BC_VALUES:
6367       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6368         const PetscInt    point = points[2*p];
6369         const PetscInt    *perm = perms ? perms[p] : NULL;
6370         const PetscScalar *flip = flips ? flips[p] : NULL;
6371         PetscCall(PetscSectionGetDof(section, point, &dof));
6372         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
6373       } break;
6374     case ADD_VALUES:
6375       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6376         const PetscInt    point = points[2*p];
6377         const PetscInt    *perm = perms ? perms[p] : NULL;
6378         const PetscScalar *flip = flips ? flips[p] : NULL;
6379         PetscCall(PetscSectionGetDof(section, point, &dof));
6380         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
6381       } break;
6382     case ADD_ALL_VALUES:
6383       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6384         const PetscInt    point = points[2*p];
6385         const PetscInt    *perm = perms ? perms[p] : NULL;
6386         const PetscScalar *flip = flips ? flips[p] : NULL;
6387         PetscCall(PetscSectionGetDof(section, point, &dof));
6388         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
6389       } break;
6390     case ADD_BC_VALUES:
6391       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6392         const PetscInt    point = points[2*p];
6393         const PetscInt    *perm = perms ? perms[p] : NULL;
6394         const PetscScalar *flip = flips ? flips[p] : NULL;
6395         PetscCall(PetscSectionGetDof(section, point, &dof));
6396         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
6397       } break;
6398     default:
6399       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6400     }
6401     PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
6402   }
6403   /* Cleanup points */
6404   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6405   /* Cleanup array */
6406   PetscCall(VecRestoreArray(v, &array));
6407   PetscFunctionReturn(0);
6408 }
6409 
6410 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6411 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
6412 {
6413   PetscFunctionBegin;
6414   if (label) {
6415     PetscInt       val, fdof;
6416 
6417     /* There is a problem with this:
6418          Suppose we have two label values, defining surfaces, interecting along a line in 3D. When we add cells to the label, the cells that
6419        touch both surfaces must pick a label value. Thus we miss setting values for the surface with that other value intersecting that cell.
6420        Thus I am only going to check val != -1, not val != labelId
6421     */
6422     PetscCall(DMLabelGetValue(label, point, &val));
6423     if (val < 0) {
6424       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6425       *offset += fdof;
6426       PetscFunctionReturn(1);
6427     }
6428   }
6429   PetscFunctionReturn(0);
6430 }
6431 
6432 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6433 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)
6434 {
6435   PetscSection    clSection;
6436   IS              clPoints;
6437   PetscScalar    *array;
6438   PetscInt       *points = NULL;
6439   const PetscInt *clp;
6440   PetscInt        numFields, numPoints, p;
6441   PetscInt        offset = 0, f;
6442 
6443   PetscFunctionBeginHot;
6444   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6445   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6446   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6447   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6448   PetscCall(PetscSectionGetNumFields(section, &numFields));
6449   /* Get points */
6450   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6451   /* Get array */
6452   PetscCall(VecGetArray(v, &array));
6453   /* Get values */
6454   for (f = 0; f < numFields; ++f) {
6455     const PetscInt    **perms = NULL;
6456     const PetscScalar **flips = NULL;
6457 
6458     if (!fieldActive[f]) {
6459       for (p = 0; p < numPoints*2; p += 2) {
6460         PetscInt fdof;
6461         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6462         offset += fdof;
6463       }
6464       continue;
6465     }
6466     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6467     switch (mode) {
6468     case INSERT_VALUES:
6469       for (p = 0; p < numPoints; p++) {
6470         const PetscInt    point = points[2*p];
6471         const PetscInt    *perm = perms ? perms[p] : NULL;
6472         const PetscScalar *flip = flips ? flips[p] : NULL;
6473         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6474         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6475       } break;
6476     case INSERT_ALL_VALUES:
6477       for (p = 0; p < numPoints; p++) {
6478         const PetscInt    point = points[2*p];
6479         const PetscInt    *perm = perms ? perms[p] : NULL;
6480         const PetscScalar *flip = flips ? flips[p] : NULL;
6481         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6482         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6483       } break;
6484     case INSERT_BC_VALUES:
6485       for (p = 0; p < numPoints; p++) {
6486         const PetscInt    point = points[2*p];
6487         const PetscInt    *perm = perms ? perms[p] : NULL;
6488         const PetscScalar *flip = flips ? flips[p] : NULL;
6489         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6490         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6491       } break;
6492     case ADD_VALUES:
6493       for (p = 0; p < numPoints; p++) {
6494         const PetscInt    point = points[2*p];
6495         const PetscInt    *perm = perms ? perms[p] : NULL;
6496         const PetscScalar *flip = flips ? flips[p] : NULL;
6497         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6498         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6499       } break;
6500     case ADD_ALL_VALUES:
6501       for (p = 0; p < numPoints; p++) {
6502         const PetscInt    point = points[2*p];
6503         const PetscInt    *perm = perms ? perms[p] : NULL;
6504         const PetscScalar *flip = flips ? flips[p] : NULL;
6505         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6506         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6507       } break;
6508     default:
6509       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6510     }
6511     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6512   }
6513   /* Cleanup points */
6514   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6515   /* Cleanup array */
6516   PetscCall(VecRestoreArray(v, &array));
6517   PetscFunctionReturn(0);
6518 }
6519 
6520 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6521 {
6522   PetscMPIInt    rank;
6523   PetscInt       i, j;
6524 
6525   PetscFunctionBegin;
6526   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6527   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6528   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6529   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6530   numCIndices = numCIndices ? numCIndices : numRIndices;
6531   if (!values) PetscFunctionReturn(0);
6532   for (i = 0; i < numRIndices; i++) {
6533     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6534     for (j = 0; j < numCIndices; j++) {
6535 #if defined(PETSC_USE_COMPLEX)
6536       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j])));
6537 #else
6538       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]));
6539 #endif
6540     }
6541     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6542   }
6543   PetscFunctionReturn(0);
6544 }
6545 
6546 /*
6547   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6548 
6549   Input Parameters:
6550 + section - The section for this data layout
6551 . islocal - Is the section (and thus indices being requested) local or global?
6552 . point   - The point contributing dofs with these indices
6553 . off     - The global offset of this point
6554 . loff    - The local offset of each field
6555 . setBC   - The flag determining whether to include indices of boundary values
6556 . perm    - A permutation of the dofs on this point, or NULL
6557 - indperm - A permutation of the entire indices array, or NULL
6558 
6559   Output Parameter:
6560 . indices - Indices for dofs on this point
6561 
6562   Level: developer
6563 
6564   Note: The indices could be local or global, depending on the value of 'off'.
6565 */
6566 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6567 {
6568   PetscInt        dof;   /* The number of unknowns on this point */
6569   PetscInt        cdof;  /* The number of constraints on this point */
6570   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6571   PetscInt        cind = 0, k;
6572 
6573   PetscFunctionBegin;
6574   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6575   PetscCall(PetscSectionGetDof(section, point, &dof));
6576   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6577   if (!cdof || setBC) {
6578     for (k = 0; k < dof; ++k) {
6579       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6580       const PetscInt ind    = indperm ? indperm[preind] : preind;
6581 
6582       indices[ind] = off + k;
6583     }
6584   } else {
6585     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6586     for (k = 0; k < dof; ++k) {
6587       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6588       const PetscInt ind    = indperm ? indperm[preind] : preind;
6589 
6590       if ((cind < cdof) && (k == cdofs[cind])) {
6591         /* Insert check for returning constrained indices */
6592         indices[ind] = -(off+k+1);
6593         ++cind;
6594       } else {
6595         indices[ind] = off + k - (islocal ? 0 : cind);
6596       }
6597     }
6598   }
6599   *loff += dof;
6600   PetscFunctionReturn(0);
6601 }
6602 
6603 /*
6604  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6605 
6606  Input Parameters:
6607 + section - a section (global or local)
6608 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6609 . point - point within section
6610 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6611 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6612 . setBC - identify constrained (boundary condition) points via involution.
6613 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6614 . permsoff - offset
6615 - indperm - index permutation
6616 
6617  Output Parameter:
6618 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6619 . indices - array to hold indices (as defined by section) of each dof associated with point
6620 
6621  Notes:
6622  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6623  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6624  in the local vector.
6625 
6626  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6627  significant).  It is invalid to call with a global section and setBC=true.
6628 
6629  Developer Note:
6630  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6631  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6632  offset could be obtained from the section instead of passing it explicitly as we do now.
6633 
6634  Example:
6635  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6636  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6637  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6638  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.
6639 
6640  Level: developer
6641 */
6642 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[])
6643 {
6644   PetscInt       numFields, foff, f;
6645 
6646   PetscFunctionBegin;
6647   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6648   PetscCall(PetscSectionGetNumFields(section, &numFields));
6649   for (f = 0, foff = 0; f < numFields; ++f) {
6650     PetscInt        fdof, cfdof;
6651     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6652     PetscInt        cind = 0, b;
6653     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6654 
6655     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6656     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6657     if (!cfdof || setBC) {
6658       for (b = 0; b < fdof; ++b) {
6659         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6660         const PetscInt ind    = indperm ? indperm[preind] : preind;
6661 
6662         indices[ind] = off+foff+b;
6663       }
6664     } else {
6665       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6666       for (b = 0; b < fdof; ++b) {
6667         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6668         const PetscInt ind    = indperm ? indperm[preind] : preind;
6669 
6670         if ((cind < cfdof) && (b == fcdofs[cind])) {
6671           indices[ind] = -(off+foff+b+1);
6672           ++cind;
6673         } else {
6674           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6675         }
6676       }
6677     }
6678     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6679     foffs[f] += fdof;
6680   }
6681   PetscFunctionReturn(0);
6682 }
6683 
6684 /*
6685   This version believes the globalSection offsets for each field, rather than just the point offset
6686 
6687  . foffs - The offset into 'indices' for each field, since it is segregated by field
6688 
6689  Notes:
6690  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6691  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6692 */
6693 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6694 {
6695   PetscInt       numFields, foff, f;
6696 
6697   PetscFunctionBegin;
6698   PetscCall(PetscSectionGetNumFields(section, &numFields));
6699   for (f = 0; f < numFields; ++f) {
6700     PetscInt        fdof, cfdof;
6701     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6702     PetscInt        cind = 0, b;
6703     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6704 
6705     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6706     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6707     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6708     if (!cfdof) {
6709       for (b = 0; b < fdof; ++b) {
6710         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6711         const PetscInt ind    = indperm ? indperm[preind] : preind;
6712 
6713         indices[ind] = foff+b;
6714       }
6715     } else {
6716       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6717       for (b = 0; b < fdof; ++b) {
6718         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6719         const PetscInt ind    = indperm ? indperm[preind] : preind;
6720 
6721         if ((cind < cfdof) && (b == fcdofs[cind])) {
6722           indices[ind] = -(foff+b+1);
6723           ++cind;
6724         } else {
6725           indices[ind] = foff+b-cind;
6726         }
6727       }
6728     }
6729     foffs[f] += fdof;
6730   }
6731   PetscFunctionReturn(0);
6732 }
6733 
6734 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)
6735 {
6736   Mat             cMat;
6737   PetscSection    aSec, cSec;
6738   IS              aIS;
6739   PetscInt        aStart = -1, aEnd = -1;
6740   const PetscInt  *anchors;
6741   PetscInt        numFields, f, p, q, newP = 0;
6742   PetscInt        newNumPoints = 0, newNumIndices = 0;
6743   PetscInt        *newPoints, *indices, *newIndices;
6744   PetscInt        maxAnchor, maxDof;
6745   PetscInt        newOffsets[32];
6746   PetscInt        *pointMatOffsets[32];
6747   PetscInt        *newPointOffsets[32];
6748   PetscScalar     *pointMat[32];
6749   PetscScalar     *newValues=NULL,*tmpValues;
6750   PetscBool       anyConstrained = PETSC_FALSE;
6751 
6752   PetscFunctionBegin;
6753   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6754   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6755   PetscCall(PetscSectionGetNumFields(section, &numFields));
6756 
6757   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
6758   /* if there are point-to-point constraints */
6759   if (aSec) {
6760     PetscCall(PetscArrayzero(newOffsets, 32));
6761     PetscCall(ISGetIndices(aIS,&anchors));
6762     PetscCall(PetscSectionGetChart(aSec,&aStart,&aEnd));
6763     /* figure out how many points are going to be in the new element matrix
6764      * (we allow double counting, because it's all just going to be summed
6765      * into the global matrix anyway) */
6766     for (p = 0; p < 2*numPoints; p+=2) {
6767       PetscInt b    = points[p];
6768       PetscInt bDof = 0, bSecDof;
6769 
6770       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6771       if (!bSecDof) {
6772         continue;
6773       }
6774       if (b >= aStart && b < aEnd) {
6775         PetscCall(PetscSectionGetDof(aSec,b,&bDof));
6776       }
6777       if (bDof) {
6778         /* this point is constrained */
6779         /* it is going to be replaced by its anchors */
6780         PetscInt bOff, q;
6781 
6782         anyConstrained = PETSC_TRUE;
6783         newNumPoints  += bDof;
6784         PetscCall(PetscSectionGetOffset(aSec,b,&bOff));
6785         for (q = 0; q < bDof; q++) {
6786           PetscInt a = anchors[bOff + q];
6787           PetscInt aDof;
6788 
6789           PetscCall(PetscSectionGetDof(section,a,&aDof));
6790           newNumIndices += aDof;
6791           for (f = 0; f < numFields; ++f) {
6792             PetscInt fDof;
6793 
6794             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6795             newOffsets[f+1] += fDof;
6796           }
6797         }
6798       }
6799       else {
6800         /* this point is not constrained */
6801         newNumPoints++;
6802         newNumIndices += bSecDof;
6803         for (f = 0; f < numFields; ++f) {
6804           PetscInt fDof;
6805 
6806           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6807           newOffsets[f+1] += fDof;
6808         }
6809       }
6810     }
6811   }
6812   if (!anyConstrained) {
6813     if (outNumPoints)  *outNumPoints  = 0;
6814     if (outNumIndices) *outNumIndices = 0;
6815     if (outPoints)     *outPoints     = NULL;
6816     if (outValues)     *outValues     = NULL;
6817     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6818     PetscFunctionReturn(0);
6819   }
6820 
6821   if (outNumPoints)  *outNumPoints  = newNumPoints;
6822   if (outNumIndices) *outNumIndices = newNumIndices;
6823 
6824   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6825 
6826   if (!outPoints && !outValues) {
6827     if (offsets) {
6828       for (f = 0; f <= numFields; f++) {
6829         offsets[f] = newOffsets[f];
6830       }
6831     }
6832     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6833     PetscFunctionReturn(0);
6834   }
6835 
6836   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6837 
6838   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6839 
6840   /* workspaces */
6841   if (numFields) {
6842     for (f = 0; f < numFields; f++) {
6843       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
6844       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
6845     }
6846   }
6847   else {
6848     PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
6849     PetscCall(DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]));
6850   }
6851 
6852   /* get workspaces for the point-to-point matrices */
6853   if (numFields) {
6854     PetscInt totalOffset, totalMatOffset;
6855 
6856     for (p = 0; p < numPoints; p++) {
6857       PetscInt b    = points[2*p];
6858       PetscInt bDof = 0, bSecDof;
6859 
6860       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6861       if (!bSecDof) {
6862         for (f = 0; f < numFields; f++) {
6863           newPointOffsets[f][p + 1] = 0;
6864           pointMatOffsets[f][p + 1] = 0;
6865         }
6866         continue;
6867       }
6868       if (b >= aStart && b < aEnd) {
6869         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6870       }
6871       if (bDof) {
6872         for (f = 0; f < numFields; f++) {
6873           PetscInt fDof, q, bOff, allFDof = 0;
6874 
6875           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6876           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6877           for (q = 0; q < bDof; q++) {
6878             PetscInt a = anchors[bOff + q];
6879             PetscInt aFDof;
6880 
6881             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6882             allFDof += aFDof;
6883           }
6884           newPointOffsets[f][p+1] = allFDof;
6885           pointMatOffsets[f][p+1] = fDof * allFDof;
6886         }
6887       }
6888       else {
6889         for (f = 0; f < numFields; f++) {
6890           PetscInt fDof;
6891 
6892           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6893           newPointOffsets[f][p+1] = fDof;
6894           pointMatOffsets[f][p+1] = 0;
6895         }
6896       }
6897     }
6898     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6899       newPointOffsets[f][0] = totalOffset;
6900       pointMatOffsets[f][0] = totalMatOffset;
6901       for (p = 0; p < numPoints; p++) {
6902         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6903         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6904       }
6905       totalOffset    = newPointOffsets[f][numPoints];
6906       totalMatOffset = pointMatOffsets[f][numPoints];
6907       PetscCall(DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
6908     }
6909   }
6910   else {
6911     for (p = 0; p < numPoints; p++) {
6912       PetscInt b    = points[2*p];
6913       PetscInt bDof = 0, bSecDof;
6914 
6915       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6916       if (!bSecDof) {
6917         newPointOffsets[0][p + 1] = 0;
6918         pointMatOffsets[0][p + 1] = 0;
6919         continue;
6920       }
6921       if (b >= aStart && b < aEnd) {
6922         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6923       }
6924       if (bDof) {
6925         PetscInt bOff, q, allDof = 0;
6926 
6927         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6928         for (q = 0; q < bDof; q++) {
6929           PetscInt a = anchors[bOff + q], aDof;
6930 
6931           PetscCall(PetscSectionGetDof(section, a, &aDof));
6932           allDof += aDof;
6933         }
6934         newPointOffsets[0][p+1] = allDof;
6935         pointMatOffsets[0][p+1] = bSecDof * allDof;
6936       }
6937       else {
6938         newPointOffsets[0][p+1] = bSecDof;
6939         pointMatOffsets[0][p+1] = 0;
6940       }
6941     }
6942     newPointOffsets[0][0] = 0;
6943     pointMatOffsets[0][0] = 0;
6944     for (p = 0; p < numPoints; p++) {
6945       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6946       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6947     }
6948     PetscCall(DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
6949   }
6950 
6951   /* output arrays */
6952   PetscCall(DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
6953 
6954   /* get the point-to-point matrices; construct newPoints */
6955   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6956   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6957   PetscCall(DMGetWorkArray(dm,maxDof,MPIU_INT,&indices));
6958   PetscCall(DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
6959   if (numFields) {
6960     for (p = 0, newP = 0; p < numPoints; p++) {
6961       PetscInt b    = points[2*p];
6962       PetscInt o    = points[2*p+1];
6963       PetscInt bDof = 0, bSecDof;
6964 
6965       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6966       if (!bSecDof) {
6967         continue;
6968       }
6969       if (b >= aStart && b < aEnd) {
6970         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6971       }
6972       if (bDof) {
6973         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6974 
6975         fStart[0] = 0;
6976         fEnd[0]   = 0;
6977         for (f = 0; f < numFields; f++) {
6978           PetscInt fDof;
6979 
6980           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
6981           fStart[f+1] = fStart[f] + fDof;
6982           fEnd[f+1]   = fStart[f+1];
6983         }
6984         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
6985         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
6986 
6987         fAnchorStart[0] = 0;
6988         fAnchorEnd[0]   = 0;
6989         for (f = 0; f < numFields; f++) {
6990           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
6991 
6992           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
6993           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
6994         }
6995         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6996         for (q = 0; q < bDof; q++) {
6997           PetscInt a = anchors[bOff + q], aOff;
6998 
6999           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7000           newPoints[2*(newP + q)]     = a;
7001           newPoints[2*(newP + q) + 1] = 0;
7002           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7003           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7004         }
7005         newP += bDof;
7006 
7007         if (outValues) {
7008           /* get the point-to-point submatrix */
7009           for (f = 0; f < numFields; f++) {
7010             PetscCall(MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]));
7011           }
7012         }
7013       }
7014       else {
7015         newPoints[2 * newP]     = b;
7016         newPoints[2 * newP + 1] = o;
7017         newP++;
7018       }
7019     }
7020   } else {
7021     for (p = 0; p < numPoints; p++) {
7022       PetscInt b    = points[2*p];
7023       PetscInt o    = points[2*p+1];
7024       PetscInt bDof = 0, bSecDof;
7025 
7026       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7027       if (!bSecDof) {
7028         continue;
7029       }
7030       if (b >= aStart && b < aEnd) {
7031         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7032       }
7033       if (bDof) {
7034         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7035 
7036         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7037         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7038 
7039         PetscCall(PetscSectionGetOffset (aSec, b, &bOff));
7040         for (q = 0; q < bDof; q++) {
7041           PetscInt a = anchors[bOff + q], aOff;
7042 
7043           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7044 
7045           newPoints[2*(newP + q)]     = a;
7046           newPoints[2*(newP + q) + 1] = 0;
7047           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7048           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7049         }
7050         newP += bDof;
7051 
7052         /* get the point-to-point submatrix */
7053         if (outValues) {
7054           PetscCall(MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]));
7055         }
7056       }
7057       else {
7058         newPoints[2 * newP]     = b;
7059         newPoints[2 * newP + 1] = o;
7060         newP++;
7061       }
7062     }
7063   }
7064 
7065   if (outValues) {
7066     PetscCall(DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7067     PetscCall(PetscArrayzero(tmpValues,newNumIndices*numIndices));
7068     /* multiply constraints on the right */
7069     if (numFields) {
7070       for (f = 0; f < numFields; f++) {
7071         PetscInt oldOff = offsets[f];
7072 
7073         for (p = 0; p < numPoints; p++) {
7074           PetscInt cStart = newPointOffsets[f][p];
7075           PetscInt b      = points[2 * p];
7076           PetscInt c, r, k;
7077           PetscInt dof;
7078 
7079           PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7080           if (!dof) {
7081             continue;
7082           }
7083           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7084             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
7085             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
7086 
7087             for (r = 0; r < numIndices; r++) {
7088               for (c = 0; c < nCols; c++) {
7089                 for (k = 0; k < dof; k++) {
7090                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7091                 }
7092               }
7093             }
7094           }
7095           else {
7096             /* copy this column as is */
7097             for (r = 0; r < numIndices; r++) {
7098               for (c = 0; c < dof; c++) {
7099                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7100               }
7101             }
7102           }
7103           oldOff += dof;
7104         }
7105       }
7106     }
7107     else {
7108       PetscInt oldOff = 0;
7109       for (p = 0; p < numPoints; p++) {
7110         PetscInt cStart = newPointOffsets[0][p];
7111         PetscInt b      = points[2 * p];
7112         PetscInt c, r, k;
7113         PetscInt dof;
7114 
7115         PetscCall(PetscSectionGetDof(section,b,&dof));
7116         if (!dof) {
7117           continue;
7118         }
7119         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7120           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
7121           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
7122 
7123           for (r = 0; r < numIndices; r++) {
7124             for (c = 0; c < nCols; c++) {
7125               for (k = 0; k < dof; k++) {
7126                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7127               }
7128             }
7129           }
7130         }
7131         else {
7132           /* copy this column as is */
7133           for (r = 0; r < numIndices; r++) {
7134             for (c = 0; c < dof; c++) {
7135               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7136             }
7137           }
7138         }
7139         oldOff += dof;
7140       }
7141     }
7142 
7143     if (multiplyLeft) {
7144       PetscCall(DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues));
7145       PetscCall(PetscArrayzero(newValues,newNumIndices*newNumIndices));
7146       /* multiply constraints transpose on the left */
7147       if (numFields) {
7148         for (f = 0; f < numFields; f++) {
7149           PetscInt oldOff = offsets[f];
7150 
7151           for (p = 0; p < numPoints; p++) {
7152             PetscInt rStart = newPointOffsets[f][p];
7153             PetscInt b      = points[2 * p];
7154             PetscInt c, r, k;
7155             PetscInt dof;
7156 
7157             PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7158             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7159               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
7160               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
7161 
7162               for (r = 0; r < nRows; r++) {
7163                 for (c = 0; c < newNumIndices; c++) {
7164                   for (k = 0; k < dof; k++) {
7165                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7166                   }
7167                 }
7168               }
7169             }
7170             else {
7171               /* copy this row as is */
7172               for (r = 0; r < dof; r++) {
7173                 for (c = 0; c < newNumIndices; c++) {
7174                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7175                 }
7176               }
7177             }
7178             oldOff += dof;
7179           }
7180         }
7181       }
7182       else {
7183         PetscInt oldOff = 0;
7184 
7185         for (p = 0; p < numPoints; p++) {
7186           PetscInt rStart = newPointOffsets[0][p];
7187           PetscInt b      = points[2 * p];
7188           PetscInt c, r, k;
7189           PetscInt dof;
7190 
7191           PetscCall(PetscSectionGetDof(section,b,&dof));
7192           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7193             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
7194             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
7195 
7196             for (r = 0; r < nRows; r++) {
7197               for (c = 0; c < newNumIndices; c++) {
7198                 for (k = 0; k < dof; k++) {
7199                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7200                 }
7201               }
7202             }
7203           }
7204           else {
7205             /* copy this row as is */
7206             for (r = 0; r < dof; r++) {
7207               for (c = 0; c < newNumIndices; c++) {
7208                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7209               }
7210             }
7211           }
7212           oldOff += dof;
7213         }
7214       }
7215 
7216       PetscCall(DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7217     }
7218     else {
7219       newValues = tmpValues;
7220     }
7221   }
7222 
7223   /* clean up */
7224   PetscCall(DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices));
7225   PetscCall(DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
7226 
7227   if (numFields) {
7228     for (f = 0; f < numFields; f++) {
7229       PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
7230       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
7231       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
7232     }
7233   }
7234   else {
7235     PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
7236     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
7237     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]));
7238   }
7239   PetscCall(ISRestoreIndices(aIS,&anchors));
7240 
7241   /* output */
7242   if (outPoints) {
7243     *outPoints = newPoints;
7244   }
7245   else {
7246     PetscCall(DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
7247   }
7248   if (outValues) {
7249     *outValues = newValues;
7250   }
7251   for (f = 0; f <= numFields; f++) {
7252     offsets[f] = newOffsets[f];
7253   }
7254   PetscFunctionReturn(0);
7255 }
7256 
7257 /*@C
7258   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7259 
7260   Not collective
7261 
7262   Input Parameters:
7263 + dm         - The DM
7264 . section    - The PetscSection describing the points (a local section)
7265 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7266 . point      - The point defining the closure
7267 - useClPerm  - Use the closure point permutation if available
7268 
7269   Output Parameters:
7270 + numIndices - The number of dof indices in the closure of point with the input sections
7271 . indices    - The dof indices
7272 . outOffsets - Array to write the field offsets into, or NULL
7273 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7274 
7275   Notes:
7276   Must call DMPlexRestoreClosureIndices() to free allocated memory
7277 
7278   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7279   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7280   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7281   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7282   indices (with the above semantics) are implied.
7283 
7284   Level: advanced
7285 
7286 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7287 @*/
7288 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7289                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7290 {
7291   /* Closure ordering */
7292   PetscSection        clSection;
7293   IS                  clPoints;
7294   const PetscInt     *clp;
7295   PetscInt           *points;
7296   const PetscInt     *clperm = NULL;
7297   /* Dof permutation and sign flips */
7298   const PetscInt    **perms[32] = {NULL};
7299   const PetscScalar **flips[32] = {NULL};
7300   PetscScalar        *valCopy   = NULL;
7301   /* Hanging node constraints */
7302   PetscInt           *pointsC = NULL;
7303   PetscScalar        *valuesC = NULL;
7304   PetscInt            NclC, NiC;
7305 
7306   PetscInt           *idx;
7307   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
7308   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7309 
7310   PetscFunctionBeginHot;
7311   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7312   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7313   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7314   if (numIndices) PetscValidIntPointer(numIndices, 6);
7315   if (indices)    PetscValidPointer(indices, 7);
7316   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7317   if (values)     PetscValidPointer(values, 9);
7318   PetscCall(PetscSectionGetNumFields(section, &Nf));
7319   PetscCheck(Nf <= 31,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7320   PetscCall(PetscArrayzero(offsets, 32));
7321   /* 1) Get points in closure */
7322   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7323   if (useClPerm) {
7324     PetscInt depth, clsize;
7325     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7326     for (clsize=0,p=0; p<Ncl; p++) {
7327       PetscInt dof;
7328       PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
7329       clsize += dof;
7330     }
7331     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
7332   }
7333   /* 2) Get number of indices on these points and field offsets from section */
7334   for (p = 0; p < Ncl*2; p += 2) {
7335     PetscInt dof, fdof;
7336 
7337     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7338     for (f = 0; f < Nf; ++f) {
7339       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7340       offsets[f+1] += fdof;
7341     }
7342     Ni += dof;
7343   }
7344   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
7345   PetscCheck(!Nf || offsets[Nf] == Ni,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7346   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7347   for (f = 0; f < PetscMax(1, Nf); ++f) {
7348     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7349     else    PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7350     /* may need to apply sign changes to the element matrix */
7351     if (values && flips[f]) {
7352       PetscInt foffset = offsets[f];
7353 
7354       for (p = 0; p < Ncl; ++p) {
7355         PetscInt           pnt  = points[2*p], fdof;
7356         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7357 
7358         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7359         else     PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7360         if (flip) {
7361           PetscInt i, j, k;
7362 
7363           if (!valCopy) {
7364             PetscCall(DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7365             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7366             *values = valCopy;
7367           }
7368           for (i = 0; i < fdof; ++i) {
7369             PetscScalar fval = flip[i];
7370 
7371             for (k = 0; k < Ni; ++k) {
7372               valCopy[Ni * (foffset + i) + k] *= fval;
7373               valCopy[Ni * k + (foffset + i)] *= fval;
7374             }
7375           }
7376         }
7377         foffset += fdof;
7378       }
7379     }
7380   }
7381   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7382   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7383   if (NclC) {
7384     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7385     for (f = 0; f < PetscMax(1, Nf); ++f) {
7386       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7387       else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7388     }
7389     for (f = 0; f < PetscMax(1, Nf); ++f) {
7390       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7391       else    PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7392     }
7393     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7394     Ncl     = NclC;
7395     Ni      = NiC;
7396     points  = pointsC;
7397     if (values) *values = valuesC;
7398   }
7399   /* 5) Calculate indices */
7400   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7401   if (Nf) {
7402     PetscInt  idxOff;
7403     PetscBool useFieldOffsets;
7404 
7405     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
7406     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7407     if (useFieldOffsets) {
7408       for (p = 0; p < Ncl; ++p) {
7409         const PetscInt pnt = points[p*2];
7410 
7411         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7412       }
7413     } else {
7414       for (p = 0; p < Ncl; ++p) {
7415         const PetscInt pnt = points[p*2];
7416 
7417         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7418         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7419          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7420          * global section. */
7421         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7422       }
7423     }
7424   } else {
7425     PetscInt off = 0, idxOff;
7426 
7427     for (p = 0; p < Ncl; ++p) {
7428       const PetscInt  pnt  = points[p*2];
7429       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7430 
7431       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7432       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7433        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7434       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7435     }
7436   }
7437   /* 6) Cleanup */
7438   for (f = 0; f < PetscMax(1, Nf); ++f) {
7439     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7440     else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7441   }
7442   if (NclC) {
7443     PetscCall(DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC));
7444   } else {
7445     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7446   }
7447 
7448   if (numIndices) *numIndices = Ni;
7449   if (indices)    *indices    = idx;
7450   PetscFunctionReturn(0);
7451 }
7452 
7453 /*@C
7454   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7455 
7456   Not collective
7457 
7458   Input Parameters:
7459 + dm         - The DM
7460 . section    - The PetscSection describing the points (a local section)
7461 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7462 . point      - The point defining the closure
7463 - useClPerm  - Use the closure point permutation if available
7464 
7465   Output Parameters:
7466 + numIndices - The number of dof indices in the closure of point with the input sections
7467 . indices    - The dof indices
7468 . outOffsets - Array to write the field offsets into, or NULL
7469 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7470 
7471   Notes:
7472   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7473 
7474   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7475   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7476   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7477   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7478   indices (with the above semantics) are implied.
7479 
7480   Level: advanced
7481 
7482 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7483 @*/
7484 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7485                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7486 {
7487   PetscFunctionBegin;
7488   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7489   PetscValidPointer(indices, 7);
7490   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7491   PetscFunctionReturn(0);
7492 }
7493 
7494 /*@C
7495   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7496 
7497   Not collective
7498 
7499   Input Parameters:
7500 + dm - The DM
7501 . section - The section describing the layout in v, or NULL to use the default section
7502 . globalSection - The section describing the layout in v, or NULL to use the default global section
7503 . A - The matrix
7504 . point - The point in the DM
7505 . values - The array of values
7506 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7507 
7508   Fortran Notes:
7509   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7510 
7511   Level: intermediate
7512 
7513 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7514 @*/
7515 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7516 {
7517   DM_Plex           *mesh = (DM_Plex*) dm->data;
7518   PetscInt          *indices;
7519   PetscInt           numIndices;
7520   const PetscScalar *valuesOrig = values;
7521   PetscErrorCode     ierr;
7522 
7523   PetscFunctionBegin;
7524   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7525   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7526   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7527   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7528   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7529   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7530 
7531   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7532 
7533   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7534   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7535   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7536   if (ierr) {
7537     PetscMPIInt    rank;
7538 
7539     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7540     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7541     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7542     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7543     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7544     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7545   }
7546   if (mesh->printFEM > 1) {
7547     PetscInt i;
7548     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7549     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7550     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7551   }
7552 
7553   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7554   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7555   PetscFunctionReturn(0);
7556 }
7557 
7558 /*@C
7559   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7560 
7561   Not collective
7562 
7563   Input Parameters:
7564 + dmRow - The DM for the row fields
7565 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7566 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7567 . dmCol - The DM for the column fields
7568 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7569 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7570 . A - The matrix
7571 . point - The point in the DMs
7572 . values - The array of values
7573 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7574 
7575   Level: intermediate
7576 
7577 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7578 @*/
7579 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7580 {
7581   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7582   PetscInt          *indicesRow, *indicesCol;
7583   PetscInt           numIndicesRow, numIndicesCol;
7584   const PetscScalar *valuesOrig = values;
7585   PetscErrorCode     ierr;
7586 
7587   PetscFunctionBegin;
7588   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7589   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7590   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7591   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7592   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7593   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7594   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7595   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7596   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7597   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7598   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7599 
7600   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7601   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7602 
7603   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7604   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7605   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7606   if (ierr) {
7607     PetscMPIInt    rank;
7608 
7609     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7610     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7611     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7612     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7613     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7614     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7615   }
7616 
7617   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7618   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7619   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7620   PetscFunctionReturn(0);
7621 }
7622 
7623 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7624 {
7625   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7626   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7627   PetscInt       *cpoints = NULL;
7628   PetscInt       *findices, *cindices;
7629   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7630   PetscInt        foffsets[32], coffsets[32];
7631   DMPolytopeType  ct;
7632   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7633   PetscErrorCode  ierr;
7634 
7635   PetscFunctionBegin;
7636   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7637   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7638   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7639   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7640   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7641   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7642   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7643   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7644   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7645   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7646   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7647   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7648   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7649   PetscCall(PetscArrayzero(foffsets, 32));
7650   PetscCall(PetscArrayzero(coffsets, 32));
7651   /* Column indices */
7652   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7653   maxFPoints = numCPoints;
7654   /* Compress out points not in the section */
7655   /*   TODO: Squeeze out points with 0 dof as well */
7656   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7657   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7658     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7659       cpoints[q*2]   = cpoints[p];
7660       cpoints[q*2+1] = cpoints[p+1];
7661       ++q;
7662     }
7663   }
7664   numCPoints = q;
7665   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7666     PetscInt fdof;
7667 
7668     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7669     if (!dof) continue;
7670     for (f = 0; f < numFields; ++f) {
7671       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7672       coffsets[f+1] += fdof;
7673     }
7674     numCIndices += dof;
7675   }
7676   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7677   /* Row indices */
7678   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7679   {
7680     DMPlexTransform tr;
7681     DMPolytopeType *rct;
7682     PetscInt       *rsize, *rcone, *rornt, Nt;
7683 
7684     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7685     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7686     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7687     numSubcells = rsize[Nt-1];
7688     PetscCall(DMPlexTransformDestroy(&tr));
7689   }
7690   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7691   for (r = 0, q = 0; r < numSubcells; ++r) {
7692     /* TODO Map from coarse to fine cells */
7693     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7694     /* Compress out points not in the section */
7695     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7696     for (p = 0; p < numFPoints*2; p += 2) {
7697       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7698         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7699         if (!dof) continue;
7700         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7701         if (s < q) continue;
7702         ftotpoints[q*2]   = fpoints[p];
7703         ftotpoints[q*2+1] = fpoints[p+1];
7704         ++q;
7705       }
7706     }
7707     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7708   }
7709   numFPoints = q;
7710   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7711     PetscInt fdof;
7712 
7713     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7714     if (!dof) continue;
7715     for (f = 0; f < numFields; ++f) {
7716       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7717       foffsets[f+1] += fdof;
7718     }
7719     numFIndices += dof;
7720   }
7721   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7722 
7723   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7724   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7725   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7726   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7727   if (numFields) {
7728     const PetscInt **permsF[32] = {NULL};
7729     const PetscInt **permsC[32] = {NULL};
7730 
7731     for (f = 0; f < numFields; f++) {
7732       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7733       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7734     }
7735     for (p = 0; p < numFPoints; p++) {
7736       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7737       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7738     }
7739     for (p = 0; p < numCPoints; p++) {
7740       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7741       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7742     }
7743     for (f = 0; f < numFields; f++) {
7744       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7745       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7746     }
7747   } else {
7748     const PetscInt **permsF = NULL;
7749     const PetscInt **permsC = NULL;
7750 
7751     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7752     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7753     for (p = 0, off = 0; p < numFPoints; p++) {
7754       const PetscInt *perm = permsF ? permsF[p] : NULL;
7755 
7756       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7757       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7758     }
7759     for (p = 0, off = 0; p < numCPoints; p++) {
7760       const PetscInt *perm = permsC ? permsC[p] : NULL;
7761 
7762       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7763       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7764     }
7765     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7766     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7767   }
7768   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7769   /* TODO: flips */
7770   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7771   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7772   if (ierr) {
7773     PetscMPIInt    rank;
7774 
7775     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7776     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7777     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7778     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7779     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7780   }
7781   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7782   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7783   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7784   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7785   PetscFunctionReturn(0);
7786 }
7787 
7788 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7789 {
7790   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7791   PetscInt      *cpoints = NULL;
7792   PetscInt       foffsets[32], coffsets[32];
7793   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7794   DMPolytopeType ct;
7795   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7796 
7797   PetscFunctionBegin;
7798   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7799   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7800   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7801   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7802   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7803   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7804   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7805   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7806   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7807   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7808   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7809   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7810   PetscCall(PetscArrayzero(foffsets, 32));
7811   PetscCall(PetscArrayzero(coffsets, 32));
7812   /* Column indices */
7813   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7814   maxFPoints = numCPoints;
7815   /* Compress out points not in the section */
7816   /*   TODO: Squeeze out points with 0 dof as well */
7817   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7818   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7819     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7820       cpoints[q*2]   = cpoints[p];
7821       cpoints[q*2+1] = cpoints[p+1];
7822       ++q;
7823     }
7824   }
7825   numCPoints = q;
7826   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7827     PetscInt fdof;
7828 
7829     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7830     if (!dof) continue;
7831     for (f = 0; f < numFields; ++f) {
7832       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7833       coffsets[f+1] += fdof;
7834     }
7835     numCIndices += dof;
7836   }
7837   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7838   /* Row indices */
7839   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7840   {
7841     DMPlexTransform tr;
7842     DMPolytopeType *rct;
7843     PetscInt       *rsize, *rcone, *rornt, Nt;
7844 
7845     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7846     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7847     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7848     numSubcells = rsize[Nt-1];
7849     PetscCall(DMPlexTransformDestroy(&tr));
7850   }
7851   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7852   for (r = 0, q = 0; r < numSubcells; ++r) {
7853     /* TODO Map from coarse to fine cells */
7854     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7855     /* Compress out points not in the section */
7856     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7857     for (p = 0; p < numFPoints*2; p += 2) {
7858       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7859         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7860         if (!dof) continue;
7861         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7862         if (s < q) continue;
7863         ftotpoints[q*2]   = fpoints[p];
7864         ftotpoints[q*2+1] = fpoints[p+1];
7865         ++q;
7866       }
7867     }
7868     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7869   }
7870   numFPoints = q;
7871   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7872     PetscInt fdof;
7873 
7874     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7875     if (!dof) continue;
7876     for (f = 0; f < numFields; ++f) {
7877       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7878       foffsets[f+1] += fdof;
7879     }
7880     numFIndices += dof;
7881   }
7882   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7883 
7884   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7885   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7886   if (numFields) {
7887     const PetscInt **permsF[32] = {NULL};
7888     const PetscInt **permsC[32] = {NULL};
7889 
7890     for (f = 0; f < numFields; f++) {
7891       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7892       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7893     }
7894     for (p = 0; p < numFPoints; p++) {
7895       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7896       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7897     }
7898     for (p = 0; p < numCPoints; p++) {
7899       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7900       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7901     }
7902     for (f = 0; f < numFields; f++) {
7903       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7904       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7905     }
7906   } else {
7907     const PetscInt **permsF = NULL;
7908     const PetscInt **permsC = NULL;
7909 
7910     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7911     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7912     for (p = 0, off = 0; p < numFPoints; p++) {
7913       const PetscInt *perm = permsF ? permsF[p] : NULL;
7914 
7915       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7916       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7917     }
7918     for (p = 0, off = 0; p < numCPoints; p++) {
7919       const PetscInt *perm = permsC ? permsC[p] : NULL;
7920 
7921       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7922       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7923     }
7924     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7925     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7926   }
7927   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7928   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7929   PetscFunctionReturn(0);
7930 }
7931 
7932 /*@C
7933   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7934 
7935   Input Parameter:
7936 . dm   - The DMPlex object
7937 
7938   Output Parameter:
7939 . cellHeight - The height of a cell
7940 
7941   Level: developer
7942 
7943 .seealso `DMPlexSetVTKCellHeight()`
7944 @*/
7945 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7946 {
7947   DM_Plex *mesh = (DM_Plex*) dm->data;
7948 
7949   PetscFunctionBegin;
7950   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7951   PetscValidIntPointer(cellHeight, 2);
7952   *cellHeight = mesh->vtkCellHeight;
7953   PetscFunctionReturn(0);
7954 }
7955 
7956 /*@C
7957   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7958 
7959   Input Parameters:
7960 + dm   - The DMPlex object
7961 - cellHeight - The height of a cell
7962 
7963   Level: developer
7964 
7965 .seealso `DMPlexGetVTKCellHeight()`
7966 @*/
7967 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7968 {
7969   DM_Plex *mesh = (DM_Plex*) dm->data;
7970 
7971   PetscFunctionBegin;
7972   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7973   mesh->vtkCellHeight = cellHeight;
7974   PetscFunctionReturn(0);
7975 }
7976 
7977 /*@
7978   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7979 
7980   Input Parameter:
7981 . dm - The DMPlex object
7982 
7983   Output Parameters:
7984 + gcStart - The first ghost cell, or NULL
7985 - gcEnd   - The upper bound on ghost cells, or NULL
7986 
7987   Level: advanced
7988 
7989 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
7990 @*/
7991 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
7992 {
7993   DMLabel        ctLabel;
7994 
7995   PetscFunctionBegin;
7996   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7997   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
7998   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
7999   PetscFunctionReturn(0);
8000 }
8001 
8002 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8003 {
8004   PetscSection   section, globalSection;
8005   PetscInt      *numbers, p;
8006 
8007   PetscFunctionBegin;
8008   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
8009   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8010   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8011   for (p = pStart; p < pEnd; ++p) {
8012     PetscCall(PetscSectionSetDof(section, p, 1));
8013   }
8014   PetscCall(PetscSectionSetUp(section));
8015   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8016   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8017   for (p = pStart; p < pEnd; ++p) {
8018     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8019     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8020     else                       numbers[p-pStart] += shift;
8021   }
8022   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8023   if (globalSize) {
8024     PetscLayout layout;
8025     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8026     PetscCall(PetscLayoutGetSize(layout, globalSize));
8027     PetscCall(PetscLayoutDestroy(&layout));
8028   }
8029   PetscCall(PetscSectionDestroy(&section));
8030   PetscCall(PetscSectionDestroy(&globalSection));
8031   PetscFunctionReturn(0);
8032 }
8033 
8034 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8035 {
8036   PetscInt       cellHeight, cStart, cEnd;
8037 
8038   PetscFunctionBegin;
8039   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8040   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8041   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8042   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8043   PetscFunctionReturn(0);
8044 }
8045 
8046 /*@
8047   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8048 
8049   Input Parameter:
8050 . dm   - The DMPlex object
8051 
8052   Output Parameter:
8053 . globalCellNumbers - Global cell numbers for all cells on this process
8054 
8055   Level: developer
8056 
8057 .seealso `DMPlexGetVertexNumbering()`
8058 @*/
8059 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8060 {
8061   DM_Plex       *mesh = (DM_Plex*) dm->data;
8062 
8063   PetscFunctionBegin;
8064   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8065   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8066   *globalCellNumbers = mesh->globalCellNumbers;
8067   PetscFunctionReturn(0);
8068 }
8069 
8070 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8071 {
8072   PetscInt       vStart, vEnd;
8073 
8074   PetscFunctionBegin;
8075   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8076   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8077   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8078   PetscFunctionReturn(0);
8079 }
8080 
8081 /*@
8082   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8083 
8084   Input Parameter:
8085 . dm   - The DMPlex object
8086 
8087   Output Parameter:
8088 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8089 
8090   Level: developer
8091 
8092 .seealso `DMPlexGetCellNumbering()`
8093 @*/
8094 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8095 {
8096   DM_Plex       *mesh = (DM_Plex*) dm->data;
8097 
8098   PetscFunctionBegin;
8099   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8100   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8101   *globalVertexNumbers = mesh->globalVertexNumbers;
8102   PetscFunctionReturn(0);
8103 }
8104 
8105 /*@
8106   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
8107 
8108   Input Parameter:
8109 . dm   - The DMPlex object
8110 
8111   Output Parameter:
8112 . globalPointNumbers - Global numbers for all points on this process
8113 
8114   Level: developer
8115 
8116 .seealso `DMPlexGetCellNumbering()`
8117 @*/
8118 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8119 {
8120   IS             nums[4];
8121   PetscInt       depths[4], gdepths[4], starts[4];
8122   PetscInt       depth, d, shift = 0;
8123 
8124   PetscFunctionBegin;
8125   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8126   PetscCall(DMPlexGetDepth(dm, &depth));
8127   /* For unstratified meshes use dim instead of depth */
8128   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8129   for (d = 0; d <= depth; ++d) {
8130     PetscInt end;
8131 
8132     depths[d] = depth-d;
8133     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8134     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8135   }
8136   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8137   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8138   for (d = 0; d <= depth; ++d) {
8139     PetscCheck(starts[d] < 0 || depths[d] == gdepths[d],PETSC_COMM_SELF,PETSC_ERR_PLIB,"Expected depth %" PetscInt_FMT ", found %" PetscInt_FMT,depths[d],gdepths[d]);
8140   }
8141   for (d = 0; d <= depth; ++d) {
8142     PetscInt pStart, pEnd, gsize;
8143 
8144     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8145     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8146     shift += gsize;
8147   }
8148   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8149   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8150   PetscFunctionReturn(0);
8151 }
8152 
8153 /*@
8154   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8155 
8156   Input Parameter:
8157 . dm - The DMPlex object
8158 
8159   Output Parameter:
8160 . ranks - The rank field
8161 
8162   Options Database Keys:
8163 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8164 
8165   Level: intermediate
8166 
8167 .seealso: `DMView()`
8168 @*/
8169 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8170 {
8171   DM             rdm;
8172   PetscFE        fe;
8173   PetscScalar   *r;
8174   PetscMPIInt    rank;
8175   DMPolytopeType ct;
8176   PetscInt       dim, cStart, cEnd, c;
8177   PetscBool      simplex;
8178 
8179   PetscFunctionBeginUser;
8180   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8181   PetscValidPointer(ranks, 2);
8182   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8183   PetscCall(DMClone(dm, &rdm));
8184   PetscCall(DMGetDimension(rdm, &dim));
8185   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8186   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8187   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8188   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8189   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8190   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8191   PetscCall(PetscFEDestroy(&fe));
8192   PetscCall(DMCreateDS(rdm));
8193   PetscCall(DMCreateGlobalVector(rdm, ranks));
8194   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8195   PetscCall(VecGetArray(*ranks, &r));
8196   for (c = cStart; c < cEnd; ++c) {
8197     PetscScalar *lr;
8198 
8199     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8200     if (lr) *lr = rank;
8201   }
8202   PetscCall(VecRestoreArray(*ranks, &r));
8203   PetscCall(DMDestroy(&rdm));
8204   PetscFunctionReturn(0);
8205 }
8206 
8207 /*@
8208   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8209 
8210   Input Parameters:
8211 + dm    - The DMPlex
8212 - label - The DMLabel
8213 
8214   Output Parameter:
8215 . val - The label value field
8216 
8217   Options Database Keys:
8218 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8219 
8220   Level: intermediate
8221 
8222 .seealso: `DMView()`
8223 @*/
8224 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8225 {
8226   DM             rdm;
8227   PetscFE        fe;
8228   PetscScalar   *v;
8229   PetscInt       dim, cStart, cEnd, c;
8230 
8231   PetscFunctionBeginUser;
8232   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8233   PetscValidPointer(label, 2);
8234   PetscValidPointer(val, 3);
8235   PetscCall(DMClone(dm, &rdm));
8236   PetscCall(DMGetDimension(rdm, &dim));
8237   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8238   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8239   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8240   PetscCall(PetscFEDestroy(&fe));
8241   PetscCall(DMCreateDS(rdm));
8242   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8243   PetscCall(DMCreateGlobalVector(rdm, val));
8244   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8245   PetscCall(VecGetArray(*val, &v));
8246   for (c = cStart; c < cEnd; ++c) {
8247     PetscScalar *lv;
8248     PetscInt     cval;
8249 
8250     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8251     PetscCall(DMLabelGetValue(label, c, &cval));
8252     *lv = cval;
8253   }
8254   PetscCall(VecRestoreArray(*val, &v));
8255   PetscCall(DMDestroy(&rdm));
8256   PetscFunctionReturn(0);
8257 }
8258 
8259 /*@
8260   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8261 
8262   Input Parameter:
8263 . dm - The DMPlex object
8264 
8265   Notes:
8266   This is a useful diagnostic when creating meshes programmatically.
8267 
8268   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8269 
8270   Level: developer
8271 
8272 .seealso: `DMCreate()`, `DMSetFromOptions()`
8273 @*/
8274 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8275 {
8276   PetscSection    coneSection, supportSection;
8277   const PetscInt *cone, *support;
8278   PetscInt        coneSize, c, supportSize, s;
8279   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8280   PetscBool       storagecheck = PETSC_TRUE;
8281 
8282   PetscFunctionBegin;
8283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8284   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8285   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8286   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8287   /* Check that point p is found in the support of its cone points, and vice versa */
8288   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8289   for (p = pStart; p < pEnd; ++p) {
8290     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8291     PetscCall(DMPlexGetCone(dm, p, &cone));
8292     for (c = 0; c < coneSize; ++c) {
8293       PetscBool dup = PETSC_FALSE;
8294       PetscInt  d;
8295       for (d = c-1; d >= 0; --d) {
8296         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8297       }
8298       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8299       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8300       for (s = 0; s < supportSize; ++s) {
8301         if (support[s] == p) break;
8302       }
8303       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8304         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8305         for (s = 0; s < coneSize; ++s) {
8306           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8307         }
8308         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8309         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8310         for (s = 0; s < supportSize; ++s) {
8311           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8312         }
8313         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8314         PetscCheck(!dup,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not repeatedly found in support of repeated cone point %" PetscInt_FMT, p, cone[c]);
8315         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8316       }
8317     }
8318     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8319     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8320     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8321     PetscCall(DMPlexGetSupport(dm, p, &support));
8322     for (s = 0; s < supportSize; ++s) {
8323       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8324       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8325       for (c = 0; c < coneSize; ++c) {
8326         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8327         if (cone[c] != pp) { c = 0; break; }
8328         if (cone[c] == p) break;
8329       }
8330       if (c >= coneSize) {
8331         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8332         for (c = 0; c < supportSize; ++c) {
8333           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8334         }
8335         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8336         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8337         for (c = 0; c < coneSize; ++c) {
8338           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8339         }
8340         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8341         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8342       }
8343     }
8344   }
8345   if (storagecheck) {
8346     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8347     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8348     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8349   }
8350   PetscFunctionReturn(0);
8351 }
8352 
8353 /*
8354   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.
8355 */
8356 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8357 {
8358   DMPolytopeType  cct;
8359   PetscInt        ptpoints[4];
8360   const PetscInt *cone, *ccone, *ptcone;
8361   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8362 
8363   PetscFunctionBegin;
8364   *unsplit = 0;
8365   switch (ct) {
8366     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8367       ptpoints[npt++] = c;
8368       break;
8369     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8370       PetscCall(DMPlexGetCone(dm, c, &cone));
8371       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8372       for (cp = 0; cp < coneSize; ++cp) {
8373         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8374         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8375       }
8376       break;
8377     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8378     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8379       PetscCall(DMPlexGetCone(dm, c, &cone));
8380       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8381       for (cp = 0; cp < coneSize; ++cp) {
8382         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8383         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8384         for (ccp = 0; ccp < cconeSize; ++ccp) {
8385           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8386           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8387             PetscInt p;
8388             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8389             if (p == npt) ptpoints[npt++] = ccone[ccp];
8390           }
8391         }
8392       }
8393       break;
8394     default: break;
8395   }
8396   for (pt = 0; pt < npt; ++pt) {
8397     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8398     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8399   }
8400   PetscFunctionReturn(0);
8401 }
8402 
8403 /*@
8404   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8405 
8406   Input Parameters:
8407 + dm - The DMPlex object
8408 - cellHeight - Normally 0
8409 
8410   Notes:
8411   This is a useful diagnostic when creating meshes programmatically.
8412   Currently applicable only to homogeneous simplex or tensor meshes.
8413 
8414   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8415 
8416   Level: developer
8417 
8418 .seealso: `DMCreate()`, `DMSetFromOptions()`
8419 @*/
8420 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8421 {
8422   DMPlexInterpolatedFlag interp;
8423   DMPolytopeType         ct;
8424   PetscInt               vStart, vEnd, cStart, cEnd, c;
8425 
8426   PetscFunctionBegin;
8427   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8428   PetscCall(DMPlexIsInterpolated(dm, &interp));
8429   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8430   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8431   for (c = cStart; c < cEnd; ++c) {
8432     PetscInt *closure = NULL;
8433     PetscInt  coneSize, closureSize, cl, Nv = 0;
8434 
8435     PetscCall(DMPlexGetCellType(dm, c, &ct));
8436     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8437     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8438     if (interp == DMPLEX_INTERPOLATED_FULL) {
8439       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8440       PetscCheck(coneSize == DMPolytopeTypeGetConeSize(ct),PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " of type %s has cone size %" PetscInt_FMT " != %" PetscInt_FMT, c, DMPolytopeTypes[ct], coneSize, DMPolytopeTypeGetConeSize(ct));
8441     }
8442     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8443     for (cl = 0; cl < closureSize*2; cl += 2) {
8444       const PetscInt p = closure[cl];
8445       if ((p >= vStart) && (p < vEnd)) ++Nv;
8446     }
8447     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8448     /* Special Case: Tensor faces with identified vertices */
8449     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8450       PetscInt unsplit;
8451 
8452       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8453       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8454     }
8455     PetscCheck(Nv == DMPolytopeTypeGetNumVertices(ct),PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " of type %s has %" PetscInt_FMT " vertices != %" PetscInt_FMT, c, DMPolytopeTypes[ct], Nv, DMPolytopeTypeGetNumVertices(ct));
8456   }
8457   PetscFunctionReturn(0);
8458 }
8459 
8460 /*@
8461   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8462 
8463   Collective
8464 
8465   Input Parameters:
8466 + dm - The DMPlex object
8467 - cellHeight - Normally 0
8468 
8469   Notes:
8470   This is a useful diagnostic when creating meshes programmatically.
8471   This routine is only relevant for meshes that are fully interpolated across all ranks.
8472   It will error out if a partially interpolated mesh is given on some rank.
8473   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8474 
8475   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8476 
8477   Level: developer
8478 
8479 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8480 @*/
8481 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8482 {
8483   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8484   DMPlexInterpolatedFlag interpEnum;
8485 
8486   PetscFunctionBegin;
8487   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8488   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8489   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8490   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8491     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8492     PetscFunctionReturn(0);
8493   }
8494 
8495   PetscCall(DMGetDimension(dm, &dim));
8496   PetscCall(DMPlexGetDepth(dm, &depth));
8497   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8498   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8499     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8500     for (c = cStart; c < cEnd; ++c) {
8501       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8502       const DMPolytopeType *faceTypes;
8503       DMPolytopeType        ct;
8504       PetscInt              numFaces, coneSize, f;
8505       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8506 
8507       PetscCall(DMPlexGetCellType(dm, c, &ct));
8508       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8509       if (unsplit) continue;
8510       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8511       PetscCall(DMPlexGetCone(dm, c, &cone));
8512       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8513       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8514       for (cl = 0; cl < closureSize*2; cl += 2) {
8515         const PetscInt p = closure[cl];
8516         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8517       }
8518       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8519       PetscCheck(coneSize == numFaces,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " of type %s has %" PetscInt_FMT " faces but should have %" PetscInt_FMT, c, DMPolytopeTypes[ct], coneSize, numFaces);
8520       for (f = 0; f < numFaces; ++f) {
8521         DMPolytopeType fct;
8522         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8523 
8524         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8525         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8526         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8527           const PetscInt p = fclosure[cl];
8528           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8529         }
8530         PetscCheck(fnumCorners == faceSizes[f],PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " of type %s (cone idx %" PetscInt_FMT ") of cell %" PetscInt_FMT " of type %s has %" PetscInt_FMT " vertices but should have %" PetscInt_FMT, cone[f], DMPolytopeTypes[fct], f, c, DMPolytopeTypes[ct], fnumCorners, faceSizes[f]);
8531         for (v = 0; v < fnumCorners; ++v) {
8532           if (fclosure[v] != faces[fOff+v]) {
8533             PetscInt v1;
8534 
8535             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8536             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8537             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8538             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff+v1]));
8539             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8540             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " of type %s (cone idx %" PetscInt_FMT ", ornt %" PetscInt_FMT ") of cell %" PetscInt_FMT " of type %s vertex %" PetscInt_FMT ", %" PetscInt_FMT " != %" PetscInt_FMT, cone[f], DMPolytopeTypes[fct], f, ornt[f], c, DMPolytopeTypes[ct], v, fclosure[v], faces[fOff+v]);
8541           }
8542         }
8543         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8544         fOff += faceSizes[f];
8545       }
8546       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8547       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8548     }
8549   }
8550   PetscFunctionReturn(0);
8551 }
8552 
8553 /*@
8554   DMPlexCheckGeometry - Check the geometry of mesh cells
8555 
8556   Input Parameter:
8557 . dm - The DMPlex object
8558 
8559   Notes:
8560   This is a useful diagnostic when creating meshes programmatically.
8561 
8562   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8563 
8564   Level: developer
8565 
8566 .seealso: `DMCreate()`, `DMSetFromOptions()`
8567 @*/
8568 PetscErrorCode DMPlexCheckGeometry(DM dm)
8569 {
8570   Vec            coordinates;
8571   PetscReal      detJ, J[9], refVol = 1.0;
8572   PetscReal      vol;
8573   PetscBool      periodic;
8574   PetscInt       dim, depth, dE, d, cStart, cEnd, c;
8575 
8576   PetscFunctionBegin;
8577   PetscCall(DMGetDimension(dm, &dim));
8578   PetscCall(DMGetCoordinateDim(dm, &dE));
8579   if (dim != dE) PetscFunctionReturn(0);
8580   PetscCall(DMPlexGetDepth(dm, &depth));
8581   PetscCall(DMGetPeriodicity(dm, &periodic, NULL, NULL, NULL));
8582   for (d = 0; d < dim; ++d) refVol *= 2.0;
8583   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8584   /* Make sure local coordinates are created, because that step is collective */
8585   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8586   for (c = cStart; c < cEnd; ++c) {
8587     DMPolytopeType ct;
8588     PetscInt       unsplit;
8589     PetscBool      ignoreZeroVol = PETSC_FALSE;
8590 
8591     PetscCall(DMPlexGetCellType(dm, c, &ct));
8592     switch (ct) {
8593       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8594       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8595       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8596         ignoreZeroVol = PETSC_TRUE; break;
8597       default: break;
8598     }
8599     switch (ct) {
8600       case DM_POLYTOPE_TRI_PRISM:
8601       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8602       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8603       case DM_POLYTOPE_PYRAMID:
8604         continue;
8605       default: break;
8606     }
8607     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8608     if (unsplit) continue;
8609     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8610     PetscCheck(detJ >= -PETSC_SMALL && (detJ > 0.0 || ignoreZeroVol),PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " of type %s is inverted, |J| = %g", c, DMPolytopeTypes[ct], (double) detJ);
8611     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ*refVol)));
8612     if (depth > 1 && !periodic) {
8613       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8614       PetscCheck(vol >= -PETSC_SMALL && (vol > 0.0 || ignoreZeroVol),PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " of type %s is inverted, vol = %g", c, DMPolytopeTypes[ct], (double) vol);
8615       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double) vol));
8616     }
8617   }
8618   PetscFunctionReturn(0);
8619 }
8620 
8621 /*@
8622   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8623 
8624   Collective
8625 
8626   Input Parameters:
8627 + dm - The DMPlex object
8628 - pointSF - The Point SF, or NULL for Point SF attached to DM
8629 
8630   Notes:
8631   This is mainly intended for debugging/testing purposes.
8632 
8633   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8634 
8635   Level: developer
8636 
8637 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8638 @*/
8639 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF)
8640 {
8641   PetscInt        l, nleaves, nroots, overlap;
8642   const PetscInt *locals;
8643   const PetscSFNode *remotes;
8644   PetscBool       distributed;
8645   MPI_Comm        comm;
8646   PetscMPIInt     rank;
8647 
8648   PetscFunctionBegin;
8649   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8650   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8651   else         pointSF = dm->sf;
8652   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8653   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8654   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8655   {
8656     PetscMPIInt    mpiFlag;
8657 
8658     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF),&mpiFlag));
8659     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)",mpiFlag);
8660   }
8661   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8662   PetscCall(DMPlexIsDistributed(dm, &distributed));
8663   if (!distributed) {
8664     PetscCheck(nroots < 0 || nleaves == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Undistributed DMPlex cannot have non-empty PointSF (has %" PetscInt_FMT " roots, %" PetscInt_FMT " leaves)", nroots, nleaves);
8665     PetscFunctionReturn(0);
8666   }
8667   PetscCheck(nroots >= 0, comm, PETSC_ERR_ARG_WRONGSTATE, "This DMPlex is distributed but its PointSF has no graph set (has %" PetscInt_FMT " roots, %" PetscInt_FMT " leaves)", nroots, nleaves);
8668   PetscCall(DMPlexGetOverlap(dm, &overlap));
8669 
8670   /* Check SF graph is compatible with DMPlex chart */
8671   {
8672     PetscInt pStart, pEnd, maxLeaf;
8673 
8674     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8675     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8676     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd-pStart, nroots);
8677     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8678   }
8679 
8680   /* Check Point SF has no local points referenced */
8681   for (l = 0; l < nleaves; l++) {
8682     PetscAssert(remotes[l].rank != (PetscInt) rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%" PetscInt_FMT ",%" PetscInt_FMT ")", locals ? locals[l] : l, remotes[l].rank, remotes[l].index);
8683   }
8684 
8685   /* Check there are no cells in interface */
8686   if (!overlap) {
8687     PetscInt cellHeight, cStart, cEnd;
8688 
8689     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8690     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8691     for (l = 0; l < nleaves; ++l) {
8692       const PetscInt point = locals ? locals[l] : l;
8693 
8694       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8695     }
8696   }
8697 
8698   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8699   {
8700     const PetscInt *rootdegree;
8701 
8702     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8703     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8704     for (l = 0; l < nleaves; ++l) {
8705       const PetscInt  point = locals ? locals[l] : l;
8706       const PetscInt *cone;
8707       PetscInt        coneSize, c, idx;
8708 
8709       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8710       PetscCall(DMPlexGetCone(dm, point, &cone));
8711       for (c = 0; c < coneSize; ++c) {
8712         if (!rootdegree[cone[c]]) {
8713           if (locals) {
8714             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8715           } else {
8716             idx = (cone[c] < nleaves) ? cone[c] : -1;
8717           }
8718           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8719         }
8720       }
8721     }
8722   }
8723   PetscFunctionReturn(0);
8724 }
8725 
8726 /*@
8727   DMPlexCheck - Perform various checks of Plex sanity
8728 
8729   Input Parameter:
8730 . dm - The DMPlex object
8731 
8732   Notes:
8733   This is a useful diagnostic when creating meshes programmatically.
8734 
8735   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8736 
8737   Currently does not include DMPlexCheckCellShape().
8738 
8739   Level: developer
8740 
8741 .seealso: DMCreate(), DMSetFromOptions()
8742 @*/
8743 PetscErrorCode DMPlexCheck(DM dm)
8744 {
8745   PetscInt cellHeight;
8746 
8747   PetscFunctionBegin;
8748   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8749   PetscCall(DMPlexCheckSymmetry(dm));
8750   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8751   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8752   PetscCall(DMPlexCheckGeometry(dm));
8753   PetscCall(DMPlexCheckPointSF(dm, NULL));
8754   PetscCall(DMPlexCheckInterfaceCones(dm));
8755   PetscFunctionReturn(0);
8756 }
8757 
8758 typedef struct cell_stats
8759 {
8760   PetscReal min, max, sum, squaresum;
8761   PetscInt  count;
8762 } cell_stats_t;
8763 
8764 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8765 {
8766   PetscInt i, N = *len;
8767 
8768   for (i = 0; i < N; i++) {
8769     cell_stats_t *A = (cell_stats_t *) a;
8770     cell_stats_t *B = (cell_stats_t *) b;
8771 
8772     B->min = PetscMin(A->min,B->min);
8773     B->max = PetscMax(A->max,B->max);
8774     B->sum += A->sum;
8775     B->squaresum += A->squaresum;
8776     B->count += A->count;
8777   }
8778 }
8779 
8780 /*@
8781   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8782 
8783   Collective on dm
8784 
8785   Input Parameters:
8786 + dm        - The DMPlex object
8787 . output    - If true, statistics will be displayed on stdout
8788 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8789 
8790   Notes:
8791   This is mainly intended for debugging/testing purposes.
8792 
8793   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8794 
8795   Level: developer
8796 
8797 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8798 @*/
8799 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8800 {
8801   DM             dmCoarse;
8802   cell_stats_t   stats, globalStats;
8803   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8804   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8805   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8806   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8807   PetscMPIInt    rank,size;
8808 
8809   PetscFunctionBegin;
8810   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8811   stats.min   = PETSC_MAX_REAL;
8812   stats.max   = PETSC_MIN_REAL;
8813   stats.sum   = stats.squaresum = 0.;
8814   stats.count = 0;
8815 
8816   PetscCallMPI(MPI_Comm_size(comm, &size));
8817   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8818   PetscCall(DMGetCoordinateDim(dm,&cdim));
8819   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8820   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8821   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8822   for (c = cStart; c < cEnd; c++) {
8823     PetscInt  i;
8824     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8825 
8826     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8827     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8828     for (i = 0; i < PetscSqr(cdim); ++i) {
8829       frobJ    += J[i] * J[i];
8830       frobInvJ += invJ[i] * invJ[i];
8831     }
8832     cond2 = frobJ * frobInvJ;
8833     cond  = PetscSqrtReal(cond2);
8834 
8835     stats.min        = PetscMin(stats.min,cond);
8836     stats.max        = PetscMax(stats.max,cond);
8837     stats.sum       += cond;
8838     stats.squaresum += cond2;
8839     stats.count++;
8840     if (output && cond > limit) {
8841       PetscSection coordSection;
8842       Vec          coordsLocal;
8843       PetscScalar *coords = NULL;
8844       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8845 
8846       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8847       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8848       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8849       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double) cond));
8850       for (i = 0; i < Nv/cdim; ++i) {
8851         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8852         for (d = 0; d < cdim; ++d) {
8853           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8854           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8855         }
8856         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8857       }
8858       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8859       for (cl = 0; cl < clSize*2; cl += 2) {
8860         const PetscInt edge = closure[cl];
8861 
8862         if ((edge >= eStart) && (edge < eEnd)) {
8863           PetscReal len;
8864 
8865           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8866           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double) len));
8867         }
8868       }
8869       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8870       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8871     }
8872   }
8873   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8874 
8875   if (size > 1) {
8876     PetscMPIInt   blockLengths[2] = {4,1};
8877     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8878     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8879     MPI_Op        statReduce;
8880 
8881     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8882     PetscCallMPI(MPI_Type_commit(&statType));
8883     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8884     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8885     PetscCallMPI(MPI_Op_free(&statReduce));
8886     PetscCallMPI(MPI_Type_free(&statType));
8887   } else {
8888     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8889   }
8890   if (rank == 0) {
8891     count = globalStats.count;
8892     min   = globalStats.min;
8893     max   = globalStats.max;
8894     mean  = globalStats.sum / globalStats.count;
8895     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8896   }
8897 
8898   if (output) {
8899     PetscCall(PetscPrintf(comm,"Mesh with %" PetscInt_FMT " cells, shape condition numbers: min = %g, max = %g, mean = %g, stddev = %g\n", count, (double) min, (double) max, (double) mean, (double) stdev));
8900   }
8901   PetscCall(PetscFree2(J,invJ));
8902 
8903   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8904   if (dmCoarse) {
8905     PetscBool isplex;
8906 
8907     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8908     if (isplex) {
8909       PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8910     }
8911   }
8912   PetscFunctionReturn(0);
8913 }
8914 
8915 /*@
8916   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8917   orthogonal quality below given tolerance.
8918 
8919   Collective on dm
8920 
8921   Input Parameters:
8922 + dm   - The DMPlex object
8923 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8924 - atol - [0, 1] Absolute tolerance for tagging cells.
8925 
8926   Output Parameters:
8927 + OrthQual      - Vec containing orthogonal quality per cell
8928 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8929 
8930   Options Database Keys:
8931 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8932 supported.
8933 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8934 
8935   Notes:
8936   Orthogonal quality is given by the following formula:
8937 
8938   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8939 
8940   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
8941   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8942   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8943   calculating the cosine of the angle between these vectors.
8944 
8945   Orthogonal quality ranges from 1 (best) to 0 (worst).
8946 
8947   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8948   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8949 
8950   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8951 
8952   Level: intermediate
8953 
8954 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8955 @*/
8956 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8957 {
8958   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8959   PetscInt                *idx;
8960   PetscScalar             *oqVals;
8961   const PetscScalar       *cellGeomArr, *faceGeomArr;
8962   PetscReal               *ci, *fi, *Ai;
8963   MPI_Comm                comm;
8964   Vec                     cellgeom, facegeom;
8965   DM                      dmFace, dmCell;
8966   IS                      glob;
8967   ISLocalToGlobalMapping  ltog;
8968   PetscViewer             vwr;
8969 
8970   PetscFunctionBegin;
8971   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8972   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
8973   PetscValidPointer(OrthQual, 4);
8974   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
8975   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
8976   PetscCall(DMGetDimension(dm, &nc));
8977   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
8978   {
8979     DMPlexInterpolatedFlag interpFlag;
8980 
8981     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
8982     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
8983       PetscMPIInt rank;
8984 
8985       PetscCallMPI(MPI_Comm_rank(comm, &rank));
8986       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
8987     }
8988   }
8989   if (OrthQualLabel) {
8990     PetscValidPointer(OrthQualLabel, 5);
8991     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
8992     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
8993   } else {*OrthQualLabel = NULL;}
8994   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8995   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8996   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
8997   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
8998   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
8999   PetscCall(VecCreate(comm, OrthQual));
9000   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9001   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
9002   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9003   PetscCall(VecSetUp(*OrthQual));
9004   PetscCall(ISDestroy(&glob));
9005   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9006   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9007   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9008   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9009   PetscCall(VecGetDM(cellgeom, &dmCell));
9010   PetscCall(VecGetDM(facegeom, &dmFace));
9011   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9012   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
9013     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9014     PetscInt           cellarr[2], *adj = NULL;
9015     PetscScalar        *cArr, *fArr;
9016     PetscReal          minvalc = 1.0, minvalf = 1.0;
9017     PetscFVCellGeom    *cg;
9018 
9019     idx[cellIter] = cell-cStart;
9020     cellarr[0] = cell;
9021     /* Make indexing into cellGeom easier */
9022     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9023     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9024     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9025     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9026     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
9027       PetscInt         i;
9028       const PetscInt   neigh = adj[cellneigh];
9029       PetscReal        normci = 0, normfi = 0, normai = 0;
9030       PetscFVCellGeom  *cgneigh;
9031       PetscFVFaceGeom  *fg;
9032 
9033       /* Don't count ourselves in the neighbor list */
9034       if (neigh == cell) continue;
9035       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9036       cellarr[1] = neigh;
9037       {
9038         PetscInt       numcovpts;
9039         const PetscInt *covpts;
9040 
9041         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9042         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9043         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9044       }
9045 
9046       /* Compute c_i, f_i and their norms */
9047       for (i = 0; i < nc; i++) {
9048         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9049         fi[i] = fg->centroid[i] - cg->centroid[i];
9050         Ai[i] = fg->normal[i];
9051         normci += PetscPowReal(ci[i], 2);
9052         normfi += PetscPowReal(fi[i], 2);
9053         normai += PetscPowReal(Ai[i], 2);
9054       }
9055       normci = PetscSqrtReal(normci);
9056       normfi = PetscSqrtReal(normfi);
9057       normai = PetscSqrtReal(normai);
9058 
9059       /* Normalize and compute for each face-cell-normal pair */
9060       for (i = 0; i < nc; i++) {
9061         ci[i] = ci[i]/normci;
9062         fi[i] = fi[i]/normfi;
9063         Ai[i] = Ai[i]/normai;
9064         /* PetscAbs because I don't know if normals are guaranteed to point out */
9065         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9066         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9067       }
9068       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9069         minvalc = PetscRealPart(cArr[cellneighiter]);
9070       }
9071       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9072         minvalf = PetscRealPart(fArr[cellneighiter]);
9073       }
9074     }
9075     PetscCall(PetscFree(adj));
9076     PetscCall(PetscFree2(cArr, fArr));
9077     /* Defer to cell if they're equal */
9078     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9079     if (OrthQualLabel) {
9080       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9081     }
9082   }
9083   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9084   PetscCall(VecAssemblyBegin(*OrthQual));
9085   PetscCall(VecAssemblyEnd(*OrthQual));
9086   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9087   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9088   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9089   if (OrthQualLabel) {
9090     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9091   }
9092   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9093   PetscCall(PetscViewerDestroy(&vwr));
9094   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9095   PetscFunctionReturn(0);
9096 }
9097 
9098 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9099  * interpolator construction */
9100 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9101 {
9102   PetscSection   section, newSection, gsection;
9103   PetscSF        sf;
9104   PetscBool      hasConstraints, ghasConstraints;
9105 
9106   PetscFunctionBegin;
9107   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9108   PetscValidPointer(odm,2);
9109   PetscCall(DMGetLocalSection(dm, &section));
9110   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9111   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9112   if (!ghasConstraints) {
9113     PetscCall(PetscObjectReference((PetscObject)dm));
9114     *odm = dm;
9115     PetscFunctionReturn(0);
9116   }
9117   PetscCall(DMClone(dm, odm));
9118   PetscCall(DMCopyFields(dm, *odm));
9119   PetscCall(DMGetLocalSection(*odm, &newSection));
9120   PetscCall(DMGetPointSF(*odm, &sf));
9121   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9122   PetscCall(DMSetGlobalSection(*odm, gsection));
9123   PetscCall(PetscSectionDestroy(&gsection));
9124   PetscFunctionReturn(0);
9125 }
9126 
9127 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9128 {
9129   DM             dmco, dmfo;
9130   Mat            interpo;
9131   Vec            rscale;
9132   Vec            cglobalo, clocal;
9133   Vec            fglobal, fglobalo, flocal;
9134   PetscBool      regular;
9135 
9136   PetscFunctionBegin;
9137   PetscCall(DMGetFullDM(dmc, &dmco));
9138   PetscCall(DMGetFullDM(dmf, &dmfo));
9139   PetscCall(DMSetCoarseDM(dmfo, dmco));
9140   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9141   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9142   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9143   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9144   PetscCall(DMCreateLocalVector(dmc, &clocal));
9145   PetscCall(VecSet(cglobalo, 0.));
9146   PetscCall(VecSet(clocal, 0.));
9147   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9148   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9149   PetscCall(DMCreateLocalVector(dmf, &flocal));
9150   PetscCall(VecSet(fglobal, 0.));
9151   PetscCall(VecSet(fglobalo, 0.));
9152   PetscCall(VecSet(flocal, 0.));
9153   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9154   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9155   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9156   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9157   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9158   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9159   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9160   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9161   *shift = fglobal;
9162   PetscCall(VecDestroy(&flocal));
9163   PetscCall(VecDestroy(&fglobalo));
9164   PetscCall(VecDestroy(&clocal));
9165   PetscCall(VecDestroy(&cglobalo));
9166   PetscCall(VecDestroy(&rscale));
9167   PetscCall(MatDestroy(&interpo));
9168   PetscCall(DMDestroy(&dmfo));
9169   PetscCall(DMDestroy(&dmco));
9170   PetscFunctionReturn(0);
9171 }
9172 
9173 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9174 {
9175   PetscObject    shifto;
9176   Vec            shift;
9177 
9178   PetscFunctionBegin;
9179   if (!interp) {
9180     Vec rscale;
9181 
9182     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9183     PetscCall(VecDestroy(&rscale));
9184   } else {
9185     PetscCall(PetscObjectReference((PetscObject)interp));
9186   }
9187   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9188   if (!shifto) {
9189     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9190     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9191     shifto = (PetscObject) shift;
9192     PetscCall(VecDestroy(&shift));
9193   }
9194   shift = (Vec) shifto;
9195   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9196   PetscCall(VecAXPY(fineSol, 1.0, shift));
9197   PetscCall(MatDestroy(&interp));
9198   PetscFunctionReturn(0);
9199 }
9200 
9201 /* Pointwise interpolation
9202      Just code FEM for now
9203      u^f = I u^c
9204      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9205      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9206      I_{ij} = psi^f_i phi^c_j
9207 */
9208 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9209 {
9210   PetscSection   gsc, gsf;
9211   PetscInt       m, n;
9212   void          *ctx;
9213   DM             cdm;
9214   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9215 
9216   PetscFunctionBegin;
9217   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9218   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9219   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9220   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9221 
9222   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9223   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9224   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9225   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9226   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9227 
9228   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9229   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9230   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9231   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9232   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9233   if (scaling) {
9234     /* Use naive scaling */
9235     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9236   }
9237   PetscFunctionReturn(0);
9238 }
9239 
9240 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9241 {
9242   VecScatter     ctx;
9243 
9244   PetscFunctionBegin;
9245   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9246   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9247   PetscCall(VecScatterDestroy(&ctx));
9248   PetscFunctionReturn(0);
9249 }
9250 
9251 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9252                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9253                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9254                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9255 {
9256   const PetscInt Nc = uOff[1] - uOff[0];
9257   PetscInt       c;
9258   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9259 }
9260 
9261 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9262 {
9263   DM             dmc;
9264   PetscDS        ds;
9265   Vec            ones, locmass;
9266   IS             cellIS;
9267   PetscFormKey   key;
9268   PetscInt       depth;
9269 
9270   PetscFunctionBegin;
9271   PetscCall(DMClone(dm, &dmc));
9272   PetscCall(DMCopyDisc(dm, dmc));
9273   PetscCall(DMGetDS(dmc, &ds));
9274   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9275   PetscCall(DMCreateGlobalVector(dmc, mass));
9276   PetscCall(DMGetLocalVector(dmc, &ones));
9277   PetscCall(DMGetLocalVector(dmc, &locmass));
9278   PetscCall(DMPlexGetDepth(dmc, &depth));
9279   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9280   PetscCall(VecSet(locmass, 0.0));
9281   PetscCall(VecSet(ones, 1.0));
9282   key.label = NULL;
9283   key.value = 0;
9284   key.field = 0;
9285   key.part  = 0;
9286   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9287   PetscCall(ISDestroy(&cellIS));
9288   PetscCall(VecSet(*mass, 0.0));
9289   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9290   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9291   PetscCall(DMRestoreLocalVector(dmc, &ones));
9292   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9293   PetscCall(DMDestroy(&dmc));
9294   PetscFunctionReturn(0);
9295 }
9296 
9297 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9298 {
9299   PetscSection   gsc, gsf;
9300   PetscInt       m, n;
9301   void          *ctx;
9302   DM             cdm;
9303   PetscBool      regular;
9304 
9305   PetscFunctionBegin;
9306   if (dmFine == dmCoarse) {
9307     DM            dmc;
9308     PetscDS       ds;
9309     PetscWeakForm wf;
9310     Vec           u;
9311     IS            cellIS;
9312     PetscFormKey  key;
9313     PetscInt      depth;
9314 
9315     PetscCall(DMClone(dmFine, &dmc));
9316     PetscCall(DMCopyDisc(dmFine, dmc));
9317     PetscCall(DMGetDS(dmc, &ds));
9318     PetscCall(PetscDSGetWeakForm(ds, &wf));
9319     PetscCall(PetscWeakFormClear(wf));
9320     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9321     PetscCall(DMCreateMatrix(dmc, mass));
9322     PetscCall(DMGetGlobalVector(dmc, &u));
9323     PetscCall(DMPlexGetDepth(dmc, &depth));
9324     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9325     PetscCall(MatZeroEntries(*mass));
9326     key.label = NULL;
9327     key.value = 0;
9328     key.field = 0;
9329     key.part  = 0;
9330     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9331     PetscCall(ISDestroy(&cellIS));
9332     PetscCall(DMRestoreGlobalVector(dmc, &u));
9333     PetscCall(DMDestroy(&dmc));
9334   } else {
9335     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9336     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9337     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9338     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9339 
9340     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9341     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9342     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9343     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9344 
9345     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9346     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9347     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9348     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9349   }
9350   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9351   PetscFunctionReturn(0);
9352 }
9353 
9354 /*@
9355   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9356 
9357   Input Parameter:
9358 . dm - The DMPlex object
9359 
9360   Output Parameter:
9361 . regular - The flag
9362 
9363   Level: intermediate
9364 
9365 .seealso: `DMPlexSetRegularRefinement()`
9366 @*/
9367 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9368 {
9369   PetscFunctionBegin;
9370   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9371   PetscValidBoolPointer(regular, 2);
9372   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9373   PetscFunctionReturn(0);
9374 }
9375 
9376 /*@
9377   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9378 
9379   Input Parameters:
9380 + dm - The DMPlex object
9381 - regular - The flag
9382 
9383   Level: intermediate
9384 
9385 .seealso: `DMPlexGetRegularRefinement()`
9386 @*/
9387 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9388 {
9389   PetscFunctionBegin;
9390   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9391   ((DM_Plex *) dm->data)->regularRefinement = regular;
9392   PetscFunctionReturn(0);
9393 }
9394 
9395 /* anchors */
9396 /*@
9397   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9398   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9399 
9400   not collective
9401 
9402   Input Parameter:
9403 . dm - The DMPlex object
9404 
9405   Output Parameters:
9406 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9407 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9408 
9409   Level: intermediate
9410 
9411 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9412 @*/
9413 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9414 {
9415   DM_Plex *plex = (DM_Plex *)dm->data;
9416 
9417   PetscFunctionBegin;
9418   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9419   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9420   if (anchorSection) *anchorSection = plex->anchorSection;
9421   if (anchorIS) *anchorIS = plex->anchorIS;
9422   PetscFunctionReturn(0);
9423 }
9424 
9425 /*@
9426   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9427   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9428   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9429 
9430   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9431   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9432 
9433   collective on dm
9434 
9435   Input Parameters:
9436 + dm - The DMPlex object
9437 . 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).
9438 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9439 
9440   The reference counts of anchorSection and anchorIS are incremented.
9441 
9442   Level: intermediate
9443 
9444 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9445 @*/
9446 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9447 {
9448   DM_Plex        *plex = (DM_Plex *)dm->data;
9449   PetscMPIInt    result;
9450 
9451   PetscFunctionBegin;
9452   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9453   if (anchorSection) {
9454     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9455     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9456     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9457   }
9458   if (anchorIS) {
9459     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9460     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9461     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9462   }
9463 
9464   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9465   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9466   plex->anchorSection = anchorSection;
9467 
9468   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9469   PetscCall(ISDestroy(&plex->anchorIS));
9470   plex->anchorIS = anchorIS;
9471 
9472   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9473     PetscInt size, a, pStart, pEnd;
9474     const PetscInt *anchors;
9475 
9476     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9477     PetscCall(ISGetLocalSize(anchorIS,&size));
9478     PetscCall(ISGetIndices(anchorIS,&anchors));
9479     for (a = 0; a < size; a++) {
9480       PetscInt p;
9481 
9482       p = anchors[a];
9483       if (p >= pStart && p < pEnd) {
9484         PetscInt dof;
9485 
9486         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9487         if (dof) {
9488 
9489           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9490           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %" PetscInt_FMT " cannot be constrained and an anchor",p);
9491         }
9492       }
9493     }
9494     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9495   }
9496   /* reset the generic constraints */
9497   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9498   PetscFunctionReturn(0);
9499 }
9500 
9501 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9502 {
9503   PetscSection anchorSection;
9504   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9505 
9506   PetscFunctionBegin;
9507   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9508   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9509   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9510   PetscCall(PetscSectionGetNumFields(section,&numFields));
9511   if (numFields) {
9512     PetscInt f;
9513     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9514 
9515     for (f = 0; f < numFields; f++) {
9516       PetscInt numComp;
9517 
9518       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9519       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9520     }
9521   }
9522   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9523   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9524   pStart = PetscMax(pStart,sStart);
9525   pEnd   = PetscMin(pEnd,sEnd);
9526   pEnd   = PetscMax(pStart,pEnd);
9527   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9528   for (p = pStart; p < pEnd; p++) {
9529     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9530     if (dof) {
9531       PetscCall(PetscSectionGetDof(section,p,&dof));
9532       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9533       for (f = 0; f < numFields; f++) {
9534         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9535         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9536       }
9537     }
9538   }
9539   PetscCall(PetscSectionSetUp(*cSec));
9540   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9541   PetscFunctionReturn(0);
9542 }
9543 
9544 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9545 {
9546   PetscSection   aSec;
9547   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9548   const PetscInt *anchors;
9549   PetscInt       numFields, f;
9550   IS             aIS;
9551   MatType        mtype;
9552   PetscBool      iscuda,iskokkos;
9553 
9554   PetscFunctionBegin;
9555   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9556   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9557   PetscCall(PetscSectionGetStorageSize(section, &n));
9558   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9559   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9560   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9561   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9562   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9563   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9564   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9565   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9566   else mtype = MATSEQAIJ;
9567   PetscCall(MatSetType(*cMat,mtype));
9568   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9569   PetscCall(ISGetIndices(aIS,&anchors));
9570   /* cSec will be a subset of aSec and section */
9571   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9572   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9573   PetscCall(PetscMalloc1(m+1,&i));
9574   i[0] = 0;
9575   PetscCall(PetscSectionGetNumFields(section,&numFields));
9576   for (p = pStart; p < pEnd; p++) {
9577     PetscInt rDof, rOff, r;
9578 
9579     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9580     if (!rDof) continue;
9581     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9582     if (numFields) {
9583       for (f = 0; f < numFields; f++) {
9584         annz = 0;
9585         for (r = 0; r < rDof; r++) {
9586           a = anchors[rOff + r];
9587           if (a < sStart || a >= sEnd) continue;
9588           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9589           annz += aDof;
9590         }
9591         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9592         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9593         for (q = 0; q < dof; q++) {
9594           i[off + q + 1] = i[off + q] + annz;
9595         }
9596       }
9597     } else {
9598       annz = 0;
9599       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9600       for (q = 0; q < dof; q++) {
9601         a = anchors[rOff + q];
9602         if (a < sStart || a >= sEnd) continue;
9603         PetscCall(PetscSectionGetDof(section,a,&aDof));
9604         annz += aDof;
9605       }
9606       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9607       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9608       for (q = 0; q < dof; q++) {
9609         i[off + q + 1] = i[off + q] + annz;
9610       }
9611     }
9612   }
9613   nnz = i[m];
9614   PetscCall(PetscMalloc1(nnz,&j));
9615   offset = 0;
9616   for (p = pStart; p < pEnd; p++) {
9617     if (numFields) {
9618       for (f = 0; f < numFields; f++) {
9619         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9620         for (q = 0; q < dof; q++) {
9621           PetscInt rDof, rOff, r;
9622           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9623           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9624           for (r = 0; r < rDof; r++) {
9625             PetscInt s;
9626 
9627             a = anchors[rOff + r];
9628             if (a < sStart || a >= sEnd) continue;
9629             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9630             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9631             for (s = 0; s < aDof; s++) {
9632               j[offset++] = aOff + s;
9633             }
9634           }
9635         }
9636       }
9637     } else {
9638       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9639       for (q = 0; q < dof; q++) {
9640         PetscInt rDof, rOff, r;
9641         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9642         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9643         for (r = 0; r < rDof; r++) {
9644           PetscInt s;
9645 
9646           a = anchors[rOff + r];
9647           if (a < sStart || a >= sEnd) continue;
9648           PetscCall(PetscSectionGetDof(section,a,&aDof));
9649           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9650           for (s = 0; s < aDof; s++) {
9651             j[offset++] = aOff + s;
9652           }
9653         }
9654       }
9655     }
9656   }
9657   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9658   PetscCall(PetscFree(i));
9659   PetscCall(PetscFree(j));
9660   PetscCall(ISRestoreIndices(aIS,&anchors));
9661   PetscFunctionReturn(0);
9662 }
9663 
9664 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9665 {
9666   DM_Plex        *plex = (DM_Plex *)dm->data;
9667   PetscSection   anchorSection, section, cSec;
9668   Mat            cMat;
9669 
9670   PetscFunctionBegin;
9671   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9672   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9673   if (anchorSection) {
9674     PetscInt Nf;
9675 
9676     PetscCall(DMGetLocalSection(dm,&section));
9677     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9678     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9679     PetscCall(DMGetNumFields(dm,&Nf));
9680     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9681     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9682     PetscCall(PetscSectionDestroy(&cSec));
9683     PetscCall(MatDestroy(&cMat));
9684   }
9685   PetscFunctionReturn(0);
9686 }
9687 
9688 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9689 {
9690   IS             subis;
9691   PetscSection   section, subsection;
9692 
9693   PetscFunctionBegin;
9694   PetscCall(DMGetLocalSection(dm, &section));
9695   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9696   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9697   /* Create subdomain */
9698   PetscCall(DMPlexFilter(dm, label, value, subdm));
9699   /* Create submodel */
9700   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9701   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9702   PetscCall(DMSetLocalSection(*subdm, subsection));
9703   PetscCall(PetscSectionDestroy(&subsection));
9704   PetscCall(DMCopyDisc(dm, *subdm));
9705   /* Create map from submodel to global model */
9706   if (is) {
9707     PetscSection    sectionGlobal, subsectionGlobal;
9708     IS              spIS;
9709     const PetscInt *spmap;
9710     PetscInt       *subIndices;
9711     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9712     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9713 
9714     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9715     PetscCall(ISGetIndices(spIS, &spmap));
9716     PetscCall(PetscSectionGetNumFields(section, &Nf));
9717     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9718     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9719     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9720     for (p = pStart; p < pEnd; ++p) {
9721       PetscInt gdof, pSubSize  = 0;
9722 
9723       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9724       if (gdof > 0) {
9725         for (f = 0; f < Nf; ++f) {
9726           PetscInt fdof, fcdof;
9727 
9728           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9729           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9730           pSubSize += fdof-fcdof;
9731         }
9732         subSize += pSubSize;
9733         if (pSubSize) {
9734           if (bs < 0) {
9735             bs = pSubSize;
9736           } else if (bs != pSubSize) {
9737             /* Layout does not admit a pointwise block size */
9738             bs = 1;
9739           }
9740         }
9741       }
9742     }
9743     /* Must have same blocksize on all procs (some might have no points) */
9744     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9745     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9746     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9747     else                            {bs = bsMinMax[0];}
9748     PetscCall(PetscMalloc1(subSize, &subIndices));
9749     for (p = pStart; p < pEnd; ++p) {
9750       PetscInt gdof, goff;
9751 
9752       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9753       if (gdof > 0) {
9754         const PetscInt point = spmap[p];
9755 
9756         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9757         for (f = 0; f < Nf; ++f) {
9758           PetscInt fdof, fcdof, fc, f2, poff = 0;
9759 
9760           /* Can get rid of this loop by storing field information in the global section */
9761           for (f2 = 0; f2 < f; ++f2) {
9762             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9763             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9764             poff += fdof-fcdof;
9765           }
9766           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9767           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9768           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9769             subIndices[subOff] = goff+poff+fc;
9770           }
9771         }
9772       }
9773     }
9774     PetscCall(ISRestoreIndices(spIS, &spmap));
9775     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9776     if (bs > 1) {
9777       /* We need to check that the block size does not come from non-contiguous fields */
9778       PetscInt i, j, set = 1;
9779       for (i = 0; i < subSize; i += bs) {
9780         for (j = 0; j < bs; ++j) {
9781           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9782         }
9783       }
9784       if (set) PetscCall(ISSetBlockSize(*is, bs));
9785     }
9786     /* Attach nullspace */
9787     for (f = 0; f < Nf; ++f) {
9788       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9789       if ((*subdm)->nullspaceConstructors[f]) break;
9790     }
9791     if (f < Nf) {
9792       MatNullSpace nullSpace;
9793       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9794 
9795       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9796       PetscCall(MatNullSpaceDestroy(&nullSpace));
9797     }
9798   }
9799   PetscFunctionReturn(0);
9800 }
9801 
9802 /*@
9803   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9804 
9805   Input Parameter:
9806 - dm - The DM
9807 
9808   Level: developer
9809 
9810   Options Database Keys:
9811 . -dm_plex_monitor_throughput - Activate the monitor
9812 
9813 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9814 @*/
9815 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9816 {
9817 #if defined(PETSC_USE_LOG)
9818   PetscStageLog      stageLog;
9819   PetscLogEvent      event;
9820   PetscLogStage      stage;
9821   PetscEventPerfInfo eventInfo;
9822   PetscReal          cellRate, flopRate;
9823   PetscInt           cStart, cEnd, Nf, N;
9824   const char        *name;
9825 #endif
9826 
9827   PetscFunctionBegin;
9828   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9829 #if defined(PETSC_USE_LOG)
9830   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9831   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9832   PetscCall(DMGetNumFields(dm, &Nf));
9833   PetscCall(PetscLogGetStageLog(&stageLog));
9834   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9835   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9836   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9837   N        = (cEnd - cStart)*Nf*eventInfo.count;
9838   flopRate = eventInfo.flops/eventInfo.time;
9839   cellRate = N/eventInfo.time;
9840   PetscCall(PetscPrintf(PetscObjectComm((PetscObject) dm), "DM (%s) FE Residual Integration: %" PetscInt_FMT " integrals %d reps\n  Cell rate: %.2g/s flop rate: %.2g MF/s\n", name ? name : "unknown", N, eventInfo.count, (double) cellRate, (double) (flopRate/1.e6)));
9841 #else
9842   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9843 #endif
9844   PetscFunctionReturn(0);
9845 }
9846