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