xref: /petsc/src/dm/impls/plex/plex.c (revision c3ecd0023501c4d5c4979c57c0d4b84360631365)
1 #include <petsc/private/dmpleximpl.h>   /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 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;
14 PetscLogEvent DMPLEX_RebalBuildGraph,DMPLEX_RebalRewriteSF,DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm      - The DMPlex object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
28   If the mesh has no cells, this returns PETSC_FALSE.
29 
30   Level: intermediate
31 
32 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
33 @*/
34 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
35 {
36   DMPolytopeType ct;
37   PetscInt       cStart, cEnd;
38 
39   PetscFunctionBegin;
40   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
41   if (cEnd <= cStart) {*simplex = PETSC_FALSE; PetscFunctionReturn(0);}
42   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
43   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
44   PetscFunctionReturn(0);
45 }
46 
47 /*@
48   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
49 
50   Input Parameters:
51 + dm     - The DMPlex object
52 - height - The cell height in the Plex, 0 is the default
53 
54   Output Parameters:
55 + cStart - The first "normal" cell
56 - cEnd   - The upper bound on "normal"" cells
57 
58   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
59 
60   Level: developer
61 
62 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
63 @*/
64 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
65 {
66   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
67   PetscInt       cS, cE, c;
68 
69   PetscFunctionBegin;
70   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
71   for (c = cS; c < cE; ++c) {
72     DMPolytopeType cct;
73 
74     PetscCall(DMPlexGetCellType(dm, c, &cct));
75     if ((PetscInt) cct < 0) break;
76     switch (cct) {
77       case DM_POLYTOPE_POINT:
78       case DM_POLYTOPE_SEGMENT:
79       case DM_POLYTOPE_TRIANGLE:
80       case DM_POLYTOPE_QUADRILATERAL:
81       case DM_POLYTOPE_TETRAHEDRON:
82       case DM_POLYTOPE_HEXAHEDRON:
83         ct = cct;
84         break;
85       default: break;
86     }
87     if (ct != DM_POLYTOPE_UNKNOWN) break;
88   }
89   if (ct != DM_POLYTOPE_UNKNOWN) {
90     DMLabel ctLabel;
91 
92     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
93     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
94     // Reset label for fast lookup
95     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
96   }
97   if (cStart) *cStart = cS;
98   if (cEnd)   *cEnd   = cE;
99   PetscFunctionReturn(0);
100 }
101 
102 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
103 {
104   PetscInt       cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
105   PetscInt       vcdof[2] = {0,0}, globalvcdof[2];
106 
107   PetscFunctionBegin;
108   *ft  = PETSC_VTK_INVALID;
109   PetscCall(DMGetCoordinateDim(dm, &cdim));
110   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
111   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
112   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
113   if (field >= 0) {
114     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
115     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
116   } else {
117     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
118     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
119   }
120   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
121   if (globalvcdof[0]) {
122     *sStart = vStart;
123     *sEnd   = vEnd;
124     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
125     else                        *ft = PETSC_VTK_POINT_FIELD;
126   } else if (globalvcdof[1]) {
127     *sStart = cStart;
128     *sEnd   = cEnd;
129     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
130     else                        *ft = PETSC_VTK_CELL_FIELD;
131   } else {
132     if (field >= 0) {
133       const char *fieldname;
134 
135       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
136       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
137     } else {
138       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section\n"));
139     }
140   }
141   PetscFunctionReturn(0);
142 }
143 
144 /*@
145   DMPlexVecView1D - Plot many 1D solutions on the same line graph
146 
147   Collective on dm
148 
149   Input Parameters:
150 + dm - The DMPlex
151 . n  - The number of vectors
152 . u  - The array of local vectors
153 - viewer - The Draw viewer
154 
155   Level: advanced
156 
157 .seealso: `VecViewFromOptions()`, `VecView()`
158 @*/
159 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
160 {
161   PetscDS            ds;
162   PetscDraw          draw = NULL;
163   PetscDrawLG        lg;
164   Vec                coordinates;
165   const PetscScalar *coords, **sol;
166   PetscReal         *vals;
167   PetscInt          *Nc;
168   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
169   char             **names;
170 
171   PetscFunctionBegin;
172   PetscCall(DMGetDS(dm, &ds));
173   PetscCall(PetscDSGetNumFields(ds, &Nf));
174   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
175   PetscCall(PetscDSGetComponents(ds, &Nc));
176 
177   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
178   if (!draw) PetscFunctionReturn(0);
179   PetscCall(PetscDrawLGCreate(draw, n*Nl, &lg));
180 
181   PetscCall(PetscMalloc3(n, &sol, n*Nl, &names, n*Nl, &vals));
182   for (i = 0, l = 0; i < n; ++i) {
183     const char *vname;
184 
185     PetscCall(PetscObjectGetName((PetscObject) u[i], &vname));
186     for (f = 0; f < Nf; ++f) {
187       PetscObject disc;
188       const char *fname;
189       char        tmpname[PETSC_MAX_PATH_LEN];
190 
191       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
192       /* TODO Create names for components */
193       for (c = 0; c < Nc[f]; ++c, ++l) {
194         PetscCall(PetscObjectGetName(disc, &fname));
195         PetscCall(PetscStrcpy(tmpname, vname));
196         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
197         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
198         PetscCall(PetscStrallocpy(tmpname, &names[l]));
199       }
200     }
201   }
202   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *) names));
203   /* Just add P_1 support for now */
204   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
205   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
206   PetscCall(VecGetArrayRead(coordinates, &coords));
207   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
208   for (v = vStart; v < vEnd; ++v) {
209     PetscScalar *x, *svals;
210 
211     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
212     for (i = 0; i < n; ++i) {
213       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
214       for (l = 0; l < Nl; ++l) vals[i*Nl + l] = PetscRealPart(svals[l]);
215     }
216     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
217   }
218   PetscCall(VecRestoreArrayRead(coordinates, &coords));
219   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
220   for (l = 0; l < n*Nl; ++l) PetscCall(PetscFree(names[l]));
221   PetscCall(PetscFree3(sol, names, vals));
222 
223   PetscCall(PetscDrawLGDraw(lg));
224   PetscCall(PetscDrawLGDestroy(&lg));
225   PetscFunctionReturn(0);
226 }
227 
228 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
229 {
230   DM             dm;
231 
232   PetscFunctionBegin;
233   PetscCall(VecGetDM(u, &dm));
234   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
235   PetscFunctionReturn(0);
236 }
237 
238 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
239 {
240   DM                 dm;
241   PetscSection       s;
242   PetscDraw          draw, popup;
243   DM                 cdm;
244   PetscSection       coordSection;
245   Vec                coordinates;
246   const PetscScalar *coords, *array;
247   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
248   PetscReal          vbound[2], time;
249   PetscBool          flg;
250   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
251   const char        *name;
252   char               title[PETSC_MAX_PATH_LEN];
253 
254   PetscFunctionBegin;
255   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
256   PetscCall(VecGetDM(v, &dm));
257   PetscCall(DMGetCoordinateDim(dm, &dim));
258   PetscCall(DMGetLocalSection(dm, &s));
259   PetscCall(PetscSectionGetNumFields(s, &Nf));
260   PetscCall(DMGetCoarsenLevel(dm, &level));
261   PetscCall(DMGetCoordinateDM(dm, &cdm));
262   PetscCall(DMGetLocalSection(cdm, &coordSection));
263   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
264   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
265   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
266 
267   PetscCall(PetscObjectGetName((PetscObject) v, &name));
268   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
269 
270   PetscCall(VecGetLocalSize(coordinates, &N));
271   PetscCall(VecGetArrayRead(coordinates, &coords));
272   for (c = 0; c < N; c += dim) {
273     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
274     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
275   }
276   PetscCall(VecRestoreArrayRead(coordinates, &coords));
277   PetscCall(PetscDrawClear(draw));
278 
279   /* Could implement something like DMDASelectFields() */
280   for (f = 0; f < Nf; ++f) {
281     DM   fdm = dm;
282     Vec  fv  = v;
283     IS   fis;
284     char prefix[PETSC_MAX_PATH_LEN];
285     const char *fname;
286 
287     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
288     PetscCall(PetscSectionGetFieldName(s, f, &fname));
289 
290     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix,sizeof(prefix)));
291     else               {prefix[0] = '\0';}
292     if (Nf > 1) {
293       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
294       PetscCall(VecGetSubVector(v, fis, &fv));
295       PetscCall(PetscStrlcat(prefix, fname,sizeof(prefix)));
296       PetscCall(PetscStrlcat(prefix, "_",sizeof(prefix)));
297     }
298     for (comp = 0; comp < Nc; ++comp, ++w) {
299       PetscInt nmax = 2;
300 
301       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
302       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
303       else        PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
304       PetscCall(PetscDrawSetTitle(draw, title));
305 
306       /* TODO Get max and min only for this component */
307       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
308       if (!flg) {
309         PetscCall(VecMin(fv, NULL, &vbound[0]));
310         PetscCall(VecMax(fv, NULL, &vbound[1]));
311         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
312       }
313       PetscCall(PetscDrawGetPopup(draw, &popup));
314       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
315       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
316 
317       PetscCall(VecGetArrayRead(fv, &array));
318       for (c = cStart; c < cEnd; ++c) {
319         PetscScalar *coords = NULL, *a = NULL;
320         PetscInt     numCoords, color[4] = {-1,-1,-1,-1};
321 
322         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
323         if (a) {
324           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
325           color[1] = color[2] = color[3] = color[0];
326         } else {
327           PetscScalar *vals = NULL;
328           PetscInt     numVals, va;
329 
330           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
331           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);
332           switch (numVals/Nc) {
333           case 3: /* P1 Triangle */
334           case 4: /* P1 Quadrangle */
335             for (va = 0; va < numVals/Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp]), vbound[0], vbound[1]);
336             break;
337           case 6: /* P2 Triangle */
338           case 8: /* P2 Quadrangle */
339             for (va = 0; va < numVals/(Nc*2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp + numVals/(Nc*2)]), vbound[0], vbound[1]);
340             break;
341           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals/Nc);
342           }
343           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
344         }
345         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
346         switch (numCoords) {
347         case 6:
348         case 12: /* Localized triangle */
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           break;
351         case 8:
352         case 16: /* Localized quadrilateral */
353           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]));
354           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]));
355           break;
356         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
357         }
358         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
359       }
360       PetscCall(VecRestoreArrayRead(fv, &array));
361       PetscCall(PetscDrawFlush(draw));
362       PetscCall(PetscDrawPause(draw));
363       PetscCall(PetscDrawSave(draw));
364     }
365     if (Nf > 1) {
366       PetscCall(VecRestoreSubVector(v, fis, &fv));
367       PetscCall(ISDestroy(&fis));
368       PetscCall(DMDestroy(&fdm));
369     }
370   }
371   PetscFunctionReturn(0);
372 }
373 
374 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
375 {
376   DM        dm;
377   PetscDraw draw;
378   PetscInt  dim;
379   PetscBool isnull;
380 
381   PetscFunctionBegin;
382   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
383   PetscCall(PetscDrawIsNull(draw, &isnull));
384   if (isnull) PetscFunctionReturn(0);
385 
386   PetscCall(VecGetDM(v, &dm));
387   PetscCall(DMGetCoordinateDim(dm, &dim));
388   switch (dim) {
389   case 1: PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));break;
390   case 2: PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));break;
391   default: SETERRQ(PetscObjectComm((PetscObject) v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
392   }
393   PetscFunctionReturn(0);
394 }
395 
396 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
397 {
398   DM                      dm;
399   Vec                     locv;
400   const char              *name;
401   PetscSection            section;
402   PetscInt                pStart, pEnd;
403   PetscInt                numFields;
404   PetscViewerVTKFieldType ft;
405 
406   PetscFunctionBegin;
407   PetscCall(VecGetDM(v, &dm));
408   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
409   PetscCall(PetscObjectGetName((PetscObject) v, &name));
410   PetscCall(PetscObjectSetName((PetscObject) locv, name));
411   PetscCall(VecCopy(v, locv));
412   PetscCall(DMGetLocalSection(dm, &section));
413   PetscCall(PetscSectionGetNumFields(section, &numFields));
414   if (!numFields) {
415     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
416     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE,(PetscObject) locv));
417   } else {
418     PetscInt f;
419 
420     for (f = 0; f < numFields; f++) {
421       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
422       if (ft == PETSC_VTK_INVALID) continue;
423       PetscCall(PetscObjectReference((PetscObject)locv));
424       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE,(PetscObject) locv));
425     }
426     PetscCall(VecDestroy(&locv));
427   }
428   PetscFunctionReturn(0);
429 }
430 
431 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
432 {
433   DM             dm;
434   PetscBool      isvtk, ishdf5, isdraw, isglvis;
435 
436   PetscFunctionBegin;
437   PetscCall(VecGetDM(v, &dm));
438   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
439   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk));
440   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5));
441   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw));
442   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis));
443   if (isvtk || ishdf5 || isdraw || isglvis) {
444     PetscInt    i,numFields;
445     PetscObject fe;
446     PetscBool   fem = PETSC_FALSE;
447     Vec         locv = v;
448     const char  *name;
449     PetscInt    step;
450     PetscReal   time;
451 
452     PetscCall(DMGetNumFields(dm, &numFields));
453     for (i=0; i<numFields; i++) {
454       PetscCall(DMGetField(dm, i, NULL, &fe));
455       if (fe->classid == PETSCFE_CLASSID) { fem = PETSC_TRUE; break; }
456     }
457     if (fem) {
458       PetscObject isZero;
459 
460       PetscCall(DMGetLocalVector(dm, &locv));
461       PetscCall(PetscObjectGetName((PetscObject) v, &name));
462       PetscCall(PetscObjectSetName((PetscObject) locv, name));
463       PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
464       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
465       PetscCall(VecCopy(v, locv));
466       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
467       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
468     }
469     if (isvtk) {
470       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
471     } else if (ishdf5) {
472 #if defined(PETSC_HAVE_HDF5)
473       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
474 #else
475       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
476 #endif
477     } else if (isdraw) {
478       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
479     } else if (isglvis) {
480       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
481       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
482       PetscCall(VecView_GLVis(locv, viewer));
483     }
484     if (fem) {
485       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
486       PetscCall(DMRestoreLocalVector(dm, &locv));
487     }
488   } else {
489     PetscBool isseq;
490 
491     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
492     if (isseq) PetscCall(VecView_Seq(v, viewer));
493     else       PetscCall(VecView_MPI(v, viewer));
494   }
495   PetscFunctionReturn(0);
496 }
497 
498 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
499 {
500   DM        dm;
501   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii;
502 
503   PetscFunctionBegin;
504   PetscCall(VecGetDM(v, &dm));
505   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
506   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
507   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
508   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
509   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
510   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
511   if (isvtk || isdraw || isglvis) {
512     Vec         locv;
513     PetscObject isZero;
514     const char *name;
515 
516     PetscCall(DMGetLocalVector(dm, &locv));
517     PetscCall(PetscObjectGetName((PetscObject) v, &name));
518     PetscCall(PetscObjectSetName((PetscObject) locv, name));
519     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
520     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
521     PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
522     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
523     PetscCall(VecView_Plex_Local(locv, viewer));
524     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
525     PetscCall(DMRestoreLocalVector(dm, &locv));
526   } else if (ishdf5) {
527 #if defined(PETSC_HAVE_HDF5)
528     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
529 #else
530     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
531 #endif
532   } else if (isexodusii) {
533 #if defined(PETSC_HAVE_EXODUSII)
534     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
535 #else
536     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
537 #endif
538   } else {
539     PetscBool isseq;
540 
541     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
542     if (isseq) PetscCall(VecView_Seq(v, viewer));
543     else       PetscCall(VecView_MPI(v, viewer));
544   }
545   PetscFunctionReturn(0);
546 }
547 
548 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
549 {
550   DM                dm;
551   MPI_Comm          comm;
552   PetscViewerFormat format;
553   Vec               v;
554   PetscBool         isvtk, ishdf5;
555 
556   PetscFunctionBegin;
557   PetscCall(VecGetDM(originalv, &dm));
558   PetscCall(PetscObjectGetComm((PetscObject) originalv, &comm));
559   PetscCheck(dm,comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
560   PetscCall(PetscViewerGetFormat(viewer, &format));
561   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
562   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,  &isvtk));
563   if (format == PETSC_VIEWER_NATIVE) {
564     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
565     /* this need a better fix */
566     if (dm->useNatural) {
567       if (dm->sfNatural) {
568         const char *vecname;
569         PetscInt    n, nroots;
570 
571         PetscCall(VecGetLocalSize(originalv, &n));
572         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
573         if (n == nroots) {
574           PetscCall(DMGetGlobalVector(dm, &v));
575           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
576           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
577           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
578           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
579         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
580       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
581     } else v = originalv;
582   } else v = originalv;
583 
584   if (ishdf5) {
585 #if defined(PETSC_HAVE_HDF5)
586     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
587 #else
588     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
589 #endif
590   } else if (isvtk) {
591     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
592   } else {
593     PetscBool isseq;
594 
595     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
596     if (isseq) PetscCall(VecView_Seq(v, viewer));
597     else       PetscCall(VecView_MPI(v, viewer));
598   }
599   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
600   PetscFunctionReturn(0);
601 }
602 
603 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
604 {
605   DM             dm;
606   PetscBool      ishdf5;
607 
608   PetscFunctionBegin;
609   PetscCall(VecGetDM(v, &dm));
610   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
611   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
612   if (ishdf5) {
613     DM          dmBC;
614     Vec         gv;
615     const char *name;
616 
617     PetscCall(DMGetOutputDM(dm, &dmBC));
618     PetscCall(DMGetGlobalVector(dmBC, &gv));
619     PetscCall(PetscObjectGetName((PetscObject) v, &name));
620     PetscCall(PetscObjectSetName((PetscObject) gv, name));
621     PetscCall(VecLoad_Default(gv, viewer));
622     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
623     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
624     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
625   } else PetscCall(VecLoad_Default(v, viewer));
626   PetscFunctionReturn(0);
627 }
628 
629 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
630 {
631   DM             dm;
632   PetscBool      ishdf5,isexodusii;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
638   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
639   if (ishdf5) {
640 #if defined(PETSC_HAVE_HDF5)
641     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
642 #else
643     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
644 #endif
645   } else if (isexodusii) {
646 #if defined(PETSC_HAVE_EXODUSII)
647     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
648 #else
649     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
650 #endif
651   } else PetscCall(VecLoad_Default(v, viewer));
652   PetscFunctionReturn(0);
653 }
654 
655 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
656 {
657   DM                dm;
658   PetscViewerFormat format;
659   PetscBool         ishdf5;
660 
661   PetscFunctionBegin;
662   PetscCall(VecGetDM(originalv, &dm));
663   PetscCheck(dm,PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
664   PetscCall(PetscViewerGetFormat(viewer, &format));
665   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
666   if (format == PETSC_VIEWER_NATIVE) {
667     if (dm->useNatural) {
668       if (dm->sfNatural) {
669         if (ishdf5) {
670 #if defined(PETSC_HAVE_HDF5)
671           Vec         v;
672           const char *vecname;
673 
674           PetscCall(DMGetGlobalVector(dm, &v));
675           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
676           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
677           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
678           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
679           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
680           PetscCall(DMRestoreGlobalVector(dm, &v));
681 #else
682           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
683 #endif
684         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
685       }
686     } else PetscCall(VecLoad_Default(originalv, viewer));
687   }
688   PetscFunctionReturn(0);
689 }
690 
691 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
692 {
693   PetscSection       coordSection;
694   Vec                coordinates;
695   DMLabel            depthLabel, celltypeLabel;
696   const char        *name[4];
697   const PetscScalar *a;
698   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
699 
700   PetscFunctionBegin;
701   PetscCall(DMGetDimension(dm, &dim));
702   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
703   PetscCall(DMGetCoordinateSection(dm, &coordSection));
704   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
705   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
706   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
707   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
708   PetscCall(VecGetArrayRead(coordinates, &a));
709   name[0]     = "vertex";
710   name[1]     = "edge";
711   name[dim-1] = "face";
712   name[dim]   = "cell";
713   for (c = cStart; c < cEnd; ++c) {
714     PetscInt *closure = NULL;
715     PetscInt  closureSize, cl, ct;
716 
717     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
718     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
719     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
720     PetscCall(PetscViewerASCIIPushTab(viewer));
721     for (cl = 0; cl < closureSize*2; cl += 2) {
722       PetscInt point = closure[cl], depth, dof, off, d, p;
723 
724       if ((point < pStart) || (point >= pEnd)) continue;
725       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
726       if (!dof) continue;
727       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
728       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
729       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
730       for (p = 0; p < dof/dim; ++p) {
731         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
732         for (d = 0; d < dim; ++d) {
733           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
734           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d])));
735         }
736         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
737       }
738       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
739     }
740     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
741     PetscCall(PetscViewerASCIIPopTab(viewer));
742   }
743   PetscCall(VecRestoreArrayRead(coordinates, &a));
744   PetscFunctionReturn(0);
745 }
746 
747 typedef enum {CS_CARTESIAN, CS_POLAR, CS_CYLINDRICAL, CS_SPHERICAL} CoordSystem;
748 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
749 
750 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
751 {
752   PetscInt       i;
753 
754   PetscFunctionBegin;
755   if (dim > 3) {
756     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) PetscRealPart(x[i])));
757   } else {
758     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
759 
760     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
761     switch (cs) {
762       case CS_CARTESIAN: for (i = 0; i < dim; ++i) trcoords[i] = coords[i];break;
763       case CS_POLAR:
764         PetscCheck(dim == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
765         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
766         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
767         break;
768       case CS_CYLINDRICAL:
769         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
770         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
771         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
772         trcoords[2] = coords[2];
773         break;
774       case CS_SPHERICAL:
775         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
776         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
777         trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
778         trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
779         break;
780     }
781     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) trcoords[i]));
782   }
783   PetscFunctionReturn(0);
784 }
785 
786 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
787 {
788   DM_Plex          *mesh = (DM_Plex*) dm->data;
789   DM                cdm, cdmCell;
790   PetscSection      coordSection, coordSectionCell;
791   Vec               coordinates, coordinatesCell;
792   PetscViewerFormat format;
793 
794   PetscFunctionBegin;
795   PetscCall(DMGetCoordinateDM(dm, &cdm));
796   PetscCall(DMGetCoordinateSection(dm, &coordSection));
797   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
798   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
799   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
800   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
801   PetscCall(PetscViewerGetFormat(viewer, &format));
802   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
803     const char *name;
804     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
805     PetscInt    pStart, pEnd, p, numLabels, l;
806     PetscMPIInt rank, size;
807 
808     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
809     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
810     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
811     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
812     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
813     PetscCall(DMGetDimension(dm, &dim));
814     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
815     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
816     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
817     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
818     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
819     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
820     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
821     for (p = pStart; p < pEnd; ++p) {
822       PetscInt dof, off, s;
823 
824       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
825       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
826       for (s = off; s < off+dof; ++s) {
827         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
828       }
829     }
830     PetscCall(PetscViewerFlush(viewer));
831     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
832     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
833     for (p = pStart; p < pEnd; ++p) {
834       PetscInt dof, off, c;
835 
836       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
837       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
838       for (c = off; c < off+dof; ++c) {
839         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
840       }
841     }
842     PetscCall(PetscViewerFlush(viewer));
843     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
844     if (coordSection && coordinates) {
845       CoordSystem        cs = CS_CARTESIAN;
846       const PetscScalar *array, *arrayCell = NULL;
847       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
848       PetscMPIInt        rank;
849       const char        *name;
850 
851       PetscCall(PetscOptionsGetEnum(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *) &cs, NULL));
852       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
853       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
854       PetscCheck(Nf == 1,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
855       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
856       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
857       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
858       pStart =  PetscMin(pvStart, pcStart);
859       pEnd   =  PetscMax(pvEnd,   pcEnd);
860       PetscCall(PetscObjectGetName((PetscObject) coordinates, &name));
861       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
862       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
863       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
864 
865       PetscCall(VecGetArrayRead(coordinates, &array));
866       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
867       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
868       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
869       for (p = pStart; p < pEnd; ++p) {
870         PetscInt dof, off;
871 
872         if (p >= pvStart && p < pvEnd) {
873           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
874           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
875           if (dof) {
876             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
877             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
878             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
879           }
880         }
881         if (cdmCell && p >= pcStart && p < pcEnd) {
882           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
883           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
884           if (dof) {
885             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
886             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
887             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
888           }
889         }
890       }
891       PetscCall(PetscViewerFlush(viewer));
892       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
893       PetscCall(VecRestoreArrayRead(coordinates, &array));
894       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
895     }
896     PetscCall(DMGetNumLabels(dm, &numLabels));
897     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
898     for (l = 0; l < numLabels; ++l) {
899       DMLabel     label;
900       PetscBool   isdepth;
901       const char *name;
902 
903       PetscCall(DMGetLabelName(dm, l, &name));
904       PetscCall(PetscStrcmp(name, "depth", &isdepth));
905       if (isdepth) continue;
906       PetscCall(DMGetLabel(dm, name, &label));
907       PetscCall(DMLabelView(label, viewer));
908     }
909     if (size > 1) {
910       PetscSF sf;
911 
912       PetscCall(DMGetPointSF(dm, &sf));
913       PetscCall(PetscSFView(sf, viewer));
914     }
915     PetscCall(PetscViewerFlush(viewer));
916   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
917     const char  *name, *color;
918     const char  *defcolors[3]  = {"gray", "orange", "green"};
919     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
920     char         lname[PETSC_MAX_PATH_LEN];
921     PetscReal    scale         = 2.0;
922     PetscReal    tikzscale     = 1.0;
923     PetscBool    useNumbers    = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
924     double       tcoords[3];
925     PetscScalar *coords;
926     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
927     PetscMPIInt  rank, size;
928     char         **names, **colors, **lcolors;
929     PetscBool    flg, lflg;
930     PetscBT      wp = NULL;
931     PetscInt     pEnd, pStart;
932 
933     PetscCall(DMGetDimension(dm, &dim));
934     PetscCall(DMPlexGetDepth(dm, &depth));
935     PetscCall(DMGetNumLabels(dm, &numLabels));
936     numLabels  = PetscMax(numLabels, 10);
937     numColors  = 10;
938     numLColors = 10;
939     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
940     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
941     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
942     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
943     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
944     for (d = 0; d < 4; ++d) drawColors[d]  = PETSC_TRUE;
945     n = 4;
946     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
947     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
948     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
949     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
950     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
951     if (!useLabels) numLabels = 0;
952     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
953     if (!useColors) {
954       numColors = 3;
955       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
956     }
957     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
958     if (!useColors) {
959       numLColors = 4;
960       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
961     }
962     PetscCall(PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
963     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
964     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
965     PetscCheck(!flg || !plotEdges || depth >= dim,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
966     if (depth < dim) plotEdges = PETSC_FALSE;
967     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
968 
969     /* filter points with labelvalue != labeldefaultvalue */
970     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
971     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
972     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
973     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
974     if (lflg) {
975       DMLabel lbl;
976 
977       PetscCall(DMGetLabel(dm, lname, &lbl));
978       if (lbl) {
979         PetscInt val, defval;
980 
981         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
982         PetscCall(PetscBTCreate(pEnd-pStart, &wp));
983         for (c = pStart;  c < pEnd; c++) {
984           PetscInt *closure = NULL;
985           PetscInt  closureSize;
986 
987           PetscCall(DMLabelGetValue(lbl, c, &val));
988           if (val == defval) continue;
989 
990           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
991           for (p = 0; p < closureSize*2; p += 2) {
992             PetscCall(PetscBTSet(wp, closure[p] - pStart));
993           }
994           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
995         }
996       }
997     }
998 
999     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1000     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1001     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1002     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1003 \\documentclass[tikz]{standalone}\n\n\
1004 \\usepackage{pgflibraryshapes}\n\
1005 \\usetikzlibrary{backgrounds}\n\
1006 \\usetikzlibrary{arrows}\n\
1007 \\begin{document}\n"));
1008     if (size > 1) {
1009       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1010       for (p = 0; p < size; ++p) {
1011         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size-1) ? ", and " :  ", "));
1012         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p%numColors], p));
1013       }
1014       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1015     }
1016     if (drawHasse) {
1017       PetscInt maxStratum = PetscMax(vEnd-vStart, PetscMax(eEnd-eStart, cEnd-cStart));
1018 
1019       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1020       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd-1));
1021       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd-vStart));
1022       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum-(vEnd-vStart))/2.));
1023       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1024       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd-1));
1025       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum-(eEnd-eStart))/2.));
1026       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd-eStart));
1027       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1028       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd-1));
1029       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd-cStart));
1030       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum-(cEnd-cStart))/2.));
1031     }
1032     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale));
1033 
1034     /* Plot vertices */
1035     PetscCall(VecGetArray(coordinates, &coords));
1036     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1037     for (v = vStart; v < vEnd; ++v) {
1038       PetscInt  off, dof, d;
1039       PetscBool isLabeled = PETSC_FALSE;
1040 
1041       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
1042       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1043       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1044       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1045       PetscCheck(dof <= 3,PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3",v,dof);
1046       for (d = 0; d < dof; ++d) {
1047         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1048         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1049       }
1050       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1051       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1052       for (d = 0; d < dof; ++d) {
1053         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1054         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]));
1055       }
1056       if (drawHasse) color = colors[0%numColors];
1057       else           color = colors[rank%numColors];
1058       for (l = 0; l < numLabels; ++l) {
1059         PetscInt val;
1060         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1061         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1062       }
1063       if (drawNumbers[0]) {
1064         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1065       } else if (drawColors[0]) {
1066         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1067       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1068     }
1069     PetscCall(VecRestoreArray(coordinates, &coords));
1070     PetscCall(PetscViewerFlush(viewer));
1071     /* Plot edges */
1072     if (plotEdges) {
1073       PetscCall(VecGetArray(coordinates, &coords));
1074       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1075       for (e = eStart; e < eEnd; ++e) {
1076         const PetscInt *cone;
1077         PetscInt        coneSize, offA, offB, dof, d;
1078 
1079         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1080         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1081         PetscCheck(coneSize == 2,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1082         PetscCall(DMPlexGetCone(dm, e, &cone));
1083         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1084         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1085         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1086         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1087         for (d = 0; d < dof; ++d) {
1088           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
1089           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1090         }
1091         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1092         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1093         for (d = 0; d < dof; ++d) {
1094           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1095           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1096         }
1097         if (drawHasse) color = colors[1%numColors];
1098         else           color = colors[rank%numColors];
1099         for (l = 0; l < numLabels; ++l) {
1100           PetscInt val;
1101           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1102           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1103         }
1104         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1105       }
1106       PetscCall(VecRestoreArray(coordinates, &coords));
1107       PetscCall(PetscViewerFlush(viewer));
1108       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1109     }
1110     /* Plot cells */
1111     if (dim == 3 || !drawNumbers[1]) {
1112       for (e = eStart; e < eEnd; ++e) {
1113         const PetscInt *cone;
1114 
1115         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1116         color = colors[rank%numColors];
1117         for (l = 0; l < numLabels; ++l) {
1118           PetscInt val;
1119           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1120           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1121         }
1122         PetscCall(DMPlexGetCone(dm, e, &cone));
1123         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1124       }
1125     } else {
1126        DMPolytopeType ct;
1127 
1128       /* Drawing a 2D polygon */
1129       for (c = cStart; c < cEnd; ++c) {
1130         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1131         PetscCall(DMPlexGetCellType(dm, c, &ct));
1132         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR ||
1133             ct == DM_POLYTOPE_TRI_PRISM_TENSOR ||
1134             ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1135           const PetscInt *cone;
1136           PetscInt        coneSize, e;
1137 
1138           PetscCall(DMPlexGetCone(dm, c, &cone));
1139           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1140           for (e = 0; e < coneSize; ++e) {
1141             const PetscInt *econe;
1142 
1143             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1144             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));
1145           }
1146         } else {
1147           PetscInt *closure = NULL;
1148           PetscInt  closureSize, Nv = 0, v;
1149 
1150           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1151           for (p = 0; p < closureSize*2; p += 2) {
1152             const PetscInt point = closure[p];
1153 
1154             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1155           }
1156           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]));
1157           for (v = 0; v <= Nv; ++v) {
1158             const PetscInt vertex = closure[v%Nv];
1159 
1160             if (v > 0) {
1161               if (plotEdges) {
1162                 const PetscInt *edge;
1163                 PetscInt        endpoints[2], ne;
1164 
1165                 endpoints[0] = closure[v-1]; endpoints[1] = vertex;
1166                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1167                 PetscCheck(ne == 1,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1168                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1169                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1170               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1171             }
1172             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1173           }
1174           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1175           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1176         }
1177       }
1178     }
1179     for (c = cStart; c < cEnd; ++c) {
1180       double             ccoords[3] = {0.0, 0.0, 0.0};
1181       PetscBool          isLabeled  = PETSC_FALSE;
1182       PetscScalar       *cellCoords = NULL;
1183       const PetscScalar *array;
1184       PetscInt           numCoords, cdim, d;
1185       PetscBool          isDG;
1186 
1187       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
1188       PetscCall(DMGetCoordinateDim(dm, &cdim));
1189       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1190       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1191       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1192       for (p = 0; p < numCoords/cdim; ++p) {
1193         for (d = 0; d < cdim; ++d) {
1194           tcoords[d] = (double) (scale*PetscRealPart(cellCoords[p*cdim+d]));
1195           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1196         }
1197         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1198         if (cdim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1199         for (d = 0; d < dim; ++d) {ccoords[d] += tcoords[d];}
1200       }
1201       for (d = 0; d < cdim; ++d) {ccoords[d] /= (numCoords/cdim);}
1202       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1203       for (d = 0; d < cdim; ++d) {
1204         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1205         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]));
1206       }
1207       if (drawHasse) color = colors[depth%numColors];
1208       else           color = colors[rank%numColors];
1209       for (l = 0; l < numLabels; ++l) {
1210         PetscInt val;
1211         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1212         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1213       }
1214       if (drawNumbers[dim]) {
1215         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1216       } else if (drawColors[dim]) {
1217         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1218       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1219     }
1220     if (drawHasse) {
1221       color = colors[depth%numColors];
1222       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1223       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1224       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1225       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1226       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1227 
1228       color = colors[1%numColors];
1229       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1230       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1231       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1232       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1233       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1234 
1235       color = colors[0%numColors];
1236       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1237       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1238       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1239       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1241 
1242       for (p = pStart; p < pEnd; ++p) {
1243         const PetscInt *cone;
1244         PetscInt        coneSize, cp;
1245 
1246         PetscCall(DMPlexGetCone(dm, p, &cone));
1247         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1248         for (cp = 0; cp < coneSize; ++cp) {
1249           PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1250         }
1251       }
1252     }
1253     PetscCall(PetscViewerFlush(viewer));
1254     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1255     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1256     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1257     for (l = 0; l < numLabels;  ++l) PetscCall(PetscFree(names[l]));
1258     for (c = 0; c < numColors;  ++c) PetscCall(PetscFree(colors[c]));
1259     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1260     PetscCall(PetscFree3(names, colors, lcolors));
1261     PetscCall(PetscBTDestroy(&wp));
1262   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1263     Vec                    cown,acown;
1264     VecScatter             sct;
1265     ISLocalToGlobalMapping g2l;
1266     IS                     gid,acis;
1267     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
1268     MPI_Group              ggroup,ngroup;
1269     PetscScalar            *array,nid;
1270     const PetscInt         *idxs;
1271     PetscInt               *idxs2,*start,*adjacency,*work;
1272     PetscInt64             lm[3],gm[3];
1273     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
1274     PetscMPIInt            d1,d2,rank;
1275 
1276     PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
1277     PetscCallMPI(MPI_Comm_rank(comm,&rank));
1278 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1279     PetscCallMPI(MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm));
1280 #endif
1281     if (ncomm != MPI_COMM_NULL) {
1282       PetscCallMPI(MPI_Comm_group(comm,&ggroup));
1283       PetscCallMPI(MPI_Comm_group(ncomm,&ngroup));
1284       d1   = 0;
1285       PetscCallMPI(MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2));
1286       nid  = d2;
1287       PetscCallMPI(MPI_Group_free(&ggroup));
1288       PetscCallMPI(MPI_Group_free(&ngroup));
1289       PetscCallMPI(MPI_Comm_free(&ncomm));
1290     } else nid = 0.0;
1291 
1292     /* Get connectivity */
1293     PetscCall(DMPlexGetVTKCellHeight(dm,&cellHeight));
1294     PetscCall(DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid));
1295 
1296     /* filter overlapped local cells */
1297     PetscCall(DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd));
1298     PetscCall(ISGetIndices(gid,&idxs));
1299     PetscCall(ISGetLocalSize(gid,&cum));
1300     PetscCall(PetscMalloc1(cum,&idxs2));
1301     for (c = cStart, cum = 0; c < cEnd; c++) {
1302       if (idxs[c-cStart] < 0) continue;
1303       idxs2[cum++] = idxs[c-cStart];
1304     }
1305     PetscCall(ISRestoreIndices(gid,&idxs));
1306     PetscCheck(numVertices == cum,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %" PetscInt_FMT " != %" PetscInt_FMT,numVertices,cum);
1307     PetscCall(ISDestroy(&gid));
1308     PetscCall(ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid));
1309 
1310     /* support for node-aware cell locality */
1311     PetscCall(ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis));
1312     PetscCall(VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown));
1313     PetscCall(VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown));
1314     PetscCall(VecGetArray(cown,&array));
1315     for (c = 0; c < numVertices; c++) array[c] = nid;
1316     PetscCall(VecRestoreArray(cown,&array));
1317     PetscCall(VecScatterCreate(cown,acis,acown,NULL,&sct));
1318     PetscCall(VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1319     PetscCall(VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1320     PetscCall(ISDestroy(&acis));
1321     PetscCall(VecScatterDestroy(&sct));
1322     PetscCall(VecDestroy(&cown));
1323 
1324     /* compute edgeCut */
1325     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
1326     PetscCall(PetscMalloc1(cum,&work));
1327     PetscCall(ISLocalToGlobalMappingCreateIS(gid,&g2l));
1328     PetscCall(ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH));
1329     PetscCall(ISDestroy(&gid));
1330     PetscCall(VecGetArray(acown,&array));
1331     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1332       PetscInt totl;
1333 
1334       totl = start[c+1]-start[c];
1335       PetscCall(ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work));
1336       for (i = 0; i < totl; i++) {
1337         if (work[i] < 0) {
1338           ect  += 1;
1339           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1340         }
1341       }
1342     }
1343     PetscCall(PetscFree(work));
1344     PetscCall(VecRestoreArray(acown,&array));
1345     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1346     lm[1] = -numVertices;
1347     PetscCall(MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm));
1348     PetscCall(PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT,-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]));
1349     lm[0] = ect; /* edgeCut */
1350     lm[1] = ectn; /* node-aware edgeCut */
1351     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1352     PetscCall(MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm));
1353     PetscCall(PetscViewerASCIIPrintf(viewer,", empty %" PetscInt_FMT ")\n",(PetscInt)gm[2]));
1354 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1355     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),gm[0] ? ((double)(gm[1]))/((double)gm[0]) : 1.));
1356 #else
1357     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0));
1358 #endif
1359     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1360     PetscCall(PetscFree(start));
1361     PetscCall(PetscFree(adjacency));
1362     PetscCall(VecDestroy(&acown));
1363   } else {
1364     const char    *name;
1365     PetscInt      *sizes, *hybsizes, *ghostsizes;
1366     PetscInt       locDepth, depth, cellHeight, dim, d;
1367     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1368     PetscInt       numLabels, l, maxSize = 17;
1369     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1370     MPI_Comm       comm;
1371     PetscMPIInt    size, rank;
1372 
1373     PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
1374     PetscCallMPI(MPI_Comm_size(comm, &size));
1375     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1376     PetscCall(DMGetDimension(dm, &dim));
1377     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1378     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1379     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1380     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1381     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1382     PetscCall(DMPlexGetDepth(dm, &locDepth));
1383     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1384     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1385     gcNum = gcEnd - gcStart;
1386     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1387     else                PetscCall(PetscCalloc3(3,    &sizes, 3,    &hybsizes, 3,    &ghostsizes));
1388     for (d = 0; d <= depth; d++) {
1389       PetscInt Nc[2] = {0, 0}, ict;
1390 
1391       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1392       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1393       ict  = ct0;
1394       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1395       ct0  = (DMPolytopeType) ict;
1396       for (p = pStart; p < pEnd; ++p) {
1397         DMPolytopeType ct;
1398 
1399         PetscCall(DMPlexGetCellType(dm, p, &ct));
1400         if (ct == ct0) ++Nc[0];
1401         else           ++Nc[1];
1402       }
1403       if (size < maxSize) {
1404         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm));
1405         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1406         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1407         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1408         for (p = 0; p < size; ++p) {
1409           if (rank == 0) {
1410             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p]+hybsizes[p]));
1411             if (hybsizes[p]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1412             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1413           }
1414         }
1415       } else {
1416         PetscInt locMinMax[2];
1417 
1418         locMinMax[0] = Nc[0]+Nc[1]; locMinMax[1] = Nc[0]+Nc[1];
1419         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1420         locMinMax[0] = Nc[1]; locMinMax[1] = Nc[1];
1421         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1422         if (d == depth) {
1423           locMinMax[0] = gcNum; locMinMax[1] = gcNum;
1424           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1425         }
1426         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1427         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1428         if (hybsizes[0]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1429         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1430       }
1431       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1432     }
1433     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1434     {
1435       const PetscReal *maxCell;
1436       const PetscReal *L;
1437       PetscBool        localized;
1438 
1439       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1440       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1441       if (L || localized) {
1442         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1443         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1444         if (L) {
1445           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1446           for (d = 0; d < dim; ++d) {
1447             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1448             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1449           }
1450           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1451         }
1452         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1453         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1454       }
1455     }
1456     PetscCall(DMGetNumLabels(dm, &numLabels));
1457     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1458     for (l = 0; l < numLabels; ++l) {
1459       DMLabel         label;
1460       const char     *name;
1461       IS              valueIS;
1462       const PetscInt *values;
1463       PetscInt        numValues, v;
1464 
1465       PetscCall(DMGetLabelName(dm, l, &name));
1466       PetscCall(DMGetLabel(dm, name, &label));
1467       PetscCall(DMLabelGetNumValues(label, &numValues));
1468       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1469       PetscCall(DMLabelGetValueIS(label, &valueIS));
1470       PetscCall(ISGetIndices(valueIS, &values));
1471       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1472       for (v = 0; v < numValues; ++v) {
1473         PetscInt size;
1474 
1475         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1476         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1477         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1478       }
1479       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1480       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1481       PetscCall(ISRestoreIndices(valueIS, &values));
1482       PetscCall(ISDestroy(&valueIS));
1483     }
1484     {
1485       char    **labelNames;
1486       PetscInt  Nl = numLabels;
1487       PetscBool flg;
1488 
1489       PetscCall(PetscMalloc1(Nl, &labelNames));
1490       PetscCall(PetscOptionsGetStringArray(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1491       for (l = 0; l < Nl; ++l) {
1492         DMLabel label;
1493 
1494         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1495         if (flg) {
1496           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1497           PetscCall(DMLabelView(label, viewer));
1498         }
1499         PetscCall(PetscFree(labelNames[l]));
1500       }
1501       PetscCall(PetscFree(labelNames));
1502     }
1503     /* If no fields are specified, people do not want to see adjacency */
1504     if (dm->Nf) {
1505       PetscInt f;
1506 
1507       for (f = 0; f < dm->Nf; ++f) {
1508         const char *name;
1509 
1510         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1511         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1512         PetscCall(PetscViewerASCIIPushTab(viewer));
1513         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1514         if (dm->fields[f].adjacency[0]) {
1515           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1516           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1517         } else {
1518           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1519           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1520         }
1521         PetscCall(PetscViewerASCIIPopTab(viewer));
1522       }
1523     }
1524     PetscCall(DMGetCoarseDM(dm, &cdm));
1525     if (cdm) {
1526       PetscCall(PetscViewerASCIIPushTab(viewer));
1527       PetscCall(DMPlexView_Ascii(cdm, viewer));
1528       PetscCall(PetscViewerASCIIPopTab(viewer));
1529     }
1530   }
1531   PetscFunctionReturn(0);
1532 }
1533 
1534 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1535 {
1536   DMPolytopeType ct;
1537   PetscMPIInt    rank;
1538   PetscInt       cdim;
1539 
1540   PetscFunctionBegin;
1541   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1542   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1543   PetscCall(DMGetCoordinateDim(dm, &cdim));
1544   switch (ct) {
1545   case DM_POLYTOPE_SEGMENT:
1546   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1547     switch (cdim) {
1548     case 1:
1549     {
1550       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1551       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1552 
1553       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y,    PetscRealPart(coords[1]), y,    PETSC_DRAW_BLACK));
1554       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y+dy, PetscRealPart(coords[0]), y-dy, PETSC_DRAW_BLACK));
1555       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y+dy, PetscRealPart(coords[1]), y-dy, PETSC_DRAW_BLACK));
1556     }
1557     break;
1558     case 2:
1559     {
1560       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1561       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1562       const PetscReal l  = 0.1/PetscSqrtReal(dx*dx + dy*dy);
1563 
1564       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1565       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));
1566       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));
1567     }
1568     break;
1569     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1570     }
1571     break;
1572   case DM_POLYTOPE_TRIANGLE:
1573     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1574                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1575                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1576                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1577     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1578     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1579     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1580     break;
1581   case DM_POLYTOPE_QUADRILATERAL:
1582     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1583                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1584                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1585                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1586     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1587                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1588                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1589                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1590     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1591     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1592     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1593     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1594     break;
1595   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1596   }
1597   PetscFunctionReturn(0);
1598 }
1599 
1600 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1601 {
1602   DMPolytopeType ct;
1603   PetscReal      centroid[2] = {0., 0.};
1604   PetscMPIInt    rank;
1605   PetscInt       fillColor, v, e, d;
1606 
1607   PetscFunctionBegin;
1608   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1609   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1610   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2;
1611   switch (ct) {
1612   case DM_POLYTOPE_TRIANGLE:
1613     {
1614       PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1615 
1616       for (v = 0; v < 3; ++v) {centroid[0] += PetscRealPart(coords[v*2+0])/3.;centroid[1] += PetscRealPart(coords[v*2+1])/3.;}
1617       for (e = 0; e < 3; ++e) {
1618         refCoords[0] = refVertices[e*2+0];
1619         refCoords[1] = refVertices[e*2+1];
1620         for (d = 1; d <= edgeDiv; ++d) {
1621           refCoords[d*2+0] = refCoords[0] + (refVertices[(e+1)%3 * 2 + 0] - refCoords[0])*d/edgeDiv;
1622           refCoords[d*2+1] = refCoords[1] + (refVertices[(e+1)%3 * 2 + 1] - refCoords[1])*d/edgeDiv;
1623         }
1624         PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv+1, refCoords, edgeCoords));
1625         for (d = 0; d < edgeDiv; ++d) {
1626           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));
1627           PetscCall(PetscDrawLine(draw, edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], PETSC_DRAW_BLACK));
1628         }
1629       }
1630     }
1631     break;
1632   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1633   }
1634   PetscFunctionReturn(0);
1635 }
1636 
1637 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1638 {
1639   PetscDraw          draw;
1640   DM                 cdm;
1641   PetscSection       coordSection;
1642   Vec                coordinates;
1643   const PetscScalar *coords;
1644   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1645   PetscReal         *refCoords, *edgeCoords;
1646   PetscBool          isnull, drawAffine = PETSC_TRUE;
1647   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1648 
1649   PetscFunctionBegin;
1650   PetscCall(DMGetCoordinateDim(dm, &dim));
1651   PetscCheck(dim <= 2,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1652   PetscCall(PetscOptionsGetBool(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1653   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv+1)*dim, &refCoords, (edgeDiv+1)*dim, &edgeCoords));
1654   PetscCall(DMGetCoordinateDM(dm, &cdm));
1655   PetscCall(DMGetLocalSection(cdm, &coordSection));
1656   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1657   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1658   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1659 
1660   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1661   PetscCall(PetscDrawIsNull(draw, &isnull));
1662   if (isnull) PetscFunctionReturn(0);
1663   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1664 
1665   PetscCall(VecGetLocalSize(coordinates, &N));
1666   PetscCall(VecGetArrayRead(coordinates, &coords));
1667   for (c = 0; c < N; c += dim) {
1668     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1669     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1670   }
1671   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1672   PetscCall(MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm)));
1673   PetscCall(MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm)));
1674   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1675   PetscCall(PetscDrawClear(draw));
1676 
1677   for (c = cStart; c < cEnd; ++c) {
1678     PetscScalar *coords = NULL;
1679     PetscInt     numCoords;
1680 
1681     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1682     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1683     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1684     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1685   }
1686   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1687   PetscCall(PetscDrawFlush(draw));
1688   PetscCall(PetscDrawPause(draw));
1689   PetscCall(PetscDrawSave(draw));
1690   PetscFunctionReturn(0);
1691 }
1692 
1693 #if defined(PETSC_HAVE_EXODUSII)
1694 #include <exodusII.h>
1695 #include <petscviewerexodusii.h>
1696 #endif
1697 
1698 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1699 {
1700   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus;
1701   char           name[PETSC_MAX_PATH_LEN];
1702 
1703   PetscFunctionBegin;
1704   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1705   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1706   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii));
1707   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
1708   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
1709   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
1710   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
1711   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus));
1712   if (iascii) {
1713     PetscViewerFormat format;
1714     PetscCall(PetscViewerGetFormat(viewer, &format));
1715     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1716     else PetscCall(DMPlexView_Ascii(dm, viewer));
1717   } else if (ishdf5) {
1718 #if defined(PETSC_HAVE_HDF5)
1719     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1720 #else
1721     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1722 #endif
1723   } else if (isvtk) {
1724     PetscCall(DMPlexVTKWriteAll((PetscObject) dm,viewer));
1725   } else if (isdraw) {
1726     PetscCall(DMPlexView_Draw(dm, viewer));
1727   } else if (isglvis) {
1728     PetscCall(DMPlexView_GLVis(dm, viewer));
1729 #if defined(PETSC_HAVE_EXODUSII)
1730   } else if (isexodus) {
1731 /*
1732       exodusII requires that all sets be part of exactly one cell set.
1733       If the dm does not have a "Cell Sets" label defined, we create one
1734       with ID 1, containig all cells.
1735       Note that if the Cell Sets label is defined but does not cover all cells,
1736       we may still have a problem. This should probably be checked here or in the viewer;
1737     */
1738     PetscInt numCS;
1739     PetscCall(DMGetLabelSize(dm,"Cell Sets",&numCS));
1740     if (!numCS) {
1741       PetscInt cStart, cEnd, c;
1742       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1743       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1744       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1745     }
1746     PetscCall(DMView_PlexExodusII(dm, viewer));
1747 #endif
1748   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1749 
1750   /* Optionally view the partition */
1751   PetscCall(PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg));
1752   if (flg) {
1753     Vec ranks;
1754     PetscCall(DMPlexCreateRankField(dm, &ranks));
1755     PetscCall(VecView(ranks, viewer));
1756     PetscCall(VecDestroy(&ranks));
1757   }
1758   /* Optionally view a label */
1759   PetscCall(PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1760   if (flg) {
1761     DMLabel label;
1762     Vec     val;
1763 
1764     PetscCall(DMGetLabel(dm, name, &label));
1765     PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1766     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1767     PetscCall(VecView(val, viewer));
1768     PetscCall(VecDestroy(&val));
1769   }
1770   PetscFunctionReturn(0);
1771 }
1772 
1773 /*@
1774   DMPlexTopologyView - Saves a DMPlex topology into a file
1775 
1776   Collective on DM
1777 
1778   Input Parameters:
1779 + dm     - The DM whose topology is to be saved
1780 - viewer - The PetscViewer for saving
1781 
1782   Level: advanced
1783 
1784 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1785 @*/
1786 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1787 {
1788   PetscBool      ishdf5;
1789 
1790   PetscFunctionBegin;
1791   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1792   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1793   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1794   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView,viewer,0,0,0));
1795   if (ishdf5) {
1796 #if defined(PETSC_HAVE_HDF5)
1797     PetscViewerFormat format;
1798     PetscCall(PetscViewerGetFormat(viewer, &format));
1799     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1800       IS globalPointNumbering;
1801 
1802       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1803       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1804       PetscCall(ISDestroy(&globalPointNumbering));
1805     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1806 #else
1807     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1808 #endif
1809   }
1810   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView,viewer,0,0,0));
1811   PetscFunctionReturn(0);
1812 }
1813 
1814 /*@
1815   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1816 
1817   Collective on DM
1818 
1819   Input Parameters:
1820 + dm     - The DM whose coordinates are to be saved
1821 - viewer - The PetscViewer for saving
1822 
1823   Level: advanced
1824 
1825 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1826 @*/
1827 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1828 {
1829   PetscBool      ishdf5;
1830 
1831   PetscFunctionBegin;
1832   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1833   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1834   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1835   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView,viewer,0,0,0));
1836   if (ishdf5) {
1837 #if defined(PETSC_HAVE_HDF5)
1838     PetscViewerFormat format;
1839     PetscCall(PetscViewerGetFormat(viewer, &format));
1840     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1841       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1842     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1843 #else
1844     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1845 #endif
1846   }
1847   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView,viewer,0,0,0));
1848   PetscFunctionReturn(0);
1849 }
1850 
1851 /*@
1852   DMPlexLabelsView - Saves DMPlex labels into a file
1853 
1854   Collective on DM
1855 
1856   Input Parameters:
1857 + dm     - The DM whose labels are to be saved
1858 - viewer - The PetscViewer for saving
1859 
1860   Level: advanced
1861 
1862 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1863 @*/
1864 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1865 {
1866   PetscBool      ishdf5;
1867 
1868   PetscFunctionBegin;
1869   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1870   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1871   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1872   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView,viewer,0,0,0));
1873   if (ishdf5) {
1874 #if defined(PETSC_HAVE_HDF5)
1875     IS                globalPointNumbering;
1876     PetscViewerFormat format;
1877 
1878     PetscCall(PetscViewerGetFormat(viewer, &format));
1879     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1880       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1881       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1882       PetscCall(ISDestroy(&globalPointNumbering));
1883     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1884 #else
1885     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1886 #endif
1887   }
1888   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView,viewer,0,0,0));
1889   PetscFunctionReturn(0);
1890 }
1891 
1892 /*@
1893   DMPlexSectionView - Saves a section associated with a DMPlex
1894 
1895   Collective on DM
1896 
1897   Input Parameters:
1898 + dm         - The DM that contains the topology on which the section to be saved is defined
1899 . viewer     - The PetscViewer for saving
1900 - sectiondm  - The DM that contains the section to be saved
1901 
1902   Level: advanced
1903 
1904   Notes:
1905   This function is a wrapper around PetscSectionView(); in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with DMPlexTopologyLoad() and DMPlexSectionLoad(), respectively, this information is used to match section points with topology points.
1906 
1907   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1908 
1909 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1910 @*/
1911 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1912 {
1913   PetscBool      ishdf5;
1914 
1915   PetscFunctionBegin;
1916   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1917   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1918   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1919   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
1920   PetscCall(PetscLogEventBegin(DMPLEX_SectionView,viewer,0,0,0));
1921   if (ishdf5) {
1922 #if defined(PETSC_HAVE_HDF5)
1923     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1924 #else
1925     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1926 #endif
1927   }
1928   PetscCall(PetscLogEventEnd(DMPLEX_SectionView,viewer,0,0,0));
1929   PetscFunctionReturn(0);
1930 }
1931 
1932 /*@
1933   DMPlexGlobalVectorView - Saves a global vector
1934 
1935   Collective on DM
1936 
1937   Input Parameters:
1938 + dm        - The DM that represents the topology
1939 . viewer    - The PetscViewer to save data with
1940 . sectiondm - The DM that contains the global section on which vec is defined
1941 - vec       - The global vector to be saved
1942 
1943   Level: advanced
1944 
1945   Notes:
1946   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1947 
1948   Typical calling sequence
1949 $       DMCreate(PETSC_COMM_WORLD, &dm);
1950 $       DMSetType(dm, DMPLEX);
1951 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1952 $       DMClone(dm, &sectiondm);
1953 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1954 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1955 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1956 $       PetscSectionSetChart(section, pStart, pEnd);
1957 $       PetscSectionSetUp(section);
1958 $       DMSetLocalSection(sectiondm, section);
1959 $       PetscSectionDestroy(&section);
1960 $       DMGetGlobalVector(sectiondm, &vec);
1961 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1962 $       DMPlexTopologyView(dm, viewer);
1963 $       DMPlexSectionView(dm, viewer, sectiondm);
1964 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1965 $       DMRestoreGlobalVector(sectiondm, &vec);
1966 $       DMDestroy(&sectiondm);
1967 $       DMDestroy(&dm);
1968 
1969 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1970 @*/
1971 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
1972 {
1973   PetscBool       ishdf5;
1974 
1975   PetscFunctionBegin;
1976   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1977   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1978   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1979   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1980   /* Check consistency */
1981   {
1982     PetscSection  section;
1983     PetscBool     includesConstraints;
1984     PetscInt      m, m1;
1985 
1986     PetscCall(VecGetLocalSize(vec, &m1));
1987     PetscCall(DMGetGlobalSection(sectiondm, &section));
1988     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1989     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
1990     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
1991     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
1992   }
1993   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1994   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView,viewer,0,0,0));
1995   if (ishdf5) {
1996 #if defined(PETSC_HAVE_HDF5)
1997     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
1998 #else
1999     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2000 #endif
2001   }
2002   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView,viewer,0,0,0));
2003   PetscFunctionReturn(0);
2004 }
2005 
2006 /*@
2007   DMPlexLocalVectorView - Saves a local vector
2008 
2009   Collective on DM
2010 
2011   Input Parameters:
2012 + dm        - The DM that represents the topology
2013 . viewer    - The PetscViewer to save data with
2014 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2015 - vec       - The local vector to be saved
2016 
2017   Level: advanced
2018 
2019   Notes:
2020   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2021 
2022   Typical calling sequence
2023 $       DMCreate(PETSC_COMM_WORLD, &dm);
2024 $       DMSetType(dm, DMPLEX);
2025 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2026 $       DMClone(dm, &sectiondm);
2027 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2028 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2029 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2030 $       PetscSectionSetChart(section, pStart, pEnd);
2031 $       PetscSectionSetUp(section);
2032 $       DMSetLocalSection(sectiondm, section);
2033 $       DMGetLocalVector(sectiondm, &vec);
2034 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2035 $       DMPlexTopologyView(dm, viewer);
2036 $       DMPlexSectionView(dm, viewer, sectiondm);
2037 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2038 $       DMRestoreLocalVector(sectiondm, &vec);
2039 $       DMDestroy(&sectiondm);
2040 $       DMDestroy(&dm);
2041 
2042 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2043 @*/
2044 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2045 {
2046   PetscBool       ishdf5;
2047 
2048   PetscFunctionBegin;
2049   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2050   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2051   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2052   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2053   /* Check consistency */
2054   {
2055     PetscSection  section;
2056     PetscBool     includesConstraints;
2057     PetscInt      m, m1;
2058 
2059     PetscCall(VecGetLocalSize(vec, &m1));
2060     PetscCall(DMGetLocalSection(sectiondm, &section));
2061     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2062     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2063     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2064     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2065   }
2066   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2067   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView,viewer,0,0,0));
2068   if (ishdf5) {
2069 #if defined(PETSC_HAVE_HDF5)
2070     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2071 #else
2072     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2073 #endif
2074   }
2075   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView,viewer,0,0,0));
2076   PetscFunctionReturn(0);
2077 }
2078 
2079 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2080 {
2081   PetscBool      ishdf5;
2082 
2083   PetscFunctionBegin;
2084   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2085   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2086   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5));
2087   if (ishdf5) {
2088 #if defined(PETSC_HAVE_HDF5)
2089     PetscViewerFormat format;
2090     PetscCall(PetscViewerGetFormat(viewer, &format));
2091     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2092       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2093     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2094       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2095     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2096     PetscFunctionReturn(0);
2097 #else
2098     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2099 #endif
2100   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2101 }
2102 
2103 /*@
2104   DMPlexTopologyLoad - Loads a topology into a DMPlex
2105 
2106   Collective on DM
2107 
2108   Input Parameters:
2109 + dm     - The DM into which the topology is loaded
2110 - viewer - The PetscViewer for the saved topology
2111 
2112   Output Parameters:
2113 . globalToLocalPointSF - The PetscSF that pushes points in [0, N) to the associated points in the loaded plex, where N is the global number of points; NULL if unneeded
2114 
2115   Level: advanced
2116 
2117 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2118 @*/
2119 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2120 {
2121   PetscBool      ishdf5;
2122 
2123   PetscFunctionBegin;
2124   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2125   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2126   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2127   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2128   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad,viewer,0,0,0));
2129   if (ishdf5) {
2130 #if defined(PETSC_HAVE_HDF5)
2131     PetscViewerFormat format;
2132     PetscCall(PetscViewerGetFormat(viewer, &format));
2133     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2134       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2135     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2136 #else
2137     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2138 #endif
2139   }
2140   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad,viewer,0,0,0));
2141   PetscFunctionReturn(0);
2142 }
2143 
2144 /*@
2145   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2146 
2147   Collective on DM
2148 
2149   Input Parameters:
2150 + dm     - The DM into which the coordinates are loaded
2151 . viewer - The PetscViewer for the saved coordinates
2152 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2153 
2154   Level: advanced
2155 
2156 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2157 @*/
2158 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2159 {
2160   PetscBool      ishdf5;
2161 
2162   PetscFunctionBegin;
2163   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2164   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2165   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2166   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2167   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2168   if (ishdf5) {
2169 #if defined(PETSC_HAVE_HDF5)
2170     PetscViewerFormat format;
2171     PetscCall(PetscViewerGetFormat(viewer, &format));
2172     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2173       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2174     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2175 #else
2176     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2177 #endif
2178   }
2179   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2180   PetscFunctionReturn(0);
2181 }
2182 
2183 /*@
2184   DMPlexLabelsLoad - Loads labels into a DMPlex
2185 
2186   Collective on DM
2187 
2188   Input Parameters:
2189 + dm     - The DM into which the labels are loaded
2190 . viewer - The PetscViewer for the saved labels
2191 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2192 
2193   Level: advanced
2194 
2195   Notes:
2196   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2197 
2198 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2199 @*/
2200 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2201 {
2202   PetscBool      ishdf5;
2203 
2204   PetscFunctionBegin;
2205   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2206   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2207   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2208   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2209   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad,viewer,0,0,0));
2210   if (ishdf5) {
2211 #if defined(PETSC_HAVE_HDF5)
2212     PetscViewerFormat format;
2213 
2214     PetscCall(PetscViewerGetFormat(viewer, &format));
2215     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2216       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2217     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2218 #else
2219     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2220 #endif
2221   }
2222   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad,viewer,0,0,0));
2223   PetscFunctionReturn(0);
2224 }
2225 
2226 /*@
2227   DMPlexSectionLoad - Loads section into a DMPlex
2228 
2229   Collective on DM
2230 
2231   Input Parameters:
2232 + dm          - The DM that represents the topology
2233 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2234 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2235 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2236 
2237   Output Parameters
2238 + globalDofSF - The SF that migrates any on-disk Vec data associated with sectionA into a global Vec associated with the sectiondm's global section (NULL if not needed)
2239 - localDofSF  - The SF that migrates any on-disk Vec data associated with sectionA into a local Vec associated with the sectiondm's local section (NULL if not needed)
2240 
2241   Level: advanced
2242 
2243   Notes:
2244   This function is a wrapper around PetscSectionLoad(); it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in dm. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2245 
2246   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2247 
2248   The output parameter, globalDofSF (localDofSF), can later be used with DMPlexGlobalVectorLoad() (DMPlexLocalVectorLoad()) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2249 
2250   Example using 2 processes:
2251 $  NX (number of points on dm): 4
2252 $  sectionA                   : the on-disk section
2253 $  vecA                       : a vector associated with sectionA
2254 $  sectionB                   : sectiondm's local section constructed in this function
2255 $  vecB (local)               : a vector associated with sectiondm's local section
2256 $  vecB (global)              : a vector associated with sectiondm's global section
2257 $
2258 $                                     rank 0    rank 1
2259 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2260 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2261 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2262 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2263 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2264 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2265 $  sectionB->atlasDof             :     1 0 1 | 1 3
2266 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2267 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2268 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2269 $
2270 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2271 
2272 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2273 @*/
2274 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2275 {
2276   PetscBool      ishdf5;
2277 
2278   PetscFunctionBegin;
2279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2280   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2281   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2282   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2283   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2284   if (localDofSF) PetscValidPointer(localDofSF, 6);
2285   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2286   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad,viewer,0,0,0));
2287   if (ishdf5) {
2288 #if defined(PETSC_HAVE_HDF5)
2289     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2290 #else
2291     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2292 #endif
2293   }
2294   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad,viewer,0,0,0));
2295   PetscFunctionReturn(0);
2296 }
2297 
2298 /*@
2299   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2300 
2301   Collective on DM
2302 
2303   Input Parameters:
2304 + dm        - The DM that represents the topology
2305 . viewer    - The PetscViewer that represents the on-disk vector data
2306 . sectiondm - The DM that contains the global section on which vec is defined
2307 . sf        - The SF that migrates the on-disk vector data into vec
2308 - vec       - The global vector to set values of
2309 
2310   Level: advanced
2311 
2312   Notes:
2313   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2314 
2315   Typical calling sequence
2316 $       DMCreate(PETSC_COMM_WORLD, &dm);
2317 $       DMSetType(dm, DMPLEX);
2318 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2319 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2320 $       DMClone(dm, &sectiondm);
2321 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2322 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2323 $       DMGetGlobalVector(sectiondm, &vec);
2324 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2325 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2326 $       DMRestoreGlobalVector(sectiondm, &vec);
2327 $       PetscSFDestroy(&gsf);
2328 $       PetscSFDestroy(&sfX);
2329 $       DMDestroy(&sectiondm);
2330 $       DMDestroy(&dm);
2331 
2332 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2333 @*/
2334 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2335 {
2336   PetscBool       ishdf5;
2337 
2338   PetscFunctionBegin;
2339   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2340   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2341   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2342   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2343   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2344   /* Check consistency */
2345   {
2346     PetscSection  section;
2347     PetscBool     includesConstraints;
2348     PetscInt      m, m1;
2349 
2350     PetscCall(VecGetLocalSize(vec, &m1));
2351     PetscCall(DMGetGlobalSection(sectiondm, &section));
2352     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2353     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2354     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2355     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2356   }
2357   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2358   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2359   if (ishdf5) {
2360 #if defined(PETSC_HAVE_HDF5)
2361     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2362 #else
2363     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2364 #endif
2365   }
2366   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2367   PetscFunctionReturn(0);
2368 }
2369 
2370 /*@
2371   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2372 
2373   Collective on DM
2374 
2375   Input Parameters:
2376 + dm        - The DM that represents the topology
2377 . viewer    - The PetscViewer that represents the on-disk vector data
2378 . sectiondm - The DM that contains the local section on which vec is defined
2379 . sf        - The SF that migrates the on-disk vector data into vec
2380 - vec       - The local vector to set values of
2381 
2382   Level: advanced
2383 
2384   Notes:
2385   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2386 
2387   Typical calling sequence
2388 $       DMCreate(PETSC_COMM_WORLD, &dm);
2389 $       DMSetType(dm, DMPLEX);
2390 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2391 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2392 $       DMClone(dm, &sectiondm);
2393 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2394 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2395 $       DMGetLocalVector(sectiondm, &vec);
2396 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2397 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2398 $       DMRestoreLocalVector(sectiondm, &vec);
2399 $       PetscSFDestroy(&lsf);
2400 $       PetscSFDestroy(&sfX);
2401 $       DMDestroy(&sectiondm);
2402 $       DMDestroy(&dm);
2403 
2404 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2405 @*/
2406 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2407 {
2408   PetscBool       ishdf5;
2409 
2410   PetscFunctionBegin;
2411   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2412   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2413   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2414   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2415   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2416   /* Check consistency */
2417   {
2418     PetscSection  section;
2419     PetscBool     includesConstraints;
2420     PetscInt      m, m1;
2421 
2422     PetscCall(VecGetLocalSize(vec, &m1));
2423     PetscCall(DMGetLocalSection(sectiondm, &section));
2424     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2425     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2426     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2427     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2428   }
2429   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2430   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2431   if (ishdf5) {
2432 #if defined(PETSC_HAVE_HDF5)
2433     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2434 #else
2435     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2436 #endif
2437   }
2438   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2439   PetscFunctionReturn(0);
2440 }
2441 
2442 PetscErrorCode DMDestroy_Plex(DM dm)
2443 {
2444   DM_Plex       *mesh = (DM_Plex*) dm->data;
2445 
2446   PetscFunctionBegin;
2447   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL));
2448   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL));
2449   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL));
2450   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMInterpolateSolution_C", NULL));
2451   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2452   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C", NULL));
2453   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeGetDefault_C", NULL));
2454   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeSetDefault_C", NULL));
2455   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"MatComputeNeumannOverlap_C",NULL));
2456   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderGetDefault_C", NULL));
2457   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderSetDefault_C", NULL));
2458   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C",NULL));
2459   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexSetOverlap_C",NULL));
2460   if (--mesh->refct > 0) PetscFunctionReturn(0);
2461   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2462   PetscCall(PetscFree(mesh->cones));
2463   PetscCall(PetscFree(mesh->coneOrientations));
2464   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2465   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2466   PetscCall(PetscFree(mesh->supports));
2467   PetscCall(PetscFree(mesh->facesTmp));
2468   PetscCall(PetscFree(mesh->tetgenOpts));
2469   PetscCall(PetscFree(mesh->triangleOpts));
2470   PetscCall(PetscFree(mesh->transformType));
2471   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2472   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2473   PetscCall(ISDestroy(&mesh->subpointIS));
2474   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2475   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2476   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2477   PetscCall(ISDestroy(&mesh->anchorIS));
2478   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2479   PetscCall(PetscFree(mesh->parents));
2480   PetscCall(PetscFree(mesh->childIDs));
2481   PetscCall(PetscSectionDestroy(&mesh->childSection));
2482   PetscCall(PetscFree(mesh->children));
2483   PetscCall(DMDestroy(&mesh->referenceTree));
2484   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2485   PetscCall(PetscFree(mesh->neighbors));
2486   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2487   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2488   PetscCall(PetscFree(mesh));
2489   PetscFunctionReturn(0);
2490 }
2491 
2492 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2493 {
2494   PetscSection           sectionGlobal;
2495   PetscInt               bs = -1, mbs;
2496   PetscInt               localSize, localStart = 0;
2497   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2498   MatType                mtype;
2499   ISLocalToGlobalMapping ltog;
2500 
2501   PetscFunctionBegin;
2502   PetscCall(MatInitializePackage());
2503   mtype = dm->mattype;
2504   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2505   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2506   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2507   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject) dm)));
2508   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2509   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2510   PetscCall(MatSetType(*J, mtype));
2511   PetscCall(MatSetFromOptions(*J));
2512   PetscCall(MatGetBlockSize(*J, &mbs));
2513   if (mbs > 1) bs = mbs;
2514   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2515   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2516   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2517   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2518   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2519   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2520   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2521   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2522   if (!isShell) {
2523     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2524     PetscInt  *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2525     PetscInt  pStart, pEnd, p, dof, cdof;
2526 
2527     PetscCall(DMGetLocalToGlobalMapping(dm,&ltog));
2528 
2529     PetscCall(PetscCalloc1(localSize, &pblocks));
2530     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2531     for (p = pStart; p < pEnd; ++p) {
2532       PetscInt bdof, offset;
2533 
2534       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2535       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2536       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2537       for (PetscInt i=0; i < dof - cdof; i++)
2538         pblocks[offset - localStart + i] = dof - cdof;
2539       dof  = dof < 0 ? -(dof+1) : dof;
2540       bdof = cdof && (dof-cdof) ? 1 : dof;
2541       if (dof) {
2542         if (bs < 0)          {bs = bdof;}
2543         else if (bs != bdof) {bs = 1;}
2544       }
2545     }
2546     /* Must have same blocksize on all procs (some might have no points) */
2547     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2548     bsLocal[1] = bs;
2549     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
2550     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2551     else bs = bsMinMax[0];
2552     bs = PetscMax(1,bs);
2553     PetscCall(MatSetLocalToGlobalMapping(*J,ltog,ltog));
2554     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2555       PetscCall(MatSetBlockSize(*J, bs));
2556       PetscCall(MatSetUp(*J));
2557     } else {
2558       PetscCall(PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu));
2559       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2560       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2561     }
2562     { // Consolidate blocks
2563       PetscInt nblocks = 0;
2564       for (PetscInt i=0; i<localSize; i += PetscMax(1, pblocks[i])) {
2565         if (pblocks[i] == 0) continue;
2566         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2567         for (PetscInt j=1; j<pblocks[i]; j++) {
2568            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]);
2569         }
2570       }
2571       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2572     }
2573     PetscCall(PetscFree(pblocks));
2574   }
2575   PetscCall(MatSetDM(*J, dm));
2576   PetscFunctionReturn(0);
2577 }
2578 
2579 /*@
2580   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2581 
2582   Not collective
2583 
2584   Input Parameter:
2585 . mesh - The DMPlex
2586 
2587   Output Parameters:
2588 . subsection - The subdomain section
2589 
2590   Level: developer
2591 
2592 .seealso:
2593 @*/
2594 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2595 {
2596   DM_Plex       *mesh = (DM_Plex*) dm->data;
2597 
2598   PetscFunctionBegin;
2599   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2600   if (!mesh->subdomainSection) {
2601     PetscSection section;
2602     PetscSF      sf;
2603 
2604     PetscCall(PetscSFCreate(PETSC_COMM_SELF,&sf));
2605     PetscCall(DMGetLocalSection(dm,&section));
2606     PetscCall(PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection));
2607     PetscCall(PetscSFDestroy(&sf));
2608   }
2609   *subsection = mesh->subdomainSection;
2610   PetscFunctionReturn(0);
2611 }
2612 
2613 /*@
2614   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2615 
2616   Not collective
2617 
2618   Input Parameter:
2619 . mesh - The DMPlex
2620 
2621   Output Parameters:
2622 + pStart - The first mesh point
2623 - pEnd   - The upper bound for mesh points
2624 
2625   Level: beginner
2626 
2627 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2628 @*/
2629 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2630 {
2631   DM_Plex       *mesh = (DM_Plex*) dm->data;
2632 
2633   PetscFunctionBegin;
2634   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2635   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2636   PetscFunctionReturn(0);
2637 }
2638 
2639 /*@
2640   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2641 
2642   Not collective
2643 
2644   Input Parameters:
2645 + mesh - The DMPlex
2646 . pStart - The first mesh point
2647 - pEnd   - The upper bound for mesh points
2648 
2649   Output Parameters:
2650 
2651   Level: beginner
2652 
2653 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2654 @*/
2655 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2656 {
2657   DM_Plex       *mesh = (DM_Plex*) dm->data;
2658 
2659   PetscFunctionBegin;
2660   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2661   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2662   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2663   PetscFunctionReturn(0);
2664 }
2665 
2666 /*@
2667   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2668 
2669   Not collective
2670 
2671   Input Parameters:
2672 + mesh - The DMPlex
2673 - p - The point, which must lie in the chart set with DMPlexSetChart()
2674 
2675   Output Parameter:
2676 . size - The cone size for point p
2677 
2678   Level: beginner
2679 
2680 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2681 @*/
2682 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2683 {
2684   DM_Plex       *mesh = (DM_Plex*) dm->data;
2685 
2686   PetscFunctionBegin;
2687   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2688   PetscValidIntPointer(size, 3);
2689   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2690   PetscFunctionReturn(0);
2691 }
2692 
2693 /*@
2694   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2695 
2696   Not collective
2697 
2698   Input Parameters:
2699 + mesh - The DMPlex
2700 . p - The point, which must lie in the chart set with DMPlexSetChart()
2701 - size - The cone size for point p
2702 
2703   Output Parameter:
2704 
2705   Note:
2706   This should be called after DMPlexSetChart().
2707 
2708   Level: beginner
2709 
2710 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2711 @*/
2712 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2713 {
2714   DM_Plex       *mesh = (DM_Plex*) dm->data;
2715 
2716   PetscFunctionBegin;
2717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2718   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2719   PetscFunctionReturn(0);
2720 }
2721 
2722 /*@
2723   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2724 
2725   Not collective
2726 
2727   Input Parameters:
2728 + mesh - The DMPlex
2729 . p - The point, which must lie in the chart set with DMPlexSetChart()
2730 - size - The additional cone size for point p
2731 
2732   Output Parameter:
2733 
2734   Note:
2735   This should be called after DMPlexSetChart().
2736 
2737   Level: beginner
2738 
2739 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2740 @*/
2741 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2742 {
2743   DM_Plex       *mesh = (DM_Plex*) dm->data;
2744   PetscFunctionBegin;
2745   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2746   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2747   PetscFunctionReturn(0);
2748 }
2749 
2750 /*@C
2751   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2752 
2753   Not collective
2754 
2755   Input Parameters:
2756 + dm - The DMPlex
2757 - p - The point, which must lie in the chart set with DMPlexSetChart()
2758 
2759   Output Parameter:
2760 . cone - An array of points which are on the in-edges for point p
2761 
2762   Level: beginner
2763 
2764   Fortran Notes:
2765   Since it returns an array, this routine is only available in Fortran 90, and you must
2766   include petsc.h90 in your code.
2767   You must also call DMPlexRestoreCone() after you finish using the returned array.
2768   DMPlexRestoreCone() is not needed/available in C.
2769 
2770 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2771 @*/
2772 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2773 {
2774   DM_Plex       *mesh = (DM_Plex*) dm->data;
2775   PetscInt       off;
2776 
2777   PetscFunctionBegin;
2778   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2779   PetscValidPointer(cone, 3);
2780   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2781   *cone = &mesh->cones[off];
2782   PetscFunctionReturn(0);
2783 }
2784 
2785 /*@C
2786   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2787 
2788   Not collective
2789 
2790   Input Parameters:
2791 + dm - The DMPlex
2792 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2793 
2794   Output Parameters:
2795 + pConesSection - PetscSection describing the layout of pCones
2796 - pCones - An array of points which are on the in-edges for the point set p
2797 
2798   Level: intermediate
2799 
2800 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2801 @*/
2802 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2803 {
2804   PetscSection        cs, newcs;
2805   PetscInt            *cones;
2806   PetscInt            *newarr=NULL;
2807   PetscInt            n;
2808 
2809   PetscFunctionBegin;
2810   PetscCall(DMPlexGetCones(dm, &cones));
2811   PetscCall(DMPlexGetConeSection(dm, &cs));
2812   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL));
2813   if (pConesSection) *pConesSection = newcs;
2814   if (pCones) {
2815     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2816     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2817   }
2818   PetscFunctionReturn(0);
2819 }
2820 
2821 /*@
2822   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2823 
2824   Not collective
2825 
2826   Input Parameters:
2827 + dm - The DMPlex
2828 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2829 
2830   Output Parameter:
2831 . expandedPoints - An array of vertices recursively expanded from input points
2832 
2833   Level: advanced
2834 
2835   Notes:
2836   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2837   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2838 
2839 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2840 @*/
2841 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2842 {
2843   IS                  *expandedPointsAll;
2844   PetscInt            depth;
2845 
2846   PetscFunctionBegin;
2847   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2848   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2849   PetscValidPointer(expandedPoints, 3);
2850   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2851   *expandedPoints = expandedPointsAll[0];
2852   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2853   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2854   PetscFunctionReturn(0);
2855 }
2856 
2857 /*@
2858   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).
2859 
2860   Not collective
2861 
2862   Input Parameters:
2863 + dm - The DMPlex
2864 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2865 
2866   Output Parameters:
2867 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2868 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2869 - sections - (optional) An array of sections which describe mappings from points to their cone points
2870 
2871   Level: advanced
2872 
2873   Notes:
2874   Like DMPlexGetConeTuple() but recursive.
2875 
2876   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.
2877   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2878 
2879   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:
2880   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2881   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2882 
2883 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2884 @*/
2885 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2886 {
2887   const PetscInt      *arr0=NULL, *cone=NULL;
2888   PetscInt            *arr=NULL, *newarr=NULL;
2889   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
2890   IS                  *expandedPoints_;
2891   PetscSection        *sections_;
2892 
2893   PetscFunctionBegin;
2894   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2895   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2896   if (depth) PetscValidIntPointer(depth, 3);
2897   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2898   if (sections) PetscValidPointer(sections, 5);
2899   PetscCall(ISGetLocalSize(points, &n));
2900   PetscCall(ISGetIndices(points, &arr0));
2901   PetscCall(DMPlexGetDepth(dm, &depth_));
2902   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2903   PetscCall(PetscCalloc1(depth_, &sections_));
2904   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
2905   for (d=depth_-1; d>=0; d--) {
2906     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2907     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2908     for (i=0; i<n; i++) {
2909       PetscCall(DMPlexGetDepthStratum(dm, d+1, &start, &end));
2910       if (arr[i] >= start && arr[i] < end) {
2911         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2912         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2913       } else {
2914         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2915       }
2916     }
2917     PetscCall(PetscSectionSetUp(sections_[d]));
2918     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2919     PetscCall(PetscMalloc1(newn, &newarr));
2920     for (i=0; i<n; i++) {
2921       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2922       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2923       if (cn > 1) {
2924         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2925         PetscCall(PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt)));
2926       } else {
2927         newarr[co] = arr[i];
2928       }
2929     }
2930     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2931     arr = newarr;
2932     n = newn;
2933   }
2934   PetscCall(ISRestoreIndices(points, &arr0));
2935   *depth = depth_;
2936   if (expandedPoints) *expandedPoints = expandedPoints_;
2937   else {
2938     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2939     PetscCall(PetscFree(expandedPoints_));
2940   }
2941   if (sections) *sections = sections_;
2942   else {
2943     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2944     PetscCall(PetscFree(sections_));
2945   }
2946   PetscFunctionReturn(0);
2947 }
2948 
2949 /*@
2950   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2951 
2952   Not collective
2953 
2954   Input Parameters:
2955 + dm - The DMPlex
2956 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2957 
2958   Output Parameters:
2959 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2960 . expandedPoints - (optional) An array of recursively expanded cones
2961 - sections - (optional) An array of sections which describe mappings from points to their cone points
2962 
2963   Level: advanced
2964 
2965   Notes:
2966   See DMPlexGetConeRecursive() for details.
2967 
2968 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2969 @*/
2970 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2971 {
2972   PetscInt            d, depth_;
2973 
2974   PetscFunctionBegin;
2975   PetscCall(DMPlexGetDepth(dm, &depth_));
2976   PetscCheck(!depth || *depth == depth_,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2977   if (depth) *depth = 0;
2978   if (expandedPoints) {
2979     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2980     PetscCall(PetscFree(*expandedPoints));
2981   }
2982   if (sections)  {
2983     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2984     PetscCall(PetscFree(*sections));
2985   }
2986   PetscFunctionReturn(0);
2987 }
2988 
2989 /*@
2990   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
2991 
2992   Not collective
2993 
2994   Input Parameters:
2995 + mesh - The DMPlex
2996 . p - The point, which must lie in the chart set with DMPlexSetChart()
2997 - cone - An array of points which are on the in-edges for point p
2998 
2999   Output Parameter:
3000 
3001   Note:
3002   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3003 
3004   Level: beginner
3005 
3006 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3007 @*/
3008 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3009 {
3010   DM_Plex       *mesh = (DM_Plex*) dm->data;
3011   PetscInt       pStart, pEnd;
3012   PetscInt       dof, off, c;
3013 
3014   PetscFunctionBegin;
3015   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3016   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3017   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3018   if (dof) PetscValidIntPointer(cone, 3);
3019   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3020   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);
3021   for (c = 0; c < dof; ++c) {
3022     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);
3023     mesh->cones[off+c] = cone[c];
3024   }
3025   PetscFunctionReturn(0);
3026 }
3027 
3028 /*@C
3029   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3030 
3031   Not collective
3032 
3033   Input Parameters:
3034 + mesh - The DMPlex
3035 - p - The point, which must lie in the chart set with DMPlexSetChart()
3036 
3037   Output Parameter:
3038 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3039                     integer giving the prescription for cone traversal.
3040 
3041   Level: beginner
3042 
3043   Notes:
3044   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3045   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3046   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3047   with the identity.
3048 
3049   Fortran Notes:
3050   Since it returns an array, this routine is only available in Fortran 90, and you must
3051   include petsc.h90 in your code.
3052   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3053   DMPlexRestoreConeOrientation() is not needed/available in C.
3054 
3055 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3056 @*/
3057 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3058 {
3059   DM_Plex       *mesh = (DM_Plex*) dm->data;
3060   PetscInt       off;
3061 
3062   PetscFunctionBegin;
3063   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3064   if (PetscDefined(USE_DEBUG)) {
3065     PetscInt dof;
3066     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3067     if (dof) PetscValidPointer(coneOrientation, 3);
3068   }
3069   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3070 
3071   *coneOrientation = &mesh->coneOrientations[off];
3072   PetscFunctionReturn(0);
3073 }
3074 
3075 /*@
3076   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3077 
3078   Not collective
3079 
3080   Input Parameters:
3081 + mesh - The DMPlex
3082 . p - The point, which must lie in the chart set with DMPlexSetChart()
3083 - coneOrientation - An array of orientations
3084   Output Parameter:
3085 
3086   Notes:
3087   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3088 
3089   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3090 
3091   Level: beginner
3092 
3093 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3094 @*/
3095 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3096 {
3097   DM_Plex       *mesh = (DM_Plex*) dm->data;
3098   PetscInt       pStart, pEnd;
3099   PetscInt       dof, off, c;
3100 
3101   PetscFunctionBegin;
3102   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3103   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3104   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3105   if (dof) PetscValidIntPointer(coneOrientation, 3);
3106   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3107   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);
3108   for (c = 0; c < dof; ++c) {
3109     PetscInt cdof, o = coneOrientation[c];
3110 
3111     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof));
3112     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);
3113     mesh->coneOrientations[off+c] = o;
3114   }
3115   PetscFunctionReturn(0);
3116 }
3117 
3118 /*@
3119   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3120 
3121   Not collective
3122 
3123   Input Parameters:
3124 + mesh - The DMPlex
3125 . p - The point, which must lie in the chart set with DMPlexSetChart()
3126 . conePos - The local index in the cone where the point should be put
3127 - conePoint - The mesh point to insert
3128 
3129   Level: beginner
3130 
3131 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3132 @*/
3133 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3134 {
3135   DM_Plex       *mesh = (DM_Plex*) dm->data;
3136   PetscInt       pStart, pEnd;
3137   PetscInt       dof, off;
3138 
3139   PetscFunctionBegin;
3140   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3141   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3142   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);
3143   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);
3144   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3145   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3146   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);
3147   mesh->cones[off+conePos] = conePoint;
3148   PetscFunctionReturn(0);
3149 }
3150 
3151 /*@
3152   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3153 
3154   Not collective
3155 
3156   Input Parameters:
3157 + mesh - The DMPlex
3158 . p - The point, which must lie in the chart set with DMPlexSetChart()
3159 . conePos - The local index in the cone where the point should be put
3160 - coneOrientation - The point orientation to insert
3161 
3162   Level: beginner
3163 
3164   Notes:
3165   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3166 
3167 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3168 @*/
3169 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3170 {
3171   DM_Plex       *mesh = (DM_Plex*) dm->data;
3172   PetscInt       pStart, pEnd;
3173   PetscInt       dof, off;
3174 
3175   PetscFunctionBegin;
3176   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3177   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3178   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);
3179   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3180   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3181   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);
3182   mesh->coneOrientations[off+conePos] = coneOrientation;
3183   PetscFunctionReturn(0);
3184 }
3185 
3186 /*@
3187   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3188 
3189   Not collective
3190 
3191   Input Parameters:
3192 + mesh - The DMPlex
3193 - p - The point, which must lie in the chart set with DMPlexSetChart()
3194 
3195   Output Parameter:
3196 . size - The support size for point p
3197 
3198   Level: beginner
3199 
3200 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3201 @*/
3202 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3203 {
3204   DM_Plex       *mesh = (DM_Plex*) dm->data;
3205 
3206   PetscFunctionBegin;
3207   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3208   PetscValidIntPointer(size, 3);
3209   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3210   PetscFunctionReturn(0);
3211 }
3212 
3213 /*@
3214   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3215 
3216   Not collective
3217 
3218   Input Parameters:
3219 + mesh - The DMPlex
3220 . p - The point, which must lie in the chart set with DMPlexSetChart()
3221 - size - The support size for point p
3222 
3223   Output Parameter:
3224 
3225   Note:
3226   This should be called after DMPlexSetChart().
3227 
3228   Level: beginner
3229 
3230 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3231 @*/
3232 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3233 {
3234   DM_Plex       *mesh = (DM_Plex*) dm->data;
3235 
3236   PetscFunctionBegin;
3237   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3238   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3239   PetscFunctionReturn(0);
3240 }
3241 
3242 /*@C
3243   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3244 
3245   Not collective
3246 
3247   Input Parameters:
3248 + mesh - The DMPlex
3249 - p - The point, which must lie in the chart set with DMPlexSetChart()
3250 
3251   Output Parameter:
3252 . support - An array of points which are on the out-edges for point p
3253 
3254   Level: beginner
3255 
3256   Fortran Notes:
3257   Since it returns an array, this routine is only available in Fortran 90, and you must
3258   include petsc.h90 in your code.
3259   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3260   DMPlexRestoreSupport() is not needed/available in C.
3261 
3262 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3263 @*/
3264 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3265 {
3266   DM_Plex       *mesh = (DM_Plex*) dm->data;
3267   PetscInt       off;
3268 
3269   PetscFunctionBegin;
3270   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3271   PetscValidPointer(support, 3);
3272   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3273   *support = &mesh->supports[off];
3274   PetscFunctionReturn(0);
3275 }
3276 
3277 /*@
3278   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3279 
3280   Not collective
3281 
3282   Input Parameters:
3283 + mesh - The DMPlex
3284 . p - The point, which must lie in the chart set with DMPlexSetChart()
3285 - support - An array of points which are on the out-edges for point p
3286 
3287   Output Parameter:
3288 
3289   Note:
3290   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3291 
3292   Level: beginner
3293 
3294 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3295 @*/
3296 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3297 {
3298   DM_Plex       *mesh = (DM_Plex*) dm->data;
3299   PetscInt       pStart, pEnd;
3300   PetscInt       dof, off, c;
3301 
3302   PetscFunctionBegin;
3303   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3304   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3305   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3306   if (dof) PetscValidIntPointer(support, 3);
3307   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3308   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);
3309   for (c = 0; c < dof; ++c) {
3310     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);
3311     mesh->supports[off+c] = support[c];
3312   }
3313   PetscFunctionReturn(0);
3314 }
3315 
3316 /*@
3317   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3318 
3319   Not collective
3320 
3321   Input Parameters:
3322 + mesh - The DMPlex
3323 . p - The point, which must lie in the chart set with DMPlexSetChart()
3324 . supportPos - The local index in the cone where the point should be put
3325 - supportPoint - The mesh point to insert
3326 
3327   Level: beginner
3328 
3329 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3330 @*/
3331 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3332 {
3333   DM_Plex       *mesh = (DM_Plex*) dm->data;
3334   PetscInt       pStart, pEnd;
3335   PetscInt       dof, off;
3336 
3337   PetscFunctionBegin;
3338   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3339   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3340   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3341   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3342   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);
3343   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);
3344   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);
3345   mesh->supports[off+supportPos] = supportPoint;
3346   PetscFunctionReturn(0);
3347 }
3348 
3349 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3350 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3351 {
3352   switch (ct) {
3353     case DM_POLYTOPE_SEGMENT:
3354       if (o == -1) return -2;
3355       break;
3356     case DM_POLYTOPE_TRIANGLE:
3357       if (o == -3) return -1;
3358       if (o == -2) return -3;
3359       if (o == -1) return -2;
3360       break;
3361     case DM_POLYTOPE_QUADRILATERAL:
3362       if (o == -4) return -2;
3363       if (o == -3) return -1;
3364       if (o == -2) return -4;
3365       if (o == -1) return -3;
3366       break;
3367     default: return o;
3368   }
3369   return o;
3370 }
3371 
3372 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3373 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3374 {
3375   switch (ct) {
3376     case DM_POLYTOPE_SEGMENT:
3377       if ((o == -2) || (o == 1)) return -1;
3378       if (o == -1) return 0;
3379       break;
3380     case DM_POLYTOPE_TRIANGLE:
3381       if (o == -3) return -2;
3382       if (o == -2) return -1;
3383       if (o == -1) return -3;
3384       break;
3385     case DM_POLYTOPE_QUADRILATERAL:
3386       if (o == -4) return -2;
3387       if (o == -3) return -1;
3388       if (o == -2) return -4;
3389       if (o == -1) return -3;
3390       break;
3391     default: return o;
3392   }
3393   return o;
3394 }
3395 
3396 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3397 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3398 {
3399   PetscInt       pStart, pEnd, p;
3400 
3401   PetscFunctionBegin;
3402   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3403   for (p = pStart; p < pEnd; ++p) {
3404     const PetscInt *cone, *ornt;
3405     PetscInt        coneSize, c;
3406 
3407     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3408     PetscCall(DMPlexGetCone(dm, p, &cone));
3409     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3410     for (c = 0; c < coneSize; ++c) {
3411       DMPolytopeType ct;
3412       const PetscInt o = ornt[c];
3413 
3414       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3415       switch (ct) {
3416         case DM_POLYTOPE_SEGMENT:
3417           if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3418           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3419           break;
3420         case DM_POLYTOPE_TRIANGLE:
3421           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3422           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3423           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3424           break;
3425         case DM_POLYTOPE_QUADRILATERAL:
3426           if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3427           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3428           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3429           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3430           break;
3431         default: break;
3432       }
3433     }
3434   }
3435   PetscFunctionReturn(0);
3436 }
3437 
3438 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3439 {
3440   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3441   PetscInt       *closure;
3442   const PetscInt *tmp = NULL, *tmpO = NULL;
3443   PetscInt        off = 0, tmpSize, t;
3444 
3445   PetscFunctionBeginHot;
3446   if (ornt) {
3447     PetscCall(DMPlexGetCellType(dm, p, &ct));
3448     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3449   }
3450   if (*points) {
3451     closure = *points;
3452   } else {
3453     PetscInt maxConeSize, maxSupportSize;
3454     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3455     PetscCall(DMGetWorkArray(dm, 2*(PetscMax(maxConeSize, maxSupportSize)+1), MPIU_INT, &closure));
3456   }
3457   if (useCone) {
3458     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3459     PetscCall(DMPlexGetCone(dm, p, &tmp));
3460     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3461   } else {
3462     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3463     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3464   }
3465   if (ct == DM_POLYTOPE_UNKNOWN) {
3466     closure[off++] = p;
3467     closure[off++] = 0;
3468     for (t = 0; t < tmpSize; ++t) {
3469       closure[off++] = tmp[t];
3470       closure[off++] = tmpO ? tmpO[t] : 0;
3471     }
3472   } else {
3473     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3474 
3475     /* We assume that cells with a valid type have faces with a valid type */
3476     closure[off++] = p;
3477     closure[off++] = ornt;
3478     for (t = 0; t < tmpSize; ++t) {
3479       DMPolytopeType ft;
3480 
3481       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3482       closure[off++] = tmp[arr[t]];
3483       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3484     }
3485   }
3486   if (numPoints) *numPoints = tmpSize+1;
3487   if (points)    *points    = closure;
3488   PetscFunctionReturn(0);
3489 }
3490 
3491 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3492 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3493 {
3494   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3495   const PetscInt *cone, *ornt;
3496   PetscInt       *pts,  *closure = NULL;
3497   DMPolytopeType  ft;
3498   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3499   PetscInt        dim, coneSize, c, d, clSize, cl;
3500 
3501   PetscFunctionBeginHot;
3502   PetscCall(DMGetDimension(dm, &dim));
3503   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3504   PetscCall(DMPlexGetCone(dm, point, &cone));
3505   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3506   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3507   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    dim+1)-1)/(maxConeSize-1))    : dim+1;
3508   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim+1)-1)/(maxSupportSize-1)) : dim+1;
3509   maxSize       = PetscMax(coneSeries, supportSeries);
3510   if (*points) {pts  = *points;}
3511   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &pts));
3512   c    = 0;
3513   pts[c++] = point;
3514   pts[c++] = o;
3515   PetscCall(DMPlexGetCellType(dm, cone[arr[0*2+0]], &ft));
3516   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[0*2+1], ornt[0]), useCone, &clSize, &closure));
3517   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3518   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[1*2+1], ornt[1]), useCone, &clSize, &closure));
3519   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3520   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3521   for (d = 2; d < coneSize; ++d) {
3522     PetscCall(DMPlexGetCellType(dm, cone[arr[d*2+0]], &ft));
3523     pts[c++] = cone[arr[d*2+0]];
3524     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]);
3525   }
3526   if (dim >= 3) {
3527     for (d = 2; d < coneSize; ++d) {
3528       const PetscInt  fpoint = cone[arr[d*2+0]];
3529       const PetscInt *fcone, *fornt;
3530       PetscInt        fconeSize, fc, i;
3531 
3532       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3533       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]));
3534       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3535       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3536       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3537       for (fc = 0; fc < fconeSize; ++fc) {
3538         const PetscInt cp = fcone[farr[fc*2+0]];
3539         const PetscInt co = farr[fc*2+1];
3540 
3541         for (i = 0; i < c; i += 2) if (pts[i] == cp) break;
3542         if (i == c) {
3543           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3544           pts[c++] = cp;
3545           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc*2+0]]);
3546         }
3547       }
3548     }
3549   }
3550   *numPoints = c/2;
3551   *points    = pts;
3552   PetscFunctionReturn(0);
3553 }
3554 
3555 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3556 {
3557   DMPolytopeType ct;
3558   PetscInt      *closure, *fifo;
3559   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3560   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3561   PetscInt       depth, maxSize;
3562 
3563   PetscFunctionBeginHot;
3564   PetscCall(DMPlexGetDepth(dm, &depth));
3565   if (depth == 1) {
3566     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3567     PetscFunctionReturn(0);
3568   }
3569   PetscCall(DMPlexGetCellType(dm, p, &ct));
3570   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3571   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3572     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3573     PetscFunctionReturn(0);
3574   }
3575   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3576   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    depth+1)-1)/(maxConeSize-1))    : depth+1;
3577   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth+1)-1)/(maxSupportSize-1)) : depth+1;
3578   maxSize       = PetscMax(coneSeries, supportSeries);
3579   PetscCall(DMGetWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3580   if (*points) {closure = *points;}
3581   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &closure));
3582   closure[closureSize++] = p;
3583   closure[closureSize++] = ornt;
3584   fifo[fifoSize++]       = p;
3585   fifo[fifoSize++]       = ornt;
3586   fifo[fifoSize++]       = ct;
3587   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3588   while (fifoSize - fifoStart) {
3589     const PetscInt       q    = fifo[fifoStart++];
3590     const PetscInt       o    = fifo[fifoStart++];
3591     const DMPolytopeType qt   = (DMPolytopeType) fifo[fifoStart++];
3592     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3593     const PetscInt      *tmp, *tmpO;
3594     PetscInt             tmpSize, t;
3595 
3596     if (PetscDefined(USE_DEBUG)) {
3597       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt)/2;
3598       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);
3599     }
3600     if (useCone) {
3601       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3602       PetscCall(DMPlexGetCone(dm, q, &tmp));
3603       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3604     } else {
3605       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3606       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3607       tmpO = NULL;
3608     }
3609     for (t = 0; t < tmpSize; ++t) {
3610       const PetscInt ip = useCone && qarr ? qarr[t*2]   : t;
3611       const PetscInt io = useCone && qarr ? qarr[t*2+1] : 0;
3612       const PetscInt cp = tmp[ip];
3613       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3614       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3615       PetscInt       c;
3616 
3617       /* Check for duplicate */
3618       for (c = 0; c < closureSize; c += 2) {
3619         if (closure[c] == cp) break;
3620       }
3621       if (c == closureSize) {
3622         closure[closureSize++] = cp;
3623         closure[closureSize++] = co;
3624         fifo[fifoSize++]       = cp;
3625         fifo[fifoSize++]       = co;
3626         fifo[fifoSize++]       = ct;
3627       }
3628     }
3629   }
3630   PetscCall(DMRestoreWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3631   if (numPoints) *numPoints = closureSize/2;
3632   if (points)    *points    = closure;
3633   PetscFunctionReturn(0);
3634 }
3635 
3636 /*@C
3637   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3638 
3639   Not collective
3640 
3641   Input Parameters:
3642 + dm      - The DMPlex
3643 . p       - The mesh point
3644 - useCone - PETSC_TRUE for the closure, otherwise return the star
3645 
3646   Input/Output Parameter:
3647 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3648            if NULL on input, internal storage will be returned, otherwise the provided array is used
3649 
3650   Output Parameter:
3651 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3652 
3653   Note:
3654   If using internal storage (points is NULL on input), each call overwrites the last output.
3655 
3656   Fortran Notes:
3657   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3658 
3659   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3660 
3661   Level: beginner
3662 
3663 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3664 @*/
3665 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3666 {
3667   PetscFunctionBeginHot;
3668   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3669   if (numPoints) PetscValidIntPointer(numPoints, 4);
3670   if (points)    PetscValidPointer(points, 5);
3671   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3672   PetscFunctionReturn(0);
3673 }
3674 
3675 /*@C
3676   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3677 
3678   Not collective
3679 
3680   Input Parameters:
3681 + dm        - The DMPlex
3682 . p         - The mesh point
3683 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3684 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3685 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3686 
3687   Note:
3688   If not using internal storage (points is not NULL on input), this call is unnecessary
3689 
3690   Fortran Notes:
3691   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3692 
3693   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3694 
3695   Level: beginner
3696 
3697 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3698 @*/
3699 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3700 {
3701   PetscFunctionBeginHot;
3702   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3703   if (numPoints) *numPoints = 0;
3704   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3705   PetscFunctionReturn(0);
3706 }
3707 
3708 /*@
3709   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3710 
3711   Not collective
3712 
3713   Input Parameter:
3714 . mesh - The DMPlex
3715 
3716   Output Parameters:
3717 + maxConeSize - The maximum number of in-edges
3718 - maxSupportSize - The maximum number of out-edges
3719 
3720   Level: beginner
3721 
3722 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3723 @*/
3724 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3725 {
3726   DM_Plex *mesh = (DM_Plex*) dm->data;
3727 
3728   PetscFunctionBegin;
3729   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3730   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3731   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3732   PetscFunctionReturn(0);
3733 }
3734 
3735 PetscErrorCode DMSetUp_Plex(DM dm)
3736 {
3737   DM_Plex       *mesh = (DM_Plex*) dm->data;
3738   PetscInt       size, maxSupportSize;
3739 
3740   PetscFunctionBegin;
3741   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3742   PetscCall(PetscSectionSetUp(mesh->coneSection));
3743   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3744   PetscCall(PetscMalloc1(size, &mesh->cones));
3745   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3746   PetscCall(PetscLogObjectMemory((PetscObject) dm, size*2*sizeof(PetscInt)));
3747   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3748   if (maxSupportSize) {
3749     PetscCall(PetscSectionSetUp(mesh->supportSection));
3750     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3751     PetscCall(PetscMalloc1(size, &mesh->supports));
3752     PetscCall(PetscLogObjectMemory((PetscObject) dm, size*sizeof(PetscInt)));
3753   }
3754   PetscFunctionReturn(0);
3755 }
3756 
3757 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3758 {
3759   PetscFunctionBegin;
3760   if (subdm) PetscCall(DMClone(dm, subdm));
3761   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3762   if (subdm) {(*subdm)->useNatural = dm->useNatural;}
3763   if (dm->useNatural && dm->sfMigration) {
3764     PetscSF        sfMigrationInv,sfNatural;
3765     PetscSection   section, sectionSeq;
3766 
3767     (*subdm)->sfMigration = dm->sfMigration;
3768     PetscCall(PetscObjectReference((PetscObject) dm->sfMigration));
3769     PetscCall(DMGetLocalSection((*subdm), &section));
3770     PetscCall(PetscSFCreateInverseSF((*subdm)->sfMigration, &sfMigrationInv));
3771     PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*subdm)), &sectionSeq));
3772     PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3773 
3774     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, sectionSeq, (*subdm)->sfMigration, &sfNatural));
3775     (*subdm)->sfNatural = sfNatural;
3776     PetscCall(PetscSectionDestroy(&sectionSeq));
3777     PetscCall(PetscSFDestroy(&sfMigrationInv));
3778   }
3779   PetscFunctionReturn(0);
3780 }
3781 
3782 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3783 {
3784   PetscInt       i = 0;
3785 
3786   PetscFunctionBegin;
3787   PetscCall(DMClone(dms[0], superdm));
3788   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3789   (*superdm)->useNatural = PETSC_FALSE;
3790   for (i = 0; i < len; i++) {
3791     if (dms[i]->useNatural && dms[i]->sfMigration) {
3792       PetscSF        sfMigrationInv,sfNatural;
3793       PetscSection   section, sectionSeq;
3794 
3795       (*superdm)->sfMigration = dms[i]->sfMigration;
3796       PetscCall(PetscObjectReference((PetscObject) dms[i]->sfMigration));
3797       (*superdm)->useNatural = PETSC_TRUE;
3798       PetscCall(DMGetLocalSection((*superdm), &section));
3799       PetscCall(PetscSFCreateInverseSF((*superdm)->sfMigration, &sfMigrationInv));
3800       PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*superdm)), &sectionSeq));
3801       PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3802 
3803       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, sectionSeq, (*superdm)->sfMigration, &sfNatural));
3804       (*superdm)->sfNatural = sfNatural;
3805       PetscCall(PetscSectionDestroy(&sectionSeq));
3806       PetscCall(PetscSFDestroy(&sfMigrationInv));
3807       break;
3808     }
3809   }
3810   PetscFunctionReturn(0);
3811 }
3812 
3813 /*@
3814   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3815 
3816   Not collective
3817 
3818   Input Parameter:
3819 . mesh - The DMPlex
3820 
3821   Output Parameter:
3822 
3823   Note:
3824   This should be called after all calls to DMPlexSetCone()
3825 
3826   Level: beginner
3827 
3828 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3829 @*/
3830 PetscErrorCode DMPlexSymmetrize(DM dm)
3831 {
3832   DM_Plex       *mesh = (DM_Plex*) dm->data;
3833   PetscInt      *offsets;
3834   PetscInt       supportSize;
3835   PetscInt       pStart, pEnd, p;
3836 
3837   PetscFunctionBegin;
3838   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3839   PetscCheck(!mesh->supports,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3840   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0));
3841   /* Calculate support sizes */
3842   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3843   for (p = pStart; p < pEnd; ++p) {
3844     PetscInt dof, off, c;
3845 
3846     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3847     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3848     for (c = off; c < off+dof; ++c) {
3849       PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3850     }
3851   }
3852   PetscCall(PetscSectionSetUp(mesh->supportSection));
3853   /* Calculate supports */
3854   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3855   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3856   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3857   for (p = pStart; p < pEnd; ++p) {
3858     PetscInt dof, off, c;
3859 
3860     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3861     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3862     for (c = off; c < off+dof; ++c) {
3863       const PetscInt q = mesh->cones[c];
3864       PetscInt       offS;
3865 
3866       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3867 
3868       mesh->supports[offS+offsets[q]] = p;
3869       ++offsets[q];
3870     }
3871   }
3872   PetscCall(PetscFree(offsets));
3873   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0));
3874   PetscFunctionReturn(0);
3875 }
3876 
3877 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3878 {
3879   IS             stratumIS;
3880 
3881   PetscFunctionBegin;
3882   if (pStart >= pEnd) PetscFunctionReturn(0);
3883   if (PetscDefined(USE_DEBUG)) {
3884     PetscInt  qStart, qEnd, numLevels, level;
3885     PetscBool overlap = PETSC_FALSE;
3886     PetscCall(DMLabelGetNumValues(label, &numLevels));
3887     for (level = 0; level < numLevels; level++) {
3888       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3889       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
3890     }
3891     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);
3892   }
3893   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS));
3894   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3895   PetscCall(ISDestroy(&stratumIS));
3896   PetscFunctionReturn(0);
3897 }
3898 
3899 /*@
3900   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3901   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3902   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3903   the DAG.
3904 
3905   Collective on dm
3906 
3907   Input Parameter:
3908 . mesh - The DMPlex
3909 
3910   Output Parameter:
3911 
3912   Notes:
3913   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3914   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3915   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3916   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3917   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3918 
3919   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3920   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3921   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
3922   to interpolate only that one (e0), so that
3923 $  cone(c0) = {e0, v2}
3924 $  cone(e0) = {v0, v1}
3925   If DMPlexStratify() is run on this mesh, it will give depths
3926 $  depth 0 = {v0, v1, v2}
3927 $  depth 1 = {e0, c0}
3928   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3929 
3930   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3931 
3932   Level: beginner
3933 
3934 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3935 @*/
3936 PetscErrorCode DMPlexStratify(DM dm)
3937 {
3938   DM_Plex       *mesh = (DM_Plex*) dm->data;
3939   DMLabel        label;
3940   PetscInt       pStart, pEnd, p;
3941   PetscInt       numRoots = 0, numLeaves = 0;
3942 
3943   PetscFunctionBegin;
3944   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3945   PetscCall(PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0));
3946 
3947   /* Create depth label */
3948   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3949   PetscCall(DMCreateLabel(dm, "depth"));
3950   PetscCall(DMPlexGetDepthLabel(dm, &label));
3951 
3952   {
3953     /* Initialize roots and count leaves */
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 (!coneSize && supportSize) {
3962         sMin = PetscMin(p, sMin);
3963         sMax = PetscMax(p, sMax);
3964         ++numRoots;
3965       } else if (!supportSize && coneSize) {
3966         ++numLeaves;
3967       } else if (!supportSize && !coneSize) {
3968         /* Isolated points */
3969         sMin = PetscMin(p, sMin);
3970         sMax = PetscMax(p, sMax);
3971       }
3972     }
3973     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1));
3974   }
3975 
3976   if (numRoots + numLeaves == (pEnd - pStart)) {
3977     PetscInt sMin = PETSC_MAX_INT;
3978     PetscInt sMax = PETSC_MIN_INT;
3979     PetscInt coneSize, supportSize;
3980 
3981     for (p = pStart; p < pEnd; ++p) {
3982       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3983       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3984       if (!supportSize && coneSize) {
3985         sMin = PetscMin(p, sMin);
3986         sMax = PetscMax(p, sMax);
3987       }
3988     }
3989     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1));
3990   } else {
3991     PetscInt level = 0;
3992     PetscInt qStart, qEnd, q;
3993 
3994     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3995     while (qEnd > qStart) {
3996       PetscInt sMin = PETSC_MAX_INT;
3997       PetscInt sMax = PETSC_MIN_INT;
3998 
3999       for (q = qStart; q < qEnd; ++q) {
4000         const PetscInt *support;
4001         PetscInt        supportSize, s;
4002 
4003         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4004         PetscCall(DMPlexGetSupport(dm, q, &support));
4005         for (s = 0; s < supportSize; ++s) {
4006           sMin = PetscMin(support[s], sMin);
4007           sMax = PetscMax(support[s], sMax);
4008         }
4009       }
4010       PetscCall(DMLabelGetNumValues(label, &level));
4011       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1));
4012       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4013     }
4014   }
4015   { /* just in case there is an empty process */
4016     PetscInt numValues, maxValues = 0, v;
4017 
4018     PetscCall(DMLabelGetNumValues(label, &numValues));
4019     PetscCallMPI(MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm)));
4020     for (v = numValues; v < maxValues; v++) {
4021       PetscCall(DMLabelAddStratum(label, v));
4022     }
4023   }
4024   PetscCall(PetscObjectStateGet((PetscObject) label, &mesh->depthState));
4025   PetscCall(PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0));
4026   PetscFunctionReturn(0);
4027 }
4028 
4029 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4030 {
4031   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4032   PetscInt       dim, depth, pheight, coneSize;
4033 
4034   PetscFunctionBeginHot;
4035   PetscCall(DMGetDimension(dm, &dim));
4036   PetscCall(DMPlexGetDepth(dm, &depth));
4037   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4038   pheight = depth - pdepth;
4039   if (depth <= 1) {
4040     switch (pdepth) {
4041       case 0: ct = DM_POLYTOPE_POINT;break;
4042       case 1:
4043         switch (coneSize) {
4044           case 2: ct = DM_POLYTOPE_SEGMENT;break;
4045           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4046           case 4:
4047           switch (dim) {
4048             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
4049             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
4050             default: break;
4051           }
4052           break;
4053         case 5: ct = DM_POLYTOPE_PYRAMID;break;
4054         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4055         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
4056         default: break;
4057       }
4058     }
4059   } else {
4060     if (pdepth == 0) {
4061       ct = DM_POLYTOPE_POINT;
4062     } else if (pheight == 0) {
4063       switch (dim) {
4064         case 1:
4065           switch (coneSize) {
4066             case 2: ct = DM_POLYTOPE_SEGMENT;break;
4067             default: break;
4068           }
4069           break;
4070         case 2:
4071           switch (coneSize) {
4072             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4073             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4074             default: break;
4075           }
4076           break;
4077         case 3:
4078           switch (coneSize) {
4079             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
4080             case 5:
4081             {
4082               const PetscInt *cone;
4083               PetscInt        faceConeSize;
4084 
4085               PetscCall(DMPlexGetCone(dm, p, &cone));
4086               PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4087               switch (faceConeSize) {
4088                 case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4089                 case 4: ct = DM_POLYTOPE_PYRAMID;break;
4090               }
4091             }
4092             break;
4093             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
4094             default: break;
4095           }
4096           break;
4097         default: break;
4098       }
4099     } else if (pheight > 0) {
4100       switch (coneSize) {
4101         case 2: ct = DM_POLYTOPE_SEGMENT;break;
4102         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4103         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4104         default: break;
4105       }
4106     }
4107   }
4108   *pt = ct;
4109   PetscFunctionReturn(0);
4110 }
4111 
4112 /*@
4113   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4114 
4115   Collective on dm
4116 
4117   Input Parameter:
4118 . mesh - The DMPlex
4119 
4120   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4121 
4122   Level: developer
4123 
4124   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4125   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4126   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4127 
4128 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4129 @*/
4130 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4131 {
4132   DM_Plex       *mesh;
4133   DMLabel        ctLabel;
4134   PetscInt       pStart, pEnd, p;
4135 
4136   PetscFunctionBegin;
4137   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4138   mesh = (DM_Plex *) dm->data;
4139   PetscCall(DMCreateLabel(dm, "celltype"));
4140   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4141   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4142   for (p = pStart; p < pEnd; ++p) {
4143     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4144     PetscInt       pdepth;
4145 
4146     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4147     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4148     PetscCheck(ct != DM_POLYTOPE_UNKNOWN,PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4149     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4150   }
4151   PetscCall(PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState));
4152   PetscCall(PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view"));
4153   PetscFunctionReturn(0);
4154 }
4155 
4156 /*@C
4157   DMPlexGetJoin - Get an array for the join of the set of points
4158 
4159   Not Collective
4160 
4161   Input Parameters:
4162 + dm - The DMPlex object
4163 . numPoints - The number of input points for the join
4164 - points - The input points
4165 
4166   Output Parameters:
4167 + numCoveredPoints - The number of points in the join
4168 - coveredPoints - The points in the join
4169 
4170   Level: intermediate
4171 
4172   Note: Currently, this is restricted to a single level join
4173 
4174   Fortran Notes:
4175   Since it returns an array, this routine is only available in Fortran 90, and you must
4176   include petsc.h90 in your code.
4177 
4178   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4179 
4180 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4181 @*/
4182 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4183 {
4184   DM_Plex       *mesh = (DM_Plex*) dm->data;
4185   PetscInt      *join[2];
4186   PetscInt       joinSize, i = 0;
4187   PetscInt       dof, off, p, c, m;
4188   PetscInt       maxSupportSize;
4189 
4190   PetscFunctionBegin;
4191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4192   PetscValidIntPointer(points, 3);
4193   PetscValidIntPointer(numCoveredPoints, 4);
4194   PetscValidPointer(coveredPoints, 5);
4195   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4196   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4197   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4198   /* Copy in support of first point */
4199   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4200   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4201   for (joinSize = 0; joinSize < dof; ++joinSize) {
4202     join[i][joinSize] = mesh->supports[off+joinSize];
4203   }
4204   /* Check each successive support */
4205   for (p = 1; p < numPoints; ++p) {
4206     PetscInt newJoinSize = 0;
4207 
4208     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4209     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4210     for (c = 0; c < dof; ++c) {
4211       const PetscInt point = mesh->supports[off+c];
4212 
4213       for (m = 0; m < joinSize; ++m) {
4214         if (point == join[i][m]) {
4215           join[1-i][newJoinSize++] = point;
4216           break;
4217         }
4218       }
4219     }
4220     joinSize = newJoinSize;
4221     i        = 1-i;
4222   }
4223   *numCoveredPoints = joinSize;
4224   *coveredPoints    = join[i];
4225   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1-i]));
4226   PetscFunctionReturn(0);
4227 }
4228 
4229 /*@C
4230   DMPlexRestoreJoin - Restore an array for the join of the set of points
4231 
4232   Not Collective
4233 
4234   Input Parameters:
4235 + dm - The DMPlex object
4236 . numPoints - The number of input points for the join
4237 - points - The input points
4238 
4239   Output Parameters:
4240 + numCoveredPoints - The number of points in the join
4241 - coveredPoints - The points in the join
4242 
4243   Fortran Notes:
4244   Since it returns an array, this routine is only available in Fortran 90, and you must
4245   include petsc.h90 in your code.
4246 
4247   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4248 
4249   Level: intermediate
4250 
4251 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4252 @*/
4253 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4254 {
4255   PetscFunctionBegin;
4256   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4257   if (points) PetscValidIntPointer(points,3);
4258   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4259   PetscValidPointer(coveredPoints, 5);
4260   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4261   if (numCoveredPoints) *numCoveredPoints = 0;
4262   PetscFunctionReturn(0);
4263 }
4264 
4265 /*@C
4266   DMPlexGetFullJoin - Get an array for the join of the set of points
4267 
4268   Not Collective
4269 
4270   Input Parameters:
4271 + dm - The DMPlex object
4272 . numPoints - The number of input points for the join
4273 - points - The input points
4274 
4275   Output Parameters:
4276 + numCoveredPoints - The number of points in the join
4277 - coveredPoints - The points in the join
4278 
4279   Fortran Notes:
4280   Since it returns an array, this routine is only available in Fortran 90, and you must
4281   include petsc.h90 in your code.
4282 
4283   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4284 
4285   Level: intermediate
4286 
4287 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4288 @*/
4289 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4290 {
4291   PetscInt      *offsets, **closures;
4292   PetscInt      *join[2];
4293   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
4294   PetscInt       p, d, c, m, ms;
4295 
4296   PetscFunctionBegin;
4297   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4298   PetscValidIntPointer(points, 3);
4299   PetscValidIntPointer(numCoveredPoints, 4);
4300   PetscValidPointer(coveredPoints, 5);
4301 
4302   PetscCall(DMPlexGetDepth(dm, &depth));
4303   PetscCall(PetscCalloc1(numPoints, &closures));
4304   PetscCall(DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4305   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4306   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
4307   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4308   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4309 
4310   for (p = 0; p < numPoints; ++p) {
4311     PetscInt closureSize;
4312 
4313     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4314 
4315     offsets[p*(depth+2)+0] = 0;
4316     for (d = 0; d < depth+1; ++d) {
4317       PetscInt pStart, pEnd, i;
4318 
4319       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4320       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
4321         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4322           offsets[p*(depth+2)+d+1] = i;
4323           break;
4324         }
4325       }
4326       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
4327     }
4328     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);
4329   }
4330   for (d = 0; d < depth+1; ++d) {
4331     PetscInt dof;
4332 
4333     /* Copy in support of first point */
4334     dof = offsets[d+1] - offsets[d];
4335     for (joinSize = 0; joinSize < dof; ++joinSize) {
4336       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
4337     }
4338     /* Check each successive cone */
4339     for (p = 1; p < numPoints && joinSize; ++p) {
4340       PetscInt newJoinSize = 0;
4341 
4342       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
4343       for (c = 0; c < dof; ++c) {
4344         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
4345 
4346         for (m = 0; m < joinSize; ++m) {
4347           if (point == join[i][m]) {
4348             join[1-i][newJoinSize++] = point;
4349             break;
4350           }
4351         }
4352       }
4353       joinSize = newJoinSize;
4354       i        = 1-i;
4355     }
4356     if (joinSize) break;
4357   }
4358   *numCoveredPoints = joinSize;
4359   *coveredPoints    = join[i];
4360   for (p = 0; p < numPoints; ++p) {
4361     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4362   }
4363   PetscCall(PetscFree(closures));
4364   PetscCall(DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4365   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1-i]));
4366   PetscFunctionReturn(0);
4367 }
4368 
4369 /*@C
4370   DMPlexGetMeet - Get an array for the meet of the set of points
4371 
4372   Not Collective
4373 
4374   Input Parameters:
4375 + dm - The DMPlex object
4376 . numPoints - The number of input points for the meet
4377 - points - The input points
4378 
4379   Output Parameters:
4380 + numCoveredPoints - The number of points in the meet
4381 - coveredPoints - The points in the meet
4382 
4383   Level: intermediate
4384 
4385   Note: Currently, this is restricted to a single level meet
4386 
4387   Fortran Notes:
4388   Since it returns an array, this routine is only available in Fortran 90, and you must
4389   include petsc.h90 in your code.
4390 
4391   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4392 
4393 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4394 @*/
4395 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4396 {
4397   DM_Plex       *mesh = (DM_Plex*) dm->data;
4398   PetscInt      *meet[2];
4399   PetscInt       meetSize, i = 0;
4400   PetscInt       dof, off, p, c, m;
4401   PetscInt       maxConeSize;
4402 
4403   PetscFunctionBegin;
4404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4405   PetscValidIntPointer(points, 3);
4406   PetscValidIntPointer(numCoveringPoints, 4);
4407   PetscValidPointer(coveringPoints, 5);
4408   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4409   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4410   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4411   /* Copy in cone of first point */
4412   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4413   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4414   for (meetSize = 0; meetSize < dof; ++meetSize) {
4415     meet[i][meetSize] = mesh->cones[off+meetSize];
4416   }
4417   /* Check each successive cone */
4418   for (p = 1; p < numPoints; ++p) {
4419     PetscInt newMeetSize = 0;
4420 
4421     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4422     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4423     for (c = 0; c < dof; ++c) {
4424       const PetscInt point = mesh->cones[off+c];
4425 
4426       for (m = 0; m < meetSize; ++m) {
4427         if (point == meet[i][m]) {
4428           meet[1-i][newMeetSize++] = point;
4429           break;
4430         }
4431       }
4432     }
4433     meetSize = newMeetSize;
4434     i        = 1-i;
4435   }
4436   *numCoveringPoints = meetSize;
4437   *coveringPoints    = meet[i];
4438   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1-i]));
4439   PetscFunctionReturn(0);
4440 }
4441 
4442 /*@C
4443   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4444 
4445   Not Collective
4446 
4447   Input Parameters:
4448 + dm - The DMPlex object
4449 . numPoints - The number of input points for the meet
4450 - points - The input points
4451 
4452   Output Parameters:
4453 + numCoveredPoints - The number of points in the meet
4454 - coveredPoints - The points in the meet
4455 
4456   Level: intermediate
4457 
4458   Fortran Notes:
4459   Since it returns an array, this routine is only available in Fortran 90, and you must
4460   include petsc.h90 in your code.
4461 
4462   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4463 
4464 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4465 @*/
4466 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4467 {
4468   PetscFunctionBegin;
4469   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4470   if (points) PetscValidIntPointer(points,3);
4471   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4472   PetscValidPointer(coveredPoints,5);
4473   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4474   if (numCoveredPoints) *numCoveredPoints = 0;
4475   PetscFunctionReturn(0);
4476 }
4477 
4478 /*@C
4479   DMPlexGetFullMeet - Get an array for the meet of the set of points
4480 
4481   Not Collective
4482 
4483   Input Parameters:
4484 + dm - The DMPlex object
4485 . numPoints - The number of input points for the meet
4486 - points - The input points
4487 
4488   Output Parameters:
4489 + numCoveredPoints - The number of points in the meet
4490 - coveredPoints - The points in the meet
4491 
4492   Level: intermediate
4493 
4494   Fortran Notes:
4495   Since it returns an array, this routine is only available in Fortran 90, and you must
4496   include petsc.h90 in your code.
4497 
4498   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4499 
4500 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4501 @*/
4502 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4503 {
4504   PetscInt      *offsets, **closures;
4505   PetscInt      *meet[2];
4506   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
4507   PetscInt       p, h, c, m, mc;
4508 
4509   PetscFunctionBegin;
4510   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4511   PetscValidIntPointer(points, 3);
4512   PetscValidIntPointer(numCoveredPoints, 4);
4513   PetscValidPointer(coveredPoints, 5);
4514 
4515   PetscCall(DMPlexGetDepth(dm, &height));
4516   PetscCall(PetscMalloc1(numPoints, &closures));
4517   PetscCall(DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4518   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4519   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
4520   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4521   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4522 
4523   for (p = 0; p < numPoints; ++p) {
4524     PetscInt closureSize;
4525 
4526     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4527 
4528     offsets[p*(height+2)+0] = 0;
4529     for (h = 0; h < height+1; ++h) {
4530       PetscInt pStart, pEnd, i;
4531 
4532       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4533       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
4534         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4535           offsets[p*(height+2)+h+1] = i;
4536           break;
4537         }
4538       }
4539       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
4540     }
4541     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);
4542   }
4543   for (h = 0; h < height+1; ++h) {
4544     PetscInt dof;
4545 
4546     /* Copy in cone of first point */
4547     dof = offsets[h+1] - offsets[h];
4548     for (meetSize = 0; meetSize < dof; ++meetSize) {
4549       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
4550     }
4551     /* Check each successive cone */
4552     for (p = 1; p < numPoints && meetSize; ++p) {
4553       PetscInt newMeetSize = 0;
4554 
4555       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
4556       for (c = 0; c < dof; ++c) {
4557         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
4558 
4559         for (m = 0; m < meetSize; ++m) {
4560           if (point == meet[i][m]) {
4561             meet[1-i][newMeetSize++] = point;
4562             break;
4563           }
4564         }
4565       }
4566       meetSize = newMeetSize;
4567       i        = 1-i;
4568     }
4569     if (meetSize) break;
4570   }
4571   *numCoveredPoints = meetSize;
4572   *coveredPoints    = meet[i];
4573   for (p = 0; p < numPoints; ++p) {
4574     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4575   }
4576   PetscCall(PetscFree(closures));
4577   PetscCall(DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4578   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1-i]));
4579   PetscFunctionReturn(0);
4580 }
4581 
4582 /*@C
4583   DMPlexEqual - Determine if two DMs have the same topology
4584 
4585   Not Collective
4586 
4587   Input Parameters:
4588 + dmA - A DMPlex object
4589 - dmB - A DMPlex object
4590 
4591   Output Parameters:
4592 . equal - PETSC_TRUE if the topologies are identical
4593 
4594   Level: intermediate
4595 
4596   Notes:
4597   We are not solving graph isomorphism, so we do not permutation.
4598 
4599 .seealso: `DMPlexGetCone()`
4600 @*/
4601 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4602 {
4603   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4604 
4605   PetscFunctionBegin;
4606   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4607   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4608   PetscValidBoolPointer(equal, 3);
4609 
4610   *equal = PETSC_FALSE;
4611   PetscCall(DMPlexGetDepth(dmA, &depth));
4612   PetscCall(DMPlexGetDepth(dmB, &depthB));
4613   if (depth != depthB) PetscFunctionReturn(0);
4614   PetscCall(DMPlexGetChart(dmA, &pStart,  &pEnd));
4615   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4616   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4617   for (p = pStart; p < pEnd; ++p) {
4618     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4619     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4620 
4621     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4622     PetscCall(DMPlexGetCone(dmA, p, &cone));
4623     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4624     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4625     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4626     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4627     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4628     for (c = 0; c < coneSize; ++c) {
4629       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4630       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4631     }
4632     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4633     PetscCall(DMPlexGetSupport(dmA, p, &support));
4634     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4635     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4636     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4637     for (s = 0; s < supportSize; ++s) {
4638       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4639     }
4640   }
4641   *equal = PETSC_TRUE;
4642   PetscFunctionReturn(0);
4643 }
4644 
4645 /*@C
4646   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4647 
4648   Not Collective
4649 
4650   Input Parameters:
4651 + dm         - The DMPlex
4652 . cellDim    - The cell dimension
4653 - numCorners - The number of vertices on a cell
4654 
4655   Output Parameters:
4656 . numFaceVertices - The number of vertices on a face
4657 
4658   Level: developer
4659 
4660   Notes:
4661   Of course this can only work for a restricted set of symmetric shapes
4662 
4663 .seealso: `DMPlexGetCone()`
4664 @*/
4665 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4666 {
4667   MPI_Comm       comm;
4668 
4669   PetscFunctionBegin;
4670   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
4671   PetscValidIntPointer(numFaceVertices,4);
4672   switch (cellDim) {
4673   case 0:
4674     *numFaceVertices = 0;
4675     break;
4676   case 1:
4677     *numFaceVertices = 1;
4678     break;
4679   case 2:
4680     switch (numCorners) {
4681     case 3: /* triangle */
4682       *numFaceVertices = 2; /* Edge has 2 vertices */
4683       break;
4684     case 4: /* quadrilateral */
4685       *numFaceVertices = 2; /* Edge has 2 vertices */
4686       break;
4687     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
4688       *numFaceVertices = 3; /* Edge has 3 vertices */
4689       break;
4690     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4691       *numFaceVertices = 3; /* Edge has 3 vertices */
4692       break;
4693     default:
4694       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4695     }
4696     break;
4697   case 3:
4698     switch (numCorners) {
4699     case 4: /* tetradehdron */
4700       *numFaceVertices = 3; /* Face has 3 vertices */
4701       break;
4702     case 6: /* tet cohesive cells */
4703       *numFaceVertices = 4; /* Face has 4 vertices */
4704       break;
4705     case 8: /* hexahedron */
4706       *numFaceVertices = 4; /* Face has 4 vertices */
4707       break;
4708     case 9: /* tet cohesive Lagrange cells */
4709       *numFaceVertices = 6; /* Face has 6 vertices */
4710       break;
4711     case 10: /* quadratic tetrahedron */
4712       *numFaceVertices = 6; /* Face has 6 vertices */
4713       break;
4714     case 12: /* hex cohesive Lagrange cells */
4715       *numFaceVertices = 6; /* Face has 6 vertices */
4716       break;
4717     case 18: /* quadratic tet cohesive Lagrange cells */
4718       *numFaceVertices = 6; /* Face has 6 vertices */
4719       break;
4720     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4721       *numFaceVertices = 9; /* Face has 9 vertices */
4722       break;
4723     default:
4724       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4725     }
4726     break;
4727   default:
4728     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4729   }
4730   PetscFunctionReturn(0);
4731 }
4732 
4733 /*@
4734   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4735 
4736   Not Collective
4737 
4738   Input Parameter:
4739 . dm    - The DMPlex object
4740 
4741   Output Parameter:
4742 . depthLabel - The DMLabel recording point depth
4743 
4744   Level: developer
4745 
4746 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4747 @*/
4748 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4749 {
4750   PetscFunctionBegin;
4751   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4752   PetscValidPointer(depthLabel, 2);
4753   *depthLabel = dm->depthLabel;
4754   PetscFunctionReturn(0);
4755 }
4756 
4757 /*@
4758   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4759 
4760   Not Collective
4761 
4762   Input Parameter:
4763 . dm    - The DMPlex object
4764 
4765   Output Parameter:
4766 . depth - The number of strata (breadth first levels) in the DAG
4767 
4768   Level: developer
4769 
4770   Notes:
4771   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4772   The point depth is described more in detail in DMPlexGetDepthStratum().
4773   An empty mesh gives -1.
4774 
4775 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4776 @*/
4777 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4778 {
4779   DMLabel        label;
4780   PetscInt       d = 0;
4781 
4782   PetscFunctionBegin;
4783   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4784   PetscValidIntPointer(depth, 2);
4785   PetscCall(DMPlexGetDepthLabel(dm, &label));
4786   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4787   *depth = d-1;
4788   PetscFunctionReturn(0);
4789 }
4790 
4791 /*@
4792   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4793 
4794   Not Collective
4795 
4796   Input Parameters:
4797 + dm    - The DMPlex object
4798 - depth - The requested depth
4799 
4800   Output Parameters:
4801 + start - The first point at this depth
4802 - end   - One beyond the last point at this depth
4803 
4804   Notes:
4805   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4806   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4807   higher dimension, e.g., "edges".
4808 
4809   Level: developer
4810 
4811 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4812 @*/
4813 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4814 {
4815   DMLabel        label;
4816   PetscInt       pStart, pEnd;
4817 
4818   PetscFunctionBegin;
4819   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4820   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4821   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4822   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4823   if (pStart == pEnd) PetscFunctionReturn(0);
4824   if (depth < 0) {
4825     if (start) *start = pStart;
4826     if (end)   *end   = pEnd;
4827     PetscFunctionReturn(0);
4828   }
4829   PetscCall(DMPlexGetDepthLabel(dm, &label));
4830   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4831   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4832   PetscFunctionReturn(0);
4833 }
4834 
4835 /*@
4836   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4837 
4838   Not Collective
4839 
4840   Input Parameters:
4841 + dm     - The DMPlex object
4842 - height - The requested height
4843 
4844   Output Parameters:
4845 + start - The first point at this height
4846 - end   - One beyond the last point at this height
4847 
4848   Notes:
4849   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4850   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4851   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4852 
4853   Level: developer
4854 
4855 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4856 @*/
4857 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4858 {
4859   DMLabel        label;
4860   PetscInt       depth, pStart, pEnd;
4861 
4862   PetscFunctionBegin;
4863   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4864   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4865   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4866   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4867   if (pStart == pEnd) PetscFunctionReturn(0);
4868   if (height < 0) {
4869     if (start) *start = pStart;
4870     if (end)   *end   = pEnd;
4871     PetscFunctionReturn(0);
4872   }
4873   PetscCall(DMPlexGetDepthLabel(dm, &label));
4874   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4875   PetscCall(DMLabelGetNumValues(label, &depth));
4876   PetscCall(DMLabelGetStratumBounds(label, depth-1-height, start, end));
4877   PetscFunctionReturn(0);
4878 }
4879 
4880 /*@
4881   DMPlexGetPointDepth - Get the depth of a given point
4882 
4883   Not Collective
4884 
4885   Input Parameters:
4886 + dm    - The DMPlex object
4887 - point - The point
4888 
4889   Output Parameter:
4890 . depth - The depth of the point
4891 
4892   Level: intermediate
4893 
4894 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4895 @*/
4896 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4897 {
4898   PetscFunctionBegin;
4899   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4900   PetscValidIntPointer(depth, 3);
4901   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4902   PetscFunctionReturn(0);
4903 }
4904 
4905 /*@
4906   DMPlexGetPointHeight - Get the height of a given point
4907 
4908   Not Collective
4909 
4910   Input Parameters:
4911 + dm    - The DMPlex object
4912 - point - The point
4913 
4914   Output Parameter:
4915 . height - The height of the point
4916 
4917   Level: intermediate
4918 
4919 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4920 @*/
4921 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4922 {
4923   PetscInt       n, pDepth;
4924 
4925   PetscFunctionBegin;
4926   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4927   PetscValidIntPointer(height, 3);
4928   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4929   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4930   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
4931   PetscFunctionReturn(0);
4932 }
4933 
4934 /*@
4935   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4936 
4937   Not Collective
4938 
4939   Input Parameter:
4940 . dm - The DMPlex object
4941 
4942   Output Parameter:
4943 . celltypeLabel - The DMLabel recording cell polytope type
4944 
4945   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4946   DMCreateLabel(dm, "celltype") beforehand.
4947 
4948   Level: developer
4949 
4950 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4951 @*/
4952 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
4953 {
4954   PetscFunctionBegin;
4955   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4956   PetscValidPointer(celltypeLabel, 2);
4957   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4958   *celltypeLabel = dm->celltypeLabel;
4959   PetscFunctionReturn(0);
4960 }
4961 
4962 /*@
4963   DMPlexGetCellType - Get the polytope type of a given cell
4964 
4965   Not Collective
4966 
4967   Input Parameters:
4968 + dm   - The DMPlex object
4969 - cell - The cell
4970 
4971   Output Parameter:
4972 . celltype - The polytope type of the cell
4973 
4974   Level: intermediate
4975 
4976 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4977 @*/
4978 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
4979 {
4980   DMLabel        label;
4981   PetscInt       ct;
4982 
4983   PetscFunctionBegin;
4984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4985   PetscValidPointer(celltype, 3);
4986   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4987   PetscCall(DMLabelGetValue(label, cell, &ct));
4988   PetscCheck(ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4989   *celltype = (DMPolytopeType) ct;
4990   PetscFunctionReturn(0);
4991 }
4992 
4993 /*@
4994   DMPlexSetCellType - Set the polytope type of a given cell
4995 
4996   Not Collective
4997 
4998   Input Parameters:
4999 + dm   - The DMPlex object
5000 . cell - The cell
5001 - celltype - The polytope type of the cell
5002 
5003   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
5004   is executed. This function will override the computed type. However, if automatic classification will not succeed
5005   and a user wants to manually specify all types, the classification must be disabled by calling
5006   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5007 
5008   Level: advanced
5009 
5010 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5011 @*/
5012 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5013 {
5014   DMLabel        label;
5015 
5016   PetscFunctionBegin;
5017   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5018   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5019   PetscCall(DMLabelSetValue(label, cell, celltype));
5020   PetscFunctionReturn(0);
5021 }
5022 
5023 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5024 {
5025   PetscSection   section, s;
5026   Mat            m;
5027   PetscInt       maxHeight;
5028 
5029   PetscFunctionBegin;
5030   PetscCall(DMClone(dm, cdm));
5031   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5032   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5033   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5034   PetscCall(DMSetLocalSection(*cdm, section));
5035   PetscCall(PetscSectionDestroy(&section));
5036   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5037   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5038   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5039   PetscCall(PetscSectionDestroy(&s));
5040   PetscCall(MatDestroy(&m));
5041 
5042   PetscCall(DMSetNumFields(*cdm, 1));
5043   PetscCall(DMCreateDS(*cdm));
5044   PetscFunctionReturn(0);
5045 }
5046 
5047 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5048 {
5049   Vec coordsLocal, cellCoordsLocal;
5050   DM  coordsDM,    cellCoordsDM;
5051 
5052   PetscFunctionBegin;
5053   *field = NULL;
5054   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5055   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5056   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5057   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5058   if (coordsLocal && coordsDM) {
5059     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5060     else                                 PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5061   }
5062   PetscFunctionReturn(0);
5063 }
5064 
5065 /*@C
5066   DMPlexGetConeSection - Return a section which describes the layout of cone data
5067 
5068   Not Collective
5069 
5070   Input Parameters:
5071 . dm        - The DMPlex object
5072 
5073   Output Parameter:
5074 . section - The PetscSection object
5075 
5076   Level: developer
5077 
5078 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5079 @*/
5080 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5081 {
5082   DM_Plex *mesh = (DM_Plex*) dm->data;
5083 
5084   PetscFunctionBegin;
5085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5086   if (section) *section = mesh->coneSection;
5087   PetscFunctionReturn(0);
5088 }
5089 
5090 /*@C
5091   DMPlexGetSupportSection - Return a section which describes the layout of support data
5092 
5093   Not Collective
5094 
5095   Input Parameters:
5096 . dm        - The DMPlex object
5097 
5098   Output Parameter:
5099 . section - The PetscSection object
5100 
5101   Level: developer
5102 
5103 .seealso: `DMPlexGetConeSection()`
5104 @*/
5105 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5106 {
5107   DM_Plex *mesh = (DM_Plex*) dm->data;
5108 
5109   PetscFunctionBegin;
5110   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5111   if (section) *section = mesh->supportSection;
5112   PetscFunctionReturn(0);
5113 }
5114 
5115 /*@C
5116   DMPlexGetCones - Return cone data
5117 
5118   Not Collective
5119 
5120   Input Parameters:
5121 . dm        - The DMPlex object
5122 
5123   Output Parameter:
5124 . cones - The cone for each point
5125 
5126   Level: developer
5127 
5128 .seealso: `DMPlexGetConeSection()`
5129 @*/
5130 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5131 {
5132   DM_Plex *mesh = (DM_Plex*) dm->data;
5133 
5134   PetscFunctionBegin;
5135   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5136   if (cones) *cones = mesh->cones;
5137   PetscFunctionReturn(0);
5138 }
5139 
5140 /*@C
5141   DMPlexGetConeOrientations - Return cone orientation data
5142 
5143   Not Collective
5144 
5145   Input Parameters:
5146 . dm        - The DMPlex object
5147 
5148   Output Parameter:
5149 . coneOrientations - The array of cone orientations for all points
5150 
5151   Level: developer
5152 
5153   Notes:
5154   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5155 
5156   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5157 
5158 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5159 @*/
5160 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5161 {
5162   DM_Plex *mesh = (DM_Plex*) dm->data;
5163 
5164   PetscFunctionBegin;
5165   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5166   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5167   PetscFunctionReturn(0);
5168 }
5169 
5170 /******************************** FEM Support **********************************/
5171 
5172 /*
5173  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5174  representing a line in the section.
5175 */
5176 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
5177 {
5178   PetscFunctionBeginHot;
5179   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5180   if (line < 0) {
5181     *k = 0;
5182     *Nc = 0;
5183   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
5184     *k = 1;
5185   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
5186     /* An order k SEM disc has k-1 dofs on an edge */
5187     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5188     *k = *k / *Nc + 1;
5189   }
5190   PetscFunctionReturn(0);
5191 }
5192 
5193 /*@
5194 
5195   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5196   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5197   section provided (or the section of the DM).
5198 
5199   Input Parameters:
5200 + dm      - The DM
5201 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5202 - section - The PetscSection to reorder, or NULL for the default section
5203 
5204   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5205   degree of the basis.
5206 
5207   Example:
5208   A typical interpolated single-quad mesh might order points as
5209 .vb
5210   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5211 
5212   v4 -- e6 -- v3
5213   |           |
5214   e7    c0    e8
5215   |           |
5216   v1 -- e5 -- v2
5217 .ve
5218 
5219   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5220   dofs in the order of points, e.g.,
5221 .vb
5222     c0 -> [0,1,2,3]
5223     v1 -> [4]
5224     ...
5225     e5 -> [8, 9]
5226 .ve
5227 
5228   which corresponds to the dofs
5229 .vb
5230     6   10  11  7
5231     13  2   3   15
5232     12  0   1   14
5233     4   8   9   5
5234 .ve
5235 
5236   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5237 .vb
5238   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5239 .ve
5240 
5241   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5242 .vb
5243    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5244 .ve
5245 
5246   Level: developer
5247 
5248 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5249 @*/
5250 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5251 {
5252   DMLabel        label;
5253   PetscInt       dim, depth = -1, eStart = -1, Nf;
5254   PetscBool      vertexchart;
5255 
5256   PetscFunctionBegin;
5257   PetscCall(DMGetDimension(dm, &dim));
5258   if (dim < 1) PetscFunctionReturn(0);
5259   if (point < 0) {
5260     PetscInt sStart,sEnd;
5261 
5262     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5263     point = sEnd-sStart ? sStart : point;
5264   }
5265   PetscCall(DMPlexGetDepthLabel(dm, &label));
5266   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5267   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5268   if (depth == 1) {eStart = point;}
5269   else if  (depth == dim) {
5270     const PetscInt *cone;
5271 
5272     PetscCall(DMPlexGetCone(dm, point, &cone));
5273     if (dim == 2) eStart = cone[0];
5274     else if (dim == 3) {
5275       const PetscInt *cone2;
5276       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5277       eStart = cone2[0];
5278     } 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);
5279   } 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);
5280   {                             /* Determine whether the chart covers all points or just vertices. */
5281     PetscInt pStart,pEnd,cStart,cEnd;
5282     PetscCall(DMPlexGetDepthStratum(dm,0,&pStart,&pEnd));
5283     PetscCall(PetscSectionGetChart(section,&cStart,&cEnd));
5284     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5285     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5286     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5287   }
5288   PetscCall(PetscSectionGetNumFields(section, &Nf));
5289   for (PetscInt d=1; d<=dim; d++) {
5290     PetscInt k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5291     PetscInt *perm;
5292 
5293     for (f = 0; f < Nf; ++f) {
5294       PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5295       size += PetscPowInt(k+1, d)*Nc;
5296     }
5297     PetscCall(PetscMalloc1(size, &perm));
5298     for (f = 0; f < Nf; ++f) {
5299       switch (d) {
5300       case 1:
5301         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5302         /*
5303          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5304          We want              [ vtx0; edge of length k-1; vtx1 ]
5305          */
5306         for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
5307         for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
5308         for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
5309         foffset = offset;
5310         break;
5311       case 2:
5312         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5313         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5314         /* The SEM order is
5315 
5316          v_lb, {e_b}, v_rb,
5317          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5318          v_lt, reverse {e_t}, v_rt
5319          */
5320         {
5321           const PetscInt of   = 0;
5322           const PetscInt oeb  = of   + PetscSqr(k-1);
5323           const PetscInt oer  = oeb  + (k-1);
5324           const PetscInt oet  = oer  + (k-1);
5325           const PetscInt oel  = oet  + (k-1);
5326           const PetscInt ovlb = oel  + (k-1);
5327           const PetscInt ovrb = ovlb + 1;
5328           const PetscInt ovrt = ovrb + 1;
5329           const PetscInt ovlt = ovrt + 1;
5330           PetscInt       o;
5331 
5332           /* bottom */
5333           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
5334           for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5335           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
5336           /* middle */
5337           for (i = 0; i < k-1; ++i) {
5338             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
5339             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;
5340             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
5341           }
5342           /* top */
5343           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
5344           for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5345           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
5346           foffset = offset;
5347         }
5348         break;
5349       case 3:
5350         /* The original hex closure is
5351 
5352          {c,
5353          f_b, f_t, f_f, f_b, f_r, f_l,
5354          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5355          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5356          */
5357         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5358         /* The SEM order is
5359          Bottom Slice
5360          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5361          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5362          v_blb, {e_bb}, v_brb,
5363 
5364          Middle Slice (j)
5365          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5366          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5367          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5368 
5369          Top Slice
5370          v_tlf, {e_tf}, v_trf,
5371          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5372          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5373          */
5374         {
5375           const PetscInt oc    = 0;
5376           const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
5377           const PetscInt oft   = ofb   + PetscSqr(k-1);
5378           const PetscInt off   = oft   + PetscSqr(k-1);
5379           const PetscInt ofk   = off   + PetscSqr(k-1);
5380           const PetscInt ofr   = ofk   + PetscSqr(k-1);
5381           const PetscInt ofl   = ofr   + PetscSqr(k-1);
5382           const PetscInt oebl  = ofl   + PetscSqr(k-1);
5383           const PetscInt oebb  = oebl  + (k-1);
5384           const PetscInt oebr  = oebb  + (k-1);
5385           const PetscInt oebf  = oebr  + (k-1);
5386           const PetscInt oetf  = oebf  + (k-1);
5387           const PetscInt oetr  = oetf  + (k-1);
5388           const PetscInt oetb  = oetr  + (k-1);
5389           const PetscInt oetl  = oetb  + (k-1);
5390           const PetscInt oerf  = oetl  + (k-1);
5391           const PetscInt oelf  = oerf  + (k-1);
5392           const PetscInt oelb  = oelf  + (k-1);
5393           const PetscInt oerb  = oelb  + (k-1);
5394           const PetscInt ovblf = oerb  + (k-1);
5395           const PetscInt ovblb = ovblf + 1;
5396           const PetscInt ovbrb = ovblb + 1;
5397           const PetscInt ovbrf = ovbrb + 1;
5398           const PetscInt ovtlf = ovbrf + 1;
5399           const PetscInt ovtrf = ovtlf + 1;
5400           const PetscInt ovtrb = ovtrf + 1;
5401           const PetscInt ovtlb = ovtrb + 1;
5402           PetscInt       o, n;
5403 
5404           /* Bottom Slice */
5405           /*   bottom */
5406           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
5407           for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5408           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
5409           /*   middle */
5410           for (i = 0; i < k-1; ++i) {
5411             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
5412             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;}
5413             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
5414           }
5415           /*   top */
5416           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
5417           for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5418           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
5419 
5420           /* Middle Slice */
5421           for (j = 0; j < k-1; ++j) {
5422             /*   bottom */
5423             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
5424             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;
5425             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
5426             /*   middle */
5427             for (i = 0; i < k-1; ++i) {
5428               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
5429               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;
5430               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
5431             }
5432             /*   top */
5433             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
5434             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;
5435             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
5436           }
5437 
5438           /* Top Slice */
5439           /*   bottom */
5440           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
5441           for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5442           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
5443           /*   middle */
5444           for (i = 0; i < k-1; ++i) {
5445             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
5446             for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
5447             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
5448           }
5449           /*   top */
5450           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
5451           for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5452           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
5453 
5454           foffset = offset;
5455         }
5456         break;
5457       default: SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5458       }
5459     }
5460     PetscCheck(offset == size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5461     /* Check permutation */
5462     {
5463       PetscInt *check;
5464 
5465       PetscCall(PetscMalloc1(size, &check));
5466       for (i = 0; i < size; ++i) {
5467         check[i] = -1;
5468         PetscCheck(perm[i] >= 0 && perm[i] < size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5469       }
5470       for (i = 0; i < size; ++i) check[perm[i]] = i;
5471       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5472       PetscCall(PetscFree(check));
5473     }
5474     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size, PETSC_OWN_POINTER, perm));
5475     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5476       PetscInt *loc_perm;
5477       PetscCall(PetscMalloc1(size*2, &loc_perm));
5478       for (PetscInt i=0; i<size; i++) {
5479         loc_perm[i] = perm[i];
5480         loc_perm[size+i] = size + perm[i];
5481       }
5482       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size*2, PETSC_OWN_POINTER, loc_perm));
5483     }
5484   }
5485   PetscFunctionReturn(0);
5486 }
5487 
5488 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5489 {
5490   PetscDS        prob;
5491   PetscInt       depth, Nf, h;
5492   DMLabel        label;
5493 
5494   PetscFunctionBeginHot;
5495   PetscCall(DMGetDS(dm, &prob));
5496   Nf      = prob->Nf;
5497   label   = dm->depthLabel;
5498   *dspace = NULL;
5499   if (field < Nf) {
5500     PetscObject disc = prob->disc[field];
5501 
5502     if (disc->classid == PETSCFE_CLASSID) {
5503       PetscDualSpace dsp;
5504 
5505       PetscCall(PetscFEGetDualSpace((PetscFE)disc,&dsp));
5506       PetscCall(DMLabelGetNumValues(label,&depth));
5507       PetscCall(DMLabelGetValue(label,point,&h));
5508       h    = depth - 1 - h;
5509       if (h) {
5510         PetscCall(PetscDualSpaceGetHeightSubspace(dsp,h,dspace));
5511       } else {
5512         *dspace = dsp;
5513       }
5514     }
5515   }
5516   PetscFunctionReturn(0);
5517 }
5518 
5519 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5520 {
5521   PetscScalar    *array;
5522   const PetscScalar *vArray;
5523   const PetscInt *cone, *coneO;
5524   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
5525 
5526   PetscFunctionBeginHot;
5527   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5528   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5529   PetscCall(DMPlexGetCone(dm, point, &cone));
5530   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5531   if (!values || !*values) {
5532     if ((point >= pStart) && (point < pEnd)) {
5533       PetscInt dof;
5534 
5535       PetscCall(PetscSectionGetDof(section, point, &dof));
5536       size += dof;
5537     }
5538     for (p = 0; p < numPoints; ++p) {
5539       const PetscInt cp = cone[p];
5540       PetscInt       dof;
5541 
5542       if ((cp < pStart) || (cp >= pEnd)) continue;
5543       PetscCall(PetscSectionGetDof(section, cp, &dof));
5544       size += dof;
5545     }
5546     if (!values) {
5547       if (csize) *csize = size;
5548       PetscFunctionReturn(0);
5549     }
5550     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5551   } else {
5552     array = *values;
5553   }
5554   size = 0;
5555   PetscCall(VecGetArrayRead(v, &vArray));
5556   if ((point >= pStart) && (point < pEnd)) {
5557     PetscInt     dof, off, d;
5558     const PetscScalar *varr;
5559 
5560     PetscCall(PetscSectionGetDof(section, point, &dof));
5561     PetscCall(PetscSectionGetOffset(section, point, &off));
5562     varr = &vArray[off];
5563     for (d = 0; d < dof; ++d, ++offset) {
5564       array[offset] = varr[d];
5565     }
5566     size += dof;
5567   }
5568   for (p = 0; p < numPoints; ++p) {
5569     const PetscInt cp = cone[p];
5570     PetscInt       o  = coneO[p];
5571     PetscInt       dof, off, d;
5572     const PetscScalar *varr;
5573 
5574     if ((cp < pStart) || (cp >= pEnd)) continue;
5575     PetscCall(PetscSectionGetDof(section, cp, &dof));
5576     PetscCall(PetscSectionGetOffset(section, cp, &off));
5577     varr = &vArray[off];
5578     if (o >= 0) {
5579       for (d = 0; d < dof; ++d, ++offset) {
5580         array[offset] = varr[d];
5581       }
5582     } else {
5583       for (d = dof-1; d >= 0; --d, ++offset) {
5584         array[offset] = varr[d];
5585       }
5586     }
5587     size += dof;
5588   }
5589   PetscCall(VecRestoreArrayRead(v, &vArray));
5590   if (!*values) {
5591     if (csize) *csize = size;
5592     *values = array;
5593   } else {
5594     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5595     *csize = size;
5596   }
5597   PetscFunctionReturn(0);
5598 }
5599 
5600 /* Compress out points not in the section */
5601 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5602 {
5603   const PetscInt np = *numPoints;
5604   PetscInt       pStart, pEnd, p, q;
5605 
5606   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5607   for (p = 0, q = 0; p < np; ++p) {
5608     const PetscInt r = points[p*2];
5609     if ((r >= pStart) && (r < pEnd)) {
5610       points[q*2]   = r;
5611       points[q*2+1] = points[p*2+1];
5612       ++q;
5613     }
5614   }
5615   *numPoints = q;
5616   return 0;
5617 }
5618 
5619 /* Compressed closure does not apply closure permutation */
5620 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5621 {
5622   const PetscInt *cla = NULL;
5623   PetscInt       np, *pts = NULL;
5624 
5625   PetscFunctionBeginHot;
5626   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints));
5627   if (*clPoints) {
5628     PetscInt dof, off;
5629 
5630     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5631     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5632     PetscCall(ISGetIndices(*clPoints, &cla));
5633     np   = dof/2;
5634     pts  = (PetscInt *) &cla[off];
5635   } else {
5636     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5637     PetscCall(CompressPoints_Private(section, &np, pts));
5638   }
5639   *numPoints = np;
5640   *points    = pts;
5641   *clp       = cla;
5642   PetscFunctionReturn(0);
5643 }
5644 
5645 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5646 {
5647   PetscFunctionBeginHot;
5648   if (!*clPoints) {
5649     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5650   } else {
5651     PetscCall(ISRestoreIndices(*clPoints, clp));
5652   }
5653   *numPoints = 0;
5654   *points    = NULL;
5655   *clSec     = NULL;
5656   *clPoints  = NULL;
5657   *clp       = NULL;
5658   PetscFunctionReturn(0);
5659 }
5660 
5661 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5662 {
5663   PetscInt          offset = 0, p;
5664   const PetscInt    **perms = NULL;
5665   const PetscScalar **flips = NULL;
5666 
5667   PetscFunctionBeginHot;
5668   *size = 0;
5669   PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
5670   for (p = 0; p < numPoints; p++) {
5671     const PetscInt    point = points[2*p];
5672     const PetscInt    *perm = perms ? perms[p] : NULL;
5673     const PetscScalar *flip = flips ? flips[p] : NULL;
5674     PetscInt          dof, off, d;
5675     const PetscScalar *varr;
5676 
5677     PetscCall(PetscSectionGetDof(section, point, &dof));
5678     PetscCall(PetscSectionGetOffset(section, point, &off));
5679     varr = &vArray[off];
5680     if (clperm) {
5681       if (perm) {
5682         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5683       } else {
5684         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5685       }
5686       if (flip) {
5687         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5688       }
5689     } else {
5690       if (perm) {
5691         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5692       } else {
5693         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5694       }
5695       if (flip) {
5696         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5697       }
5698     }
5699     offset += dof;
5700   }
5701   PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
5702   *size = offset;
5703   PetscFunctionReturn(0);
5704 }
5705 
5706 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[])
5707 {
5708   PetscInt          offset = 0, f;
5709 
5710   PetscFunctionBeginHot;
5711   *size = 0;
5712   for (f = 0; f < numFields; ++f) {
5713     PetscInt          p;
5714     const PetscInt    **perms = NULL;
5715     const PetscScalar **flips = NULL;
5716 
5717     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5718     for (p = 0; p < numPoints; p++) {
5719       const PetscInt    point = points[2*p];
5720       PetscInt          fdof, foff, b;
5721       const PetscScalar *varr;
5722       const PetscInt    *perm = perms ? perms[p] : NULL;
5723       const PetscScalar *flip = flips ? flips[p] : NULL;
5724 
5725       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5726       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5727       varr = &vArray[foff];
5728       if (clperm) {
5729         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5730         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5731         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5732       } else {
5733         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5734         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5735         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5736       }
5737       offset += fdof;
5738     }
5739     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5740   }
5741   *size = offset;
5742   PetscFunctionReturn(0);
5743 }
5744 
5745 /*@C
5746   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5747 
5748   Not collective
5749 
5750   Input Parameters:
5751 + dm - The DM
5752 . section - The section describing the layout in v, or NULL to use the default section
5753 . v - The local vector
5754 - point - The point in the DM
5755 
5756   Input/Output Parameters:
5757 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5758 - values - An array to use for the values, or NULL to have it allocated automatically;
5759            if the user provided NULL, it is a borrowed array and should not be freed
5760 
5761 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5762 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5763 $ assembly function, and a user may already have allocated storage for this operation.
5764 $
5765 $ A typical use could be
5766 $
5767 $  values = NULL;
5768 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5769 $  for (cl = 0; cl < clSize; ++cl) {
5770 $    <Compute on closure>
5771 $  }
5772 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5773 $
5774 $ or
5775 $
5776 $  PetscMalloc1(clMaxSize, &values);
5777 $  for (p = pStart; p < pEnd; ++p) {
5778 $    clSize = clMaxSize;
5779 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5780 $    for (cl = 0; cl < clSize; ++cl) {
5781 $      <Compute on closure>
5782 $    }
5783 $  }
5784 $  PetscFree(values);
5785 
5786   Fortran Notes:
5787   Since it returns an array, this routine is only available in Fortran 90, and you must
5788   include petsc.h90 in your code.
5789 
5790   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5791 
5792   Level: intermediate
5793 
5794 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5795 @*/
5796 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5797 {
5798   PetscSection       clSection;
5799   IS                 clPoints;
5800   PetscInt          *points = NULL;
5801   const PetscInt    *clp, *perm;
5802   PetscInt           depth, numFields, numPoints, asize;
5803 
5804   PetscFunctionBeginHot;
5805   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5806   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5807   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5808   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5809   PetscCall(DMPlexGetDepth(dm, &depth));
5810   PetscCall(PetscSectionGetNumFields(section, &numFields));
5811   if (depth == 1 && numFields < 2) {
5812     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5813     PetscFunctionReturn(0);
5814   }
5815   /* Get points */
5816   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5817   /* Get sizes */
5818   asize = 0;
5819   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5820     PetscInt dof;
5821     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5822     asize += dof;
5823   }
5824   if (values) {
5825     const PetscScalar *vArray;
5826     PetscInt          size;
5827 
5828     if (*values) {
5829       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);
5830     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5831     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm));
5832     PetscCall(VecGetArrayRead(v, &vArray));
5833     /* Get values */
5834     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5835     else               PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5836     PetscCheck(asize == size,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5837     /* Cleanup array */
5838     PetscCall(VecRestoreArrayRead(v, &vArray));
5839   }
5840   if (csize) *csize = asize;
5841   /* Cleanup points */
5842   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5843   PetscFunctionReturn(0);
5844 }
5845 
5846 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5847 {
5848   DMLabel            depthLabel;
5849   PetscSection       clSection;
5850   IS                 clPoints;
5851   PetscScalar       *array;
5852   const PetscScalar *vArray;
5853   PetscInt          *points = NULL;
5854   const PetscInt    *clp, *perm = NULL;
5855   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5856 
5857   PetscFunctionBeginHot;
5858   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5859   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5860   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5861   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5862   PetscCall(DMPlexGetDepth(dm, &mdepth));
5863   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5864   PetscCall(PetscSectionGetNumFields(section, &numFields));
5865   if (mdepth == 1 && numFields < 2) {
5866     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5867     PetscFunctionReturn(0);
5868   }
5869   /* Get points */
5870   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5871   for (clsize=0,p=0; p<Np; p++) {
5872     PetscInt dof;
5873     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
5874     clsize += dof;
5875   }
5876   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm));
5877   /* Filter points */
5878   for (p = 0; p < numPoints*2; p += 2) {
5879     PetscInt dep;
5880 
5881     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5882     if (dep != depth) continue;
5883     points[Np*2+0] = points[p];
5884     points[Np*2+1] = points[p+1];
5885     ++Np;
5886   }
5887   /* Get array */
5888   if (!values || !*values) {
5889     PetscInt asize = 0, dof;
5890 
5891     for (p = 0; p < Np*2; p += 2) {
5892       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5893       asize += dof;
5894     }
5895     if (!values) {
5896       PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5897       if (csize) *csize = asize;
5898       PetscFunctionReturn(0);
5899     }
5900     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5901   } else {
5902     array = *values;
5903   }
5904   PetscCall(VecGetArrayRead(v, &vArray));
5905   /* Get values */
5906   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5907   else               PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5908   /* Cleanup points */
5909   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5910   /* Cleanup array */
5911   PetscCall(VecRestoreArrayRead(v, &vArray));
5912   if (!*values) {
5913     if (csize) *csize = size;
5914     *values = array;
5915   } else {
5916     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5917     *csize = size;
5918   }
5919   PetscFunctionReturn(0);
5920 }
5921 
5922 /*@C
5923   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5924 
5925   Not collective
5926 
5927   Input Parameters:
5928 + dm - The DM
5929 . section - The section describing the layout in v, or NULL to use the default section
5930 . v - The local vector
5931 . point - The point in the DM
5932 . csize - The number of values in the closure, or NULL
5933 - values - The array of values, which is a borrowed array and should not be freed
5934 
5935   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5936 
5937   Fortran Notes:
5938   Since it returns an array, this routine is only available in Fortran 90, and you must
5939   include petsc.h90 in your code.
5940 
5941   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5942 
5943   Level: intermediate
5944 
5945 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5946 @*/
5947 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5948 {
5949   PetscInt       size = 0;
5950 
5951   PetscFunctionBegin;
5952   /* Should work without recalculating size */
5953   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values));
5954   *values = NULL;
5955   PetscFunctionReturn(0);
5956 }
5957 
5958 static inline void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5959 static inline void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5960 
5961 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[])
5962 {
5963   PetscInt        cdof;   /* The number of constraints on this point */
5964   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5965   PetscScalar    *a;
5966   PetscInt        off, cind = 0, k;
5967 
5968   PetscFunctionBegin;
5969   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5970   PetscCall(PetscSectionGetOffset(section, point, &off));
5971   a    = &array[off];
5972   if (!cdof || setBC) {
5973     if (clperm) {
5974       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5975       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5976     } else {
5977       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5978       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5979     }
5980   } else {
5981     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5982     if (clperm) {
5983       if (perm) {for (k = 0; k < dof; ++k) {
5984           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5985           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5986         }
5987       } else {
5988         for (k = 0; k < dof; ++k) {
5989           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5990           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5991         }
5992       }
5993     } else {
5994       if (perm) {
5995         for (k = 0; k < dof; ++k) {
5996           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5997           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
5998         }
5999       } else {
6000         for (k = 0; k < dof; ++k) {
6001           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6002           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6003         }
6004       }
6005     }
6006   }
6007   PetscFunctionReturn(0);
6008 }
6009 
6010 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[])
6011 {
6012   PetscInt        cdof;   /* The number of constraints on this point */
6013   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6014   PetscScalar    *a;
6015   PetscInt        off, cind = 0, k;
6016 
6017   PetscFunctionBegin;
6018   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6019   PetscCall(PetscSectionGetOffset(section, point, &off));
6020   a    = &array[off];
6021   if (cdof) {
6022     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6023     if (clperm) {
6024       if (perm) {
6025         for (k = 0; k < dof; ++k) {
6026           if ((cind < cdof) && (k == cdofs[cind])) {
6027             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
6028             cind++;
6029           }
6030         }
6031       } else {
6032         for (k = 0; k < dof; ++k) {
6033           if ((cind < cdof) && (k == cdofs[cind])) {
6034             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
6035             cind++;
6036           }
6037         }
6038       }
6039     } else {
6040       if (perm) {
6041         for (k = 0; k < dof; ++k) {
6042           if ((cind < cdof) && (k == cdofs[cind])) {
6043             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6044             cind++;
6045           }
6046         }
6047       } else {
6048         for (k = 0; k < dof; ++k) {
6049           if ((cind < cdof) && (k == cdofs[cind])) {
6050             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6051             cind++;
6052           }
6053         }
6054       }
6055     }
6056   }
6057   PetscFunctionReturn(0);
6058 }
6059 
6060 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[])
6061 {
6062   PetscScalar    *a;
6063   PetscInt        fdof, foff, fcdof, foffset = *offset;
6064   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6065   PetscInt        cind = 0, b;
6066 
6067   PetscFunctionBegin;
6068   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6069   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6070   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6071   a    = &array[foff];
6072   if (!fcdof || setBC) {
6073     if (clperm) {
6074       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
6075       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
6076     } else {
6077       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
6078       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
6079     }
6080   } else {
6081     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6082     if (clperm) {
6083       if (perm) {
6084         for (b = 0; b < fdof; b++) {
6085           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6086           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6087         }
6088       } else {
6089         for (b = 0; b < fdof; b++) {
6090           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6091           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6092         }
6093       }
6094     } else {
6095       if (perm) {
6096         for (b = 0; b < fdof; b++) {
6097           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6098           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6099         }
6100       } else {
6101         for (b = 0; b < fdof; b++) {
6102           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6103           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6104         }
6105       }
6106     }
6107   }
6108   *offset += fdof;
6109   PetscFunctionReturn(0);
6110 }
6111 
6112 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[])
6113 {
6114   PetscScalar    *a;
6115   PetscInt        fdof, foff, fcdof, foffset = *offset;
6116   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6117   PetscInt        Nc, cind = 0, ncind = 0, b;
6118   PetscBool       ncSet, fcSet;
6119 
6120   PetscFunctionBegin;
6121   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6122   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6123   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6124   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6125   a    = &array[foff];
6126   if (fcdof) {
6127     /* We just override fcdof and fcdofs with Ncc and comps */
6128     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6129     if (clperm) {
6130       if (perm) {
6131         if (comps) {
6132           for (b = 0; b < fdof; b++) {
6133             ncSet = fcSet = PETSC_FALSE;
6134             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6135             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6136             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
6137           }
6138         } else {
6139           for (b = 0; b < fdof; b++) {
6140             if ((cind < fcdof) && (b == fcdofs[cind])) {
6141               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6142               ++cind;
6143             }
6144           }
6145         }
6146       } else {
6147         if (comps) {
6148           for (b = 0; b < fdof; b++) {
6149             ncSet = fcSet = PETSC_FALSE;
6150             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6151             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6152             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
6153           }
6154         } else {
6155           for (b = 0; b < fdof; b++) {
6156             if ((cind < fcdof) && (b == fcdofs[cind])) {
6157               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6158               ++cind;
6159             }
6160           }
6161         }
6162       }
6163     } else {
6164       if (perm) {
6165         if (comps) {
6166           for (b = 0; b < fdof; b++) {
6167             ncSet = fcSet = PETSC_FALSE;
6168             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6169             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6170             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
6171           }
6172         } else {
6173           for (b = 0; b < fdof; b++) {
6174             if ((cind < fcdof) && (b == fcdofs[cind])) {
6175               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6176               ++cind;
6177             }
6178           }
6179         }
6180       } else {
6181         if (comps) {
6182           for (b = 0; b < fdof; b++) {
6183             ncSet = fcSet = PETSC_FALSE;
6184             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6185             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6186             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
6187           }
6188         } else {
6189           for (b = 0; b < fdof; b++) {
6190             if ((cind < fcdof) && (b == fcdofs[cind])) {
6191               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6192               ++cind;
6193             }
6194           }
6195         }
6196       }
6197     }
6198   }
6199   *offset += fdof;
6200   PetscFunctionReturn(0);
6201 }
6202 
6203 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6204 {
6205   PetscScalar    *array;
6206   const PetscInt *cone, *coneO;
6207   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6208 
6209   PetscFunctionBeginHot;
6210   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6211   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6212   PetscCall(DMPlexGetCone(dm, point, &cone));
6213   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6214   PetscCall(VecGetArray(v, &array));
6215   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6216     const PetscInt cp = !p ? point : cone[p-1];
6217     const PetscInt o  = !p ? 0     : coneO[p-1];
6218 
6219     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
6220     PetscCall(PetscSectionGetDof(section, cp, &dof));
6221     /* ADD_VALUES */
6222     {
6223       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6224       PetscScalar    *a;
6225       PetscInt        cdof, coff, cind = 0, k;
6226 
6227       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6228       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6229       a    = &array[coff];
6230       if (!cdof) {
6231         if (o >= 0) {
6232           for (k = 0; k < dof; ++k) {
6233             a[k] += values[off+k];
6234           }
6235         } else {
6236           for (k = 0; k < dof; ++k) {
6237             a[k] += values[off+dof-k-1];
6238           }
6239         }
6240       } else {
6241         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6242         if (o >= 0) {
6243           for (k = 0; k < dof; ++k) {
6244             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6245             a[k] += values[off+k];
6246           }
6247         } else {
6248           for (k = 0; k < dof; ++k) {
6249             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6250             a[k] += values[off+dof-k-1];
6251           }
6252         }
6253       }
6254     }
6255   }
6256   PetscCall(VecRestoreArray(v, &array));
6257   PetscFunctionReturn(0);
6258 }
6259 
6260 /*@C
6261   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6262 
6263   Not collective
6264 
6265   Input Parameters:
6266 + dm - The DM
6267 . section - The section describing the layout in v, or NULL to use the default section
6268 . v - The local vector
6269 . point - The point in the DM
6270 . values - The array of values
6271 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6272          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6273 
6274   Fortran Notes:
6275   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6276 
6277   Level: intermediate
6278 
6279 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6280 @*/
6281 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6282 {
6283   PetscSection    clSection;
6284   IS              clPoints;
6285   PetscScalar    *array;
6286   PetscInt       *points = NULL;
6287   const PetscInt *clp, *clperm = NULL;
6288   PetscInt        depth, numFields, numPoints, p, clsize;
6289 
6290   PetscFunctionBeginHot;
6291   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6292   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6293   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6294   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6295   PetscCall(DMPlexGetDepth(dm, &depth));
6296   PetscCall(PetscSectionGetNumFields(section, &numFields));
6297   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6298     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6299     PetscFunctionReturn(0);
6300   }
6301   /* Get points */
6302   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6303   for (clsize=0,p=0; p<numPoints; p++) {
6304     PetscInt dof;
6305     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
6306     clsize += dof;
6307   }
6308   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
6309   /* Get array */
6310   PetscCall(VecGetArray(v, &array));
6311   /* Get values */
6312   if (numFields > 0) {
6313     PetscInt offset = 0, f;
6314     for (f = 0; f < numFields; ++f) {
6315       const PetscInt    **perms = NULL;
6316       const PetscScalar **flips = NULL;
6317 
6318       PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6319       switch (mode) {
6320       case INSERT_VALUES:
6321         for (p = 0; p < numPoints; p++) {
6322           const PetscInt    point = points[2*p];
6323           const PetscInt    *perm = perms ? perms[p] : NULL;
6324           const PetscScalar *flip = flips ? flips[p] : NULL;
6325           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6326         } break;
6327       case INSERT_ALL_VALUES:
6328         for (p = 0; p < numPoints; p++) {
6329           const PetscInt    point = points[2*p];
6330           const PetscInt    *perm = perms ? perms[p] : NULL;
6331           const PetscScalar *flip = flips ? flips[p] : NULL;
6332           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6333         } break;
6334       case INSERT_BC_VALUES:
6335         for (p = 0; p < numPoints; p++) {
6336           const PetscInt    point = points[2*p];
6337           const PetscInt    *perm = perms ? perms[p] : NULL;
6338           const PetscScalar *flip = flips ? flips[p] : NULL;
6339           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6340         } break;
6341       case ADD_VALUES:
6342         for (p = 0; p < numPoints; p++) {
6343           const PetscInt    point = points[2*p];
6344           const PetscInt    *perm = perms ? perms[p] : NULL;
6345           const PetscScalar *flip = flips ? flips[p] : NULL;
6346           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6347         } break;
6348       case ADD_ALL_VALUES:
6349         for (p = 0; p < numPoints; p++) {
6350           const PetscInt    point = points[2*p];
6351           const PetscInt    *perm = perms ? perms[p] : NULL;
6352           const PetscScalar *flip = flips ? flips[p] : NULL;
6353           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6354         } break;
6355       case ADD_BC_VALUES:
6356         for (p = 0; p < numPoints; p++) {
6357           const PetscInt    point = points[2*p];
6358           const PetscInt    *perm = perms ? perms[p] : NULL;
6359           const PetscScalar *flip = flips ? flips[p] : NULL;
6360           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6361         } break;
6362       default:
6363         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6364       }
6365       PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6366     }
6367   } else {
6368     PetscInt dof, off;
6369     const PetscInt    **perms = NULL;
6370     const PetscScalar **flips = NULL;
6371 
6372     PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
6373     switch (mode) {
6374     case INSERT_VALUES:
6375       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6376         const PetscInt    point = points[2*p];
6377         const PetscInt    *perm = perms ? perms[p] : NULL;
6378         const PetscScalar *flip = flips ? flips[p] : NULL;
6379         PetscCall(PetscSectionGetDof(section, point, &dof));
6380         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6381       } break;
6382     case INSERT_ALL_VALUES:
6383       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6384         const PetscInt    point = points[2*p];
6385         const PetscInt    *perm = perms ? perms[p] : NULL;
6386         const PetscScalar *flip = flips ? flips[p] : NULL;
6387         PetscCall(PetscSectionGetDof(section, point, &dof));
6388         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
6389       } break;
6390     case INSERT_BC_VALUES:
6391       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6392         const PetscInt    point = points[2*p];
6393         const PetscInt    *perm = perms ? perms[p] : NULL;
6394         const PetscScalar *flip = flips ? flips[p] : NULL;
6395         PetscCall(PetscSectionGetDof(section, point, &dof));
6396         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
6397       } break;
6398     case ADD_VALUES:
6399       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6400         const PetscInt    point = points[2*p];
6401         const PetscInt    *perm = perms ? perms[p] : NULL;
6402         const PetscScalar *flip = flips ? flips[p] : NULL;
6403         PetscCall(PetscSectionGetDof(section, point, &dof));
6404         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
6405       } break;
6406     case ADD_ALL_VALUES:
6407       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6408         const PetscInt    point = points[2*p];
6409         const PetscInt    *perm = perms ? perms[p] : NULL;
6410         const PetscScalar *flip = flips ? flips[p] : NULL;
6411         PetscCall(PetscSectionGetDof(section, point, &dof));
6412         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
6413       } break;
6414     case ADD_BC_VALUES:
6415       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6416         const PetscInt    point = points[2*p];
6417         const PetscInt    *perm = perms ? perms[p] : NULL;
6418         const PetscScalar *flip = flips ? flips[p] : NULL;
6419         PetscCall(PetscSectionGetDof(section, point, &dof));
6420         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
6421       } break;
6422     default:
6423       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6424     }
6425     PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
6426   }
6427   /* Cleanup points */
6428   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6429   /* Cleanup array */
6430   PetscCall(VecRestoreArray(v, &array));
6431   PetscFunctionReturn(0);
6432 }
6433 
6434 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6435 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
6436 {
6437   PetscFunctionBegin;
6438   if (label) {
6439     PetscBool contains;
6440     PetscInt  fdof;
6441 
6442     PetscCall(DMLabelStratumHasPoint(label, labelId, point, &contains));
6443     if (!contains) {
6444       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6445       *offset += fdof;
6446       PetscFunctionReturn(1);
6447     }
6448   }
6449   PetscFunctionReturn(0);
6450 }
6451 
6452 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6453 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)
6454 {
6455   PetscSection    clSection;
6456   IS              clPoints;
6457   PetscScalar    *array;
6458   PetscInt       *points = NULL;
6459   const PetscInt *clp;
6460   PetscInt        numFields, numPoints, p;
6461   PetscInt        offset = 0, f;
6462 
6463   PetscFunctionBeginHot;
6464   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6465   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6466   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6467   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6468   PetscCall(PetscSectionGetNumFields(section, &numFields));
6469   /* Get points */
6470   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6471   /* Get array */
6472   PetscCall(VecGetArray(v, &array));
6473   /* Get values */
6474   for (f = 0; f < numFields; ++f) {
6475     const PetscInt    **perms = NULL;
6476     const PetscScalar **flips = NULL;
6477 
6478     if (!fieldActive[f]) {
6479       for (p = 0; p < numPoints*2; p += 2) {
6480         PetscInt fdof;
6481         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6482         offset += fdof;
6483       }
6484       continue;
6485     }
6486     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6487     switch (mode) {
6488     case INSERT_VALUES:
6489       for (p = 0; p < numPoints; p++) {
6490         const PetscInt    point = points[2*p];
6491         const PetscInt    *perm = perms ? perms[p] : NULL;
6492         const PetscScalar *flip = flips ? flips[p] : NULL;
6493         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6494         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6495       } break;
6496     case INSERT_ALL_VALUES:
6497       for (p = 0; p < numPoints; p++) {
6498         const PetscInt    point = points[2*p];
6499         const PetscInt    *perm = perms ? perms[p] : NULL;
6500         const PetscScalar *flip = flips ? flips[p] : NULL;
6501         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6502         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6503       } break;
6504     case INSERT_BC_VALUES:
6505       for (p = 0; p < numPoints; p++) {
6506         const PetscInt    point = points[2*p];
6507         const PetscInt    *perm = perms ? perms[p] : NULL;
6508         const PetscScalar *flip = flips ? flips[p] : NULL;
6509         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6510         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6511       } break;
6512     case ADD_VALUES:
6513       for (p = 0; p < numPoints; p++) {
6514         const PetscInt    point = points[2*p];
6515         const PetscInt    *perm = perms ? perms[p] : NULL;
6516         const PetscScalar *flip = flips ? flips[p] : NULL;
6517         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6518         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6519       } break;
6520     case ADD_ALL_VALUES:
6521       for (p = 0; p < numPoints; p++) {
6522         const PetscInt    point = points[2*p];
6523         const PetscInt    *perm = perms ? perms[p] : NULL;
6524         const PetscScalar *flip = flips ? flips[p] : NULL;
6525         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6526         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6527       } break;
6528     default:
6529       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6530     }
6531     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6532   }
6533   /* Cleanup points */
6534   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6535   /* Cleanup array */
6536   PetscCall(VecRestoreArray(v, &array));
6537   PetscFunctionReturn(0);
6538 }
6539 
6540 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6541 {
6542   PetscMPIInt    rank;
6543   PetscInt       i, j;
6544 
6545   PetscFunctionBegin;
6546   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6547   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6548   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6549   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6550   numCIndices = numCIndices ? numCIndices : numRIndices;
6551   if (!values) PetscFunctionReturn(0);
6552   for (i = 0; i < numRIndices; i++) {
6553     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6554     for (j = 0; j < numCIndices; j++) {
6555 #if defined(PETSC_USE_COMPLEX)
6556       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j])));
6557 #else
6558       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]));
6559 #endif
6560     }
6561     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6562   }
6563   PetscFunctionReturn(0);
6564 }
6565 
6566 /*
6567   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6568 
6569   Input Parameters:
6570 + section - The section for this data layout
6571 . islocal - Is the section (and thus indices being requested) local or global?
6572 . point   - The point contributing dofs with these indices
6573 . off     - The global offset of this point
6574 . loff    - The local offset of each field
6575 . setBC   - The flag determining whether to include indices of boundary values
6576 . perm    - A permutation of the dofs on this point, or NULL
6577 - indperm - A permutation of the entire indices array, or NULL
6578 
6579   Output Parameter:
6580 . indices - Indices for dofs on this point
6581 
6582   Level: developer
6583 
6584   Note: The indices could be local or global, depending on the value of 'off'.
6585 */
6586 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6587 {
6588   PetscInt        dof;   /* The number of unknowns on this point */
6589   PetscInt        cdof;  /* The number of constraints on this point */
6590   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6591   PetscInt        cind = 0, k;
6592 
6593   PetscFunctionBegin;
6594   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6595   PetscCall(PetscSectionGetDof(section, point, &dof));
6596   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6597   if (!cdof || setBC) {
6598     for (k = 0; k < dof; ++k) {
6599       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6600       const PetscInt ind    = indperm ? indperm[preind] : preind;
6601 
6602       indices[ind] = off + k;
6603     }
6604   } else {
6605     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6606     for (k = 0; k < dof; ++k) {
6607       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6608       const PetscInt ind    = indperm ? indperm[preind] : preind;
6609 
6610       if ((cind < cdof) && (k == cdofs[cind])) {
6611         /* Insert check for returning constrained indices */
6612         indices[ind] = -(off+k+1);
6613         ++cind;
6614       } else {
6615         indices[ind] = off + k - (islocal ? 0 : cind);
6616       }
6617     }
6618   }
6619   *loff += dof;
6620   PetscFunctionReturn(0);
6621 }
6622 
6623 /*
6624  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6625 
6626  Input Parameters:
6627 + section - a section (global or local)
6628 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6629 . point - point within section
6630 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6631 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6632 . setBC - identify constrained (boundary condition) points via involution.
6633 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6634 . permsoff - offset
6635 - indperm - index permutation
6636 
6637  Output Parameter:
6638 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6639 . indices - array to hold indices (as defined by section) of each dof associated with point
6640 
6641  Notes:
6642  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6643  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6644  in the local vector.
6645 
6646  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6647  significant).  It is invalid to call with a global section and setBC=true.
6648 
6649  Developer Note:
6650  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6651  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6652  offset could be obtained from the section instead of passing it explicitly as we do now.
6653 
6654  Example:
6655  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6656  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6657  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6658  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.
6659 
6660  Level: developer
6661 */
6662 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[])
6663 {
6664   PetscInt       numFields, foff, f;
6665 
6666   PetscFunctionBegin;
6667   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6668   PetscCall(PetscSectionGetNumFields(section, &numFields));
6669   for (f = 0, foff = 0; f < numFields; ++f) {
6670     PetscInt        fdof, cfdof;
6671     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6672     PetscInt        cind = 0, b;
6673     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6674 
6675     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6676     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6677     if (!cfdof || setBC) {
6678       for (b = 0; b < fdof; ++b) {
6679         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6680         const PetscInt ind    = indperm ? indperm[preind] : preind;
6681 
6682         indices[ind] = off+foff+b;
6683       }
6684     } else {
6685       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6686       for (b = 0; b < fdof; ++b) {
6687         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6688         const PetscInt ind    = indperm ? indperm[preind] : preind;
6689 
6690         if ((cind < cfdof) && (b == fcdofs[cind])) {
6691           indices[ind] = -(off+foff+b+1);
6692           ++cind;
6693         } else {
6694           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6695         }
6696       }
6697     }
6698     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6699     foffs[f] += fdof;
6700   }
6701   PetscFunctionReturn(0);
6702 }
6703 
6704 /*
6705   This version believes the globalSection offsets for each field, rather than just the point offset
6706 
6707  . foffs - The offset into 'indices' for each field, since it is segregated by field
6708 
6709  Notes:
6710  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6711  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6712 */
6713 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6714 {
6715   PetscInt       numFields, foff, f;
6716 
6717   PetscFunctionBegin;
6718   PetscCall(PetscSectionGetNumFields(section, &numFields));
6719   for (f = 0; f < numFields; ++f) {
6720     PetscInt        fdof, cfdof;
6721     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6722     PetscInt        cind = 0, b;
6723     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6724 
6725     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6726     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6727     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6728     if (!cfdof) {
6729       for (b = 0; b < fdof; ++b) {
6730         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6731         const PetscInt ind    = indperm ? indperm[preind] : preind;
6732 
6733         indices[ind] = foff+b;
6734       }
6735     } else {
6736       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6737       for (b = 0; b < fdof; ++b) {
6738         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6739         const PetscInt ind    = indperm ? indperm[preind] : preind;
6740 
6741         if ((cind < cfdof) && (b == fcdofs[cind])) {
6742           indices[ind] = -(foff+b+1);
6743           ++cind;
6744         } else {
6745           indices[ind] = foff+b-cind;
6746         }
6747       }
6748     }
6749     foffs[f] += fdof;
6750   }
6751   PetscFunctionReturn(0);
6752 }
6753 
6754 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)
6755 {
6756   Mat             cMat;
6757   PetscSection    aSec, cSec;
6758   IS              aIS;
6759   PetscInt        aStart = -1, aEnd = -1;
6760   const PetscInt  *anchors;
6761   PetscInt        numFields, f, p, q, newP = 0;
6762   PetscInt        newNumPoints = 0, newNumIndices = 0;
6763   PetscInt        *newPoints, *indices, *newIndices;
6764   PetscInt        maxAnchor, maxDof;
6765   PetscInt        newOffsets[32];
6766   PetscInt        *pointMatOffsets[32];
6767   PetscInt        *newPointOffsets[32];
6768   PetscScalar     *pointMat[32];
6769   PetscScalar     *newValues=NULL,*tmpValues;
6770   PetscBool       anyConstrained = PETSC_FALSE;
6771 
6772   PetscFunctionBegin;
6773   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6774   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6775   PetscCall(PetscSectionGetNumFields(section, &numFields));
6776 
6777   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
6778   /* if there are point-to-point constraints */
6779   if (aSec) {
6780     PetscCall(PetscArrayzero(newOffsets, 32));
6781     PetscCall(ISGetIndices(aIS,&anchors));
6782     PetscCall(PetscSectionGetChart(aSec,&aStart,&aEnd));
6783     /* figure out how many points are going to be in the new element matrix
6784      * (we allow double counting, because it's all just going to be summed
6785      * into the global matrix anyway) */
6786     for (p = 0; p < 2*numPoints; p+=2) {
6787       PetscInt b    = points[p];
6788       PetscInt bDof = 0, bSecDof;
6789 
6790       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6791       if (!bSecDof) {
6792         continue;
6793       }
6794       if (b >= aStart && b < aEnd) {
6795         PetscCall(PetscSectionGetDof(aSec,b,&bDof));
6796       }
6797       if (bDof) {
6798         /* this point is constrained */
6799         /* it is going to be replaced by its anchors */
6800         PetscInt bOff, q;
6801 
6802         anyConstrained = PETSC_TRUE;
6803         newNumPoints  += bDof;
6804         PetscCall(PetscSectionGetOffset(aSec,b,&bOff));
6805         for (q = 0; q < bDof; q++) {
6806           PetscInt a = anchors[bOff + q];
6807           PetscInt aDof;
6808 
6809           PetscCall(PetscSectionGetDof(section,a,&aDof));
6810           newNumIndices += aDof;
6811           for (f = 0; f < numFields; ++f) {
6812             PetscInt fDof;
6813 
6814             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6815             newOffsets[f+1] += fDof;
6816           }
6817         }
6818       }
6819       else {
6820         /* this point is not constrained */
6821         newNumPoints++;
6822         newNumIndices += bSecDof;
6823         for (f = 0; f < numFields; ++f) {
6824           PetscInt fDof;
6825 
6826           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6827           newOffsets[f+1] += fDof;
6828         }
6829       }
6830     }
6831   }
6832   if (!anyConstrained) {
6833     if (outNumPoints)  *outNumPoints  = 0;
6834     if (outNumIndices) *outNumIndices = 0;
6835     if (outPoints)     *outPoints     = NULL;
6836     if (outValues)     *outValues     = NULL;
6837     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6838     PetscFunctionReturn(0);
6839   }
6840 
6841   if (outNumPoints)  *outNumPoints  = newNumPoints;
6842   if (outNumIndices) *outNumIndices = newNumIndices;
6843 
6844   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6845 
6846   if (!outPoints && !outValues) {
6847     if (offsets) {
6848       for (f = 0; f <= numFields; f++) {
6849         offsets[f] = newOffsets[f];
6850       }
6851     }
6852     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6853     PetscFunctionReturn(0);
6854   }
6855 
6856   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6857 
6858   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6859 
6860   /* workspaces */
6861   if (numFields) {
6862     for (f = 0; f < numFields; f++) {
6863       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
6864       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
6865     }
6866   }
6867   else {
6868     PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
6869     PetscCall(DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]));
6870   }
6871 
6872   /* get workspaces for the point-to-point matrices */
6873   if (numFields) {
6874     PetscInt totalOffset, totalMatOffset;
6875 
6876     for (p = 0; p < numPoints; p++) {
6877       PetscInt b    = points[2*p];
6878       PetscInt bDof = 0, bSecDof;
6879 
6880       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6881       if (!bSecDof) {
6882         for (f = 0; f < numFields; f++) {
6883           newPointOffsets[f][p + 1] = 0;
6884           pointMatOffsets[f][p + 1] = 0;
6885         }
6886         continue;
6887       }
6888       if (b >= aStart && b < aEnd) {
6889         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6890       }
6891       if (bDof) {
6892         for (f = 0; f < numFields; f++) {
6893           PetscInt fDof, q, bOff, allFDof = 0;
6894 
6895           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6896           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6897           for (q = 0; q < bDof; q++) {
6898             PetscInt a = anchors[bOff + q];
6899             PetscInt aFDof;
6900 
6901             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6902             allFDof += aFDof;
6903           }
6904           newPointOffsets[f][p+1] = allFDof;
6905           pointMatOffsets[f][p+1] = fDof * allFDof;
6906         }
6907       }
6908       else {
6909         for (f = 0; f < numFields; f++) {
6910           PetscInt fDof;
6911 
6912           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6913           newPointOffsets[f][p+1] = fDof;
6914           pointMatOffsets[f][p+1] = 0;
6915         }
6916       }
6917     }
6918     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6919       newPointOffsets[f][0] = totalOffset;
6920       pointMatOffsets[f][0] = totalMatOffset;
6921       for (p = 0; p < numPoints; p++) {
6922         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6923         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6924       }
6925       totalOffset    = newPointOffsets[f][numPoints];
6926       totalMatOffset = pointMatOffsets[f][numPoints];
6927       PetscCall(DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
6928     }
6929   }
6930   else {
6931     for (p = 0; p < numPoints; p++) {
6932       PetscInt b    = points[2*p];
6933       PetscInt bDof = 0, bSecDof;
6934 
6935       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6936       if (!bSecDof) {
6937         newPointOffsets[0][p + 1] = 0;
6938         pointMatOffsets[0][p + 1] = 0;
6939         continue;
6940       }
6941       if (b >= aStart && b < aEnd) {
6942         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6943       }
6944       if (bDof) {
6945         PetscInt bOff, q, allDof = 0;
6946 
6947         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6948         for (q = 0; q < bDof; q++) {
6949           PetscInt a = anchors[bOff + q], aDof;
6950 
6951           PetscCall(PetscSectionGetDof(section, a, &aDof));
6952           allDof += aDof;
6953         }
6954         newPointOffsets[0][p+1] = allDof;
6955         pointMatOffsets[0][p+1] = bSecDof * allDof;
6956       }
6957       else {
6958         newPointOffsets[0][p+1] = bSecDof;
6959         pointMatOffsets[0][p+1] = 0;
6960       }
6961     }
6962     newPointOffsets[0][0] = 0;
6963     pointMatOffsets[0][0] = 0;
6964     for (p = 0; p < numPoints; p++) {
6965       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6966       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6967     }
6968     PetscCall(DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
6969   }
6970 
6971   /* output arrays */
6972   PetscCall(DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
6973 
6974   /* get the point-to-point matrices; construct newPoints */
6975   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6976   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6977   PetscCall(DMGetWorkArray(dm,maxDof,MPIU_INT,&indices));
6978   PetscCall(DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
6979   if (numFields) {
6980     for (p = 0, newP = 0; p < numPoints; p++) {
6981       PetscInt b    = points[2*p];
6982       PetscInt o    = points[2*p+1];
6983       PetscInt bDof = 0, bSecDof;
6984 
6985       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6986       if (!bSecDof) {
6987         continue;
6988       }
6989       if (b >= aStart && b < aEnd) {
6990         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6991       }
6992       if (bDof) {
6993         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6994 
6995         fStart[0] = 0;
6996         fEnd[0]   = 0;
6997         for (f = 0; f < numFields; f++) {
6998           PetscInt fDof;
6999 
7000           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7001           fStart[f+1] = fStart[f] + fDof;
7002           fEnd[f+1]   = fStart[f+1];
7003         }
7004         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7005         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7006 
7007         fAnchorStart[0] = 0;
7008         fAnchorEnd[0]   = 0;
7009         for (f = 0; f < numFields; f++) {
7010           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7011 
7012           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
7013           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
7014         }
7015         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7016         for (q = 0; q < bDof; q++) {
7017           PetscInt a = anchors[bOff + q], aOff;
7018 
7019           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7020           newPoints[2*(newP + q)]     = a;
7021           newPoints[2*(newP + q) + 1] = 0;
7022           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7023           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7024         }
7025         newP += bDof;
7026 
7027         if (outValues) {
7028           /* get the point-to-point submatrix */
7029           for (f = 0; f < numFields; f++) {
7030             PetscCall(MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]));
7031           }
7032         }
7033       }
7034       else {
7035         newPoints[2 * newP]     = b;
7036         newPoints[2 * newP + 1] = o;
7037         newP++;
7038       }
7039     }
7040   } else {
7041     for (p = 0; p < numPoints; p++) {
7042       PetscInt b    = points[2*p];
7043       PetscInt o    = points[2*p+1];
7044       PetscInt bDof = 0, bSecDof;
7045 
7046       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7047       if (!bSecDof) {
7048         continue;
7049       }
7050       if (b >= aStart && b < aEnd) {
7051         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7052       }
7053       if (bDof) {
7054         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7055 
7056         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7057         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7058 
7059         PetscCall(PetscSectionGetOffset (aSec, b, &bOff));
7060         for (q = 0; q < bDof; q++) {
7061           PetscInt a = anchors[bOff + q], aOff;
7062 
7063           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7064 
7065           newPoints[2*(newP + q)]     = a;
7066           newPoints[2*(newP + q) + 1] = 0;
7067           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7068           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7069         }
7070         newP += bDof;
7071 
7072         /* get the point-to-point submatrix */
7073         if (outValues) {
7074           PetscCall(MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]));
7075         }
7076       }
7077       else {
7078         newPoints[2 * newP]     = b;
7079         newPoints[2 * newP + 1] = o;
7080         newP++;
7081       }
7082     }
7083   }
7084 
7085   if (outValues) {
7086     PetscCall(DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7087     PetscCall(PetscArrayzero(tmpValues,newNumIndices*numIndices));
7088     /* multiply constraints on the right */
7089     if (numFields) {
7090       for (f = 0; f < numFields; f++) {
7091         PetscInt oldOff = offsets[f];
7092 
7093         for (p = 0; p < numPoints; p++) {
7094           PetscInt cStart = newPointOffsets[f][p];
7095           PetscInt b      = points[2 * p];
7096           PetscInt c, r, k;
7097           PetscInt dof;
7098 
7099           PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7100           if (!dof) {
7101             continue;
7102           }
7103           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7104             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
7105             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
7106 
7107             for (r = 0; r < numIndices; r++) {
7108               for (c = 0; c < nCols; c++) {
7109                 for (k = 0; k < dof; k++) {
7110                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7111                 }
7112               }
7113             }
7114           }
7115           else {
7116             /* copy this column as is */
7117             for (r = 0; r < numIndices; r++) {
7118               for (c = 0; c < dof; c++) {
7119                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7120               }
7121             }
7122           }
7123           oldOff += dof;
7124         }
7125       }
7126     }
7127     else {
7128       PetscInt oldOff = 0;
7129       for (p = 0; p < numPoints; p++) {
7130         PetscInt cStart = newPointOffsets[0][p];
7131         PetscInt b      = points[2 * p];
7132         PetscInt c, r, k;
7133         PetscInt dof;
7134 
7135         PetscCall(PetscSectionGetDof(section,b,&dof));
7136         if (!dof) {
7137           continue;
7138         }
7139         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7140           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
7141           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
7142 
7143           for (r = 0; r < numIndices; r++) {
7144             for (c = 0; c < nCols; c++) {
7145               for (k = 0; k < dof; k++) {
7146                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7147               }
7148             }
7149           }
7150         }
7151         else {
7152           /* copy this column as is */
7153           for (r = 0; r < numIndices; r++) {
7154             for (c = 0; c < dof; c++) {
7155               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7156             }
7157           }
7158         }
7159         oldOff += dof;
7160       }
7161     }
7162 
7163     if (multiplyLeft) {
7164       PetscCall(DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues));
7165       PetscCall(PetscArrayzero(newValues,newNumIndices*newNumIndices));
7166       /* multiply constraints transpose on the left */
7167       if (numFields) {
7168         for (f = 0; f < numFields; f++) {
7169           PetscInt oldOff = offsets[f];
7170 
7171           for (p = 0; p < numPoints; p++) {
7172             PetscInt rStart = newPointOffsets[f][p];
7173             PetscInt b      = points[2 * p];
7174             PetscInt c, r, k;
7175             PetscInt dof;
7176 
7177             PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7178             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7179               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
7180               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
7181 
7182               for (r = 0; r < nRows; r++) {
7183                 for (c = 0; c < newNumIndices; c++) {
7184                   for (k = 0; k < dof; k++) {
7185                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7186                   }
7187                 }
7188               }
7189             }
7190             else {
7191               /* copy this row as is */
7192               for (r = 0; r < dof; r++) {
7193                 for (c = 0; c < newNumIndices; c++) {
7194                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7195                 }
7196               }
7197             }
7198             oldOff += dof;
7199           }
7200         }
7201       }
7202       else {
7203         PetscInt oldOff = 0;
7204 
7205         for (p = 0; p < numPoints; p++) {
7206           PetscInt rStart = newPointOffsets[0][p];
7207           PetscInt b      = points[2 * p];
7208           PetscInt c, r, k;
7209           PetscInt dof;
7210 
7211           PetscCall(PetscSectionGetDof(section,b,&dof));
7212           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7213             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
7214             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
7215 
7216             for (r = 0; r < nRows; r++) {
7217               for (c = 0; c < newNumIndices; c++) {
7218                 for (k = 0; k < dof; k++) {
7219                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7220                 }
7221               }
7222             }
7223           }
7224           else {
7225             /* copy this row as is */
7226             for (r = 0; r < dof; r++) {
7227               for (c = 0; c < newNumIndices; c++) {
7228                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7229               }
7230             }
7231           }
7232           oldOff += dof;
7233         }
7234       }
7235 
7236       PetscCall(DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7237     }
7238     else {
7239       newValues = tmpValues;
7240     }
7241   }
7242 
7243   /* clean up */
7244   PetscCall(DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices));
7245   PetscCall(DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
7246 
7247   if (numFields) {
7248     for (f = 0; f < numFields; f++) {
7249       PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
7250       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
7251       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
7252     }
7253   }
7254   else {
7255     PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
7256     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
7257     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]));
7258   }
7259   PetscCall(ISRestoreIndices(aIS,&anchors));
7260 
7261   /* output */
7262   if (outPoints) {
7263     *outPoints = newPoints;
7264   }
7265   else {
7266     PetscCall(DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
7267   }
7268   if (outValues) {
7269     *outValues = newValues;
7270   }
7271   for (f = 0; f <= numFields; f++) {
7272     offsets[f] = newOffsets[f];
7273   }
7274   PetscFunctionReturn(0);
7275 }
7276 
7277 /*@C
7278   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7279 
7280   Not collective
7281 
7282   Input Parameters:
7283 + dm         - The DM
7284 . section    - The PetscSection describing the points (a local section)
7285 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7286 . point      - The point defining the closure
7287 - useClPerm  - Use the closure point permutation if available
7288 
7289   Output Parameters:
7290 + numIndices - The number of dof indices in the closure of point with the input sections
7291 . indices    - The dof indices
7292 . outOffsets - Array to write the field offsets into, or NULL
7293 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7294 
7295   Notes:
7296   Must call DMPlexRestoreClosureIndices() to free allocated memory
7297 
7298   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7299   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7300   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7301   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7302   indices (with the above semantics) are implied.
7303 
7304   Level: advanced
7305 
7306 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7307 @*/
7308 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7309                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7310 {
7311   /* Closure ordering */
7312   PetscSection        clSection;
7313   IS                  clPoints;
7314   const PetscInt     *clp;
7315   PetscInt           *points;
7316   const PetscInt     *clperm = NULL;
7317   /* Dof permutation and sign flips */
7318   const PetscInt    **perms[32] = {NULL};
7319   const PetscScalar **flips[32] = {NULL};
7320   PetscScalar        *valCopy   = NULL;
7321   /* Hanging node constraints */
7322   PetscInt           *pointsC = NULL;
7323   PetscScalar        *valuesC = NULL;
7324   PetscInt            NclC, NiC;
7325 
7326   PetscInt           *idx;
7327   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
7328   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7329 
7330   PetscFunctionBeginHot;
7331   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7332   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7333   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7334   if (numIndices) PetscValidIntPointer(numIndices, 6);
7335   if (indices)    PetscValidPointer(indices, 7);
7336   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7337   if (values)     PetscValidPointer(values, 9);
7338   PetscCall(PetscSectionGetNumFields(section, &Nf));
7339   PetscCheck(Nf <= 31,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7340   PetscCall(PetscArrayzero(offsets, 32));
7341   /* 1) Get points in closure */
7342   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7343   if (useClPerm) {
7344     PetscInt depth, clsize;
7345     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7346     for (clsize=0,p=0; p<Ncl; p++) {
7347       PetscInt dof;
7348       PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
7349       clsize += dof;
7350     }
7351     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
7352   }
7353   /* 2) Get number of indices on these points and field offsets from section */
7354   for (p = 0; p < Ncl*2; p += 2) {
7355     PetscInt dof, fdof;
7356 
7357     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7358     for (f = 0; f < Nf; ++f) {
7359       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7360       offsets[f+1] += fdof;
7361     }
7362     Ni += dof;
7363   }
7364   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
7365   PetscCheck(!Nf || offsets[Nf] == Ni,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7366   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7367   for (f = 0; f < PetscMax(1, Nf); ++f) {
7368     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7369     else    PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7370     /* may need to apply sign changes to the element matrix */
7371     if (values && flips[f]) {
7372       PetscInt foffset = offsets[f];
7373 
7374       for (p = 0; p < Ncl; ++p) {
7375         PetscInt           pnt  = points[2*p], fdof;
7376         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7377 
7378         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7379         else     PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7380         if (flip) {
7381           PetscInt i, j, k;
7382 
7383           if (!valCopy) {
7384             PetscCall(DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7385             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7386             *values = valCopy;
7387           }
7388           for (i = 0; i < fdof; ++i) {
7389             PetscScalar fval = flip[i];
7390 
7391             for (k = 0; k < Ni; ++k) {
7392               valCopy[Ni * (foffset + i) + k] *= fval;
7393               valCopy[Ni * k + (foffset + i)] *= fval;
7394             }
7395           }
7396         }
7397         foffset += fdof;
7398       }
7399     }
7400   }
7401   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7402   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7403   if (NclC) {
7404     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7405     for (f = 0; f < PetscMax(1, Nf); ++f) {
7406       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7407       else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7408     }
7409     for (f = 0; f < PetscMax(1, Nf); ++f) {
7410       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7411       else    PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7412     }
7413     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7414     Ncl     = NclC;
7415     Ni      = NiC;
7416     points  = pointsC;
7417     if (values) *values = valuesC;
7418   }
7419   /* 5) Calculate indices */
7420   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7421   if (Nf) {
7422     PetscInt  idxOff;
7423     PetscBool useFieldOffsets;
7424 
7425     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
7426     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7427     if (useFieldOffsets) {
7428       for (p = 0; p < Ncl; ++p) {
7429         const PetscInt pnt = points[p*2];
7430 
7431         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7432       }
7433     } else {
7434       for (p = 0; p < Ncl; ++p) {
7435         const PetscInt pnt = points[p*2];
7436 
7437         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7438         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7439          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7440          * global section. */
7441         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7442       }
7443     }
7444   } else {
7445     PetscInt off = 0, idxOff;
7446 
7447     for (p = 0; p < Ncl; ++p) {
7448       const PetscInt  pnt  = points[p*2];
7449       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7450 
7451       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7452       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7453        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7454       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7455     }
7456   }
7457   /* 6) Cleanup */
7458   for (f = 0; f < PetscMax(1, Nf); ++f) {
7459     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7460     else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7461   }
7462   if (NclC) {
7463     PetscCall(DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC));
7464   } else {
7465     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7466   }
7467 
7468   if (numIndices) *numIndices = Ni;
7469   if (indices)    *indices    = idx;
7470   PetscFunctionReturn(0);
7471 }
7472 
7473 /*@C
7474   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7475 
7476   Not collective
7477 
7478   Input Parameters:
7479 + dm         - The DM
7480 . section    - The PetscSection describing the points (a local section)
7481 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7482 . point      - The point defining the closure
7483 - useClPerm  - Use the closure point permutation if available
7484 
7485   Output Parameters:
7486 + numIndices - The number of dof indices in the closure of point with the input sections
7487 . indices    - The dof indices
7488 . outOffsets - Array to write the field offsets into, or NULL
7489 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7490 
7491   Notes:
7492   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7493 
7494   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7495   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7496   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7497   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7498   indices (with the above semantics) are implied.
7499 
7500   Level: advanced
7501 
7502 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7503 @*/
7504 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7505                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7506 {
7507   PetscFunctionBegin;
7508   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7509   PetscValidPointer(indices, 7);
7510   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7511   PetscFunctionReturn(0);
7512 }
7513 
7514 /*@C
7515   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7516 
7517   Not collective
7518 
7519   Input Parameters:
7520 + dm - The DM
7521 . section - The section describing the layout in v, or NULL to use the default section
7522 . globalSection - The section describing the layout in v, or NULL to use the default global section
7523 . A - The matrix
7524 . point - The point in the DM
7525 . values - The array of values
7526 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7527 
7528   Fortran Notes:
7529   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7530 
7531   Level: intermediate
7532 
7533 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7534 @*/
7535 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7536 {
7537   DM_Plex           *mesh = (DM_Plex*) dm->data;
7538   PetscInt          *indices;
7539   PetscInt           numIndices;
7540   const PetscScalar *valuesOrig = values;
7541   PetscErrorCode     ierr;
7542 
7543   PetscFunctionBegin;
7544   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7545   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7546   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7547   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7548   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7549   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7550 
7551   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7552 
7553   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7554   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7555   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7556   if (ierr) {
7557     PetscMPIInt    rank;
7558 
7559     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7560     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7561     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7562     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7563     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7564     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7565   }
7566   if (mesh->printFEM > 1) {
7567     PetscInt i;
7568     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7569     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7570     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7571   }
7572 
7573   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7574   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7575   PetscFunctionReturn(0);
7576 }
7577 
7578 /*@C
7579   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7580 
7581   Not collective
7582 
7583   Input Parameters:
7584 + dmRow - The DM for the row fields
7585 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7586 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7587 . dmCol - The DM for the column fields
7588 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7589 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7590 . A - The matrix
7591 . point - The point in the DMs
7592 . values - The array of values
7593 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7594 
7595   Level: intermediate
7596 
7597 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7598 @*/
7599 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7600 {
7601   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7602   PetscInt          *indicesRow, *indicesCol;
7603   PetscInt           numIndicesRow, numIndicesCol;
7604   const PetscScalar *valuesOrig = values;
7605   PetscErrorCode     ierr;
7606 
7607   PetscFunctionBegin;
7608   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7609   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7610   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7611   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7612   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7613   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7614   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7615   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7616   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7617   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7618   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7619 
7620   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7621   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7622 
7623   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7624   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7625   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7626   if (ierr) {
7627     PetscMPIInt    rank;
7628 
7629     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7630     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7631     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7632     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7633     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7634     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7635   }
7636 
7637   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7638   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7639   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7640   PetscFunctionReturn(0);
7641 }
7642 
7643 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7644 {
7645   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7646   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7647   PetscInt       *cpoints = NULL;
7648   PetscInt       *findices, *cindices;
7649   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7650   PetscInt        foffsets[32], coffsets[32];
7651   DMPolytopeType  ct;
7652   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7653   PetscErrorCode  ierr;
7654 
7655   PetscFunctionBegin;
7656   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7657   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7658   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7659   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7660   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7661   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7662   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7663   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7664   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7665   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7666   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7667   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7668   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7669   PetscCall(PetscArrayzero(foffsets, 32));
7670   PetscCall(PetscArrayzero(coffsets, 32));
7671   /* Column indices */
7672   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7673   maxFPoints = numCPoints;
7674   /* Compress out points not in the section */
7675   /*   TODO: Squeeze out points with 0 dof as well */
7676   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7677   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7678     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7679       cpoints[q*2]   = cpoints[p];
7680       cpoints[q*2+1] = cpoints[p+1];
7681       ++q;
7682     }
7683   }
7684   numCPoints = q;
7685   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7686     PetscInt fdof;
7687 
7688     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7689     if (!dof) continue;
7690     for (f = 0; f < numFields; ++f) {
7691       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7692       coffsets[f+1] += fdof;
7693     }
7694     numCIndices += dof;
7695   }
7696   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7697   /* Row indices */
7698   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7699   {
7700     DMPlexTransform tr;
7701     DMPolytopeType *rct;
7702     PetscInt       *rsize, *rcone, *rornt, Nt;
7703 
7704     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7705     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7706     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7707     numSubcells = rsize[Nt-1];
7708     PetscCall(DMPlexTransformDestroy(&tr));
7709   }
7710   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7711   for (r = 0, q = 0; r < numSubcells; ++r) {
7712     /* TODO Map from coarse to fine cells */
7713     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7714     /* Compress out points not in the section */
7715     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7716     for (p = 0; p < numFPoints*2; p += 2) {
7717       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7718         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7719         if (!dof) continue;
7720         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7721         if (s < q) continue;
7722         ftotpoints[q*2]   = fpoints[p];
7723         ftotpoints[q*2+1] = fpoints[p+1];
7724         ++q;
7725       }
7726     }
7727     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7728   }
7729   numFPoints = q;
7730   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7731     PetscInt fdof;
7732 
7733     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7734     if (!dof) continue;
7735     for (f = 0; f < numFields; ++f) {
7736       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7737       foffsets[f+1] += fdof;
7738     }
7739     numFIndices += dof;
7740   }
7741   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7742 
7743   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7744   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7745   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7746   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7747   if (numFields) {
7748     const PetscInt **permsF[32] = {NULL};
7749     const PetscInt **permsC[32] = {NULL};
7750 
7751     for (f = 0; f < numFields; f++) {
7752       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7753       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7754     }
7755     for (p = 0; p < numFPoints; p++) {
7756       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7757       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7758     }
7759     for (p = 0; p < numCPoints; p++) {
7760       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7761       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7762     }
7763     for (f = 0; f < numFields; f++) {
7764       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7765       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7766     }
7767   } else {
7768     const PetscInt **permsF = NULL;
7769     const PetscInt **permsC = NULL;
7770 
7771     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7772     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7773     for (p = 0, off = 0; p < numFPoints; p++) {
7774       const PetscInt *perm = permsF ? permsF[p] : NULL;
7775 
7776       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7777       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7778     }
7779     for (p = 0, off = 0; p < numCPoints; p++) {
7780       const PetscInt *perm = permsC ? permsC[p] : NULL;
7781 
7782       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7783       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7784     }
7785     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7786     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7787   }
7788   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7789   /* TODO: flips */
7790   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7791   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7792   if (ierr) {
7793     PetscMPIInt    rank;
7794 
7795     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7796     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7797     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7798     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7799     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7800   }
7801   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7802   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7803   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7804   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7805   PetscFunctionReturn(0);
7806 }
7807 
7808 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7809 {
7810   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7811   PetscInt      *cpoints = NULL;
7812   PetscInt       foffsets[32], coffsets[32];
7813   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7814   DMPolytopeType ct;
7815   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7816 
7817   PetscFunctionBegin;
7818   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7819   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7820   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7821   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7822   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7823   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7824   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7825   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7826   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7827   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7828   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7829   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7830   PetscCall(PetscArrayzero(foffsets, 32));
7831   PetscCall(PetscArrayzero(coffsets, 32));
7832   /* Column indices */
7833   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7834   maxFPoints = numCPoints;
7835   /* Compress out points not in the section */
7836   /*   TODO: Squeeze out points with 0 dof as well */
7837   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7838   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7839     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7840       cpoints[q*2]   = cpoints[p];
7841       cpoints[q*2+1] = cpoints[p+1];
7842       ++q;
7843     }
7844   }
7845   numCPoints = q;
7846   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7847     PetscInt fdof;
7848 
7849     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7850     if (!dof) continue;
7851     for (f = 0; f < numFields; ++f) {
7852       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7853       coffsets[f+1] += fdof;
7854     }
7855     numCIndices += dof;
7856   }
7857   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7858   /* Row indices */
7859   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7860   {
7861     DMPlexTransform tr;
7862     DMPolytopeType *rct;
7863     PetscInt       *rsize, *rcone, *rornt, Nt;
7864 
7865     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7866     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7867     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7868     numSubcells = rsize[Nt-1];
7869     PetscCall(DMPlexTransformDestroy(&tr));
7870   }
7871   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7872   for (r = 0, q = 0; r < numSubcells; ++r) {
7873     /* TODO Map from coarse to fine cells */
7874     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7875     /* Compress out points not in the section */
7876     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7877     for (p = 0; p < numFPoints*2; p += 2) {
7878       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7879         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7880         if (!dof) continue;
7881         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7882         if (s < q) continue;
7883         ftotpoints[q*2]   = fpoints[p];
7884         ftotpoints[q*2+1] = fpoints[p+1];
7885         ++q;
7886       }
7887     }
7888     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7889   }
7890   numFPoints = q;
7891   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7892     PetscInt fdof;
7893 
7894     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7895     if (!dof) continue;
7896     for (f = 0; f < numFields; ++f) {
7897       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7898       foffsets[f+1] += fdof;
7899     }
7900     numFIndices += dof;
7901   }
7902   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7903 
7904   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7905   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7906   if (numFields) {
7907     const PetscInt **permsF[32] = {NULL};
7908     const PetscInt **permsC[32] = {NULL};
7909 
7910     for (f = 0; f < numFields; f++) {
7911       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7912       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7913     }
7914     for (p = 0; p < numFPoints; p++) {
7915       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7916       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7917     }
7918     for (p = 0; p < numCPoints; p++) {
7919       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7920       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7921     }
7922     for (f = 0; f < numFields; f++) {
7923       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7924       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7925     }
7926   } else {
7927     const PetscInt **permsF = NULL;
7928     const PetscInt **permsC = NULL;
7929 
7930     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7931     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7932     for (p = 0, off = 0; p < numFPoints; p++) {
7933       const PetscInt *perm = permsF ? permsF[p] : NULL;
7934 
7935       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7936       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7937     }
7938     for (p = 0, off = 0; p < numCPoints; p++) {
7939       const PetscInt *perm = permsC ? permsC[p] : NULL;
7940 
7941       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7942       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7943     }
7944     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7945     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7946   }
7947   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7948   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7949   PetscFunctionReturn(0);
7950 }
7951 
7952 /*@C
7953   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7954 
7955   Input Parameter:
7956 . dm   - The DMPlex object
7957 
7958   Output Parameter:
7959 . cellHeight - The height of a cell
7960 
7961   Level: developer
7962 
7963 .seealso `DMPlexSetVTKCellHeight()`
7964 @*/
7965 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7966 {
7967   DM_Plex *mesh = (DM_Plex*) dm->data;
7968 
7969   PetscFunctionBegin;
7970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7971   PetscValidIntPointer(cellHeight, 2);
7972   *cellHeight = mesh->vtkCellHeight;
7973   PetscFunctionReturn(0);
7974 }
7975 
7976 /*@C
7977   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7978 
7979   Input Parameters:
7980 + dm   - The DMPlex object
7981 - cellHeight - The height of a cell
7982 
7983   Level: developer
7984 
7985 .seealso `DMPlexGetVTKCellHeight()`
7986 @*/
7987 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7988 {
7989   DM_Plex *mesh = (DM_Plex*) dm->data;
7990 
7991   PetscFunctionBegin;
7992   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7993   mesh->vtkCellHeight = cellHeight;
7994   PetscFunctionReturn(0);
7995 }
7996 
7997 /*@
7998   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7999 
8000   Input Parameter:
8001 . dm - The DMPlex object
8002 
8003   Output Parameters:
8004 + gcStart - The first ghost cell, or NULL
8005 - gcEnd   - The upper bound on ghost cells, or NULL
8006 
8007   Level: advanced
8008 
8009 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8010 @*/
8011 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8012 {
8013   DMLabel        ctLabel;
8014 
8015   PetscFunctionBegin;
8016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8017   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8018   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8019   // Reset label for fast lookup
8020   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8021   PetscFunctionReturn(0);
8022 }
8023 
8024 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8025 {
8026   PetscSection   section, globalSection;
8027   PetscInt      *numbers, p;
8028 
8029   PetscFunctionBegin;
8030   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
8031   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8032   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8033   for (p = pStart; p < pEnd; ++p) {
8034     PetscCall(PetscSectionSetDof(section, p, 1));
8035   }
8036   PetscCall(PetscSectionSetUp(section));
8037   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8038   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8039   for (p = pStart; p < pEnd; ++p) {
8040     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8041     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8042     else                       numbers[p-pStart] += shift;
8043   }
8044   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8045   if (globalSize) {
8046     PetscLayout layout;
8047     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8048     PetscCall(PetscLayoutGetSize(layout, globalSize));
8049     PetscCall(PetscLayoutDestroy(&layout));
8050   }
8051   PetscCall(PetscSectionDestroy(&section));
8052   PetscCall(PetscSectionDestroy(&globalSection));
8053   PetscFunctionReturn(0);
8054 }
8055 
8056 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8057 {
8058   PetscInt       cellHeight, cStart, cEnd;
8059 
8060   PetscFunctionBegin;
8061   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8062   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8063   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8064   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8065   PetscFunctionReturn(0);
8066 }
8067 
8068 /*@
8069   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8070 
8071   Input Parameter:
8072 . dm   - The DMPlex object
8073 
8074   Output Parameter:
8075 . globalCellNumbers - Global cell numbers for all cells on this process
8076 
8077   Level: developer
8078 
8079 .seealso `DMPlexGetVertexNumbering()`
8080 @*/
8081 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8082 {
8083   DM_Plex       *mesh = (DM_Plex*) dm->data;
8084 
8085   PetscFunctionBegin;
8086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8087   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8088   *globalCellNumbers = mesh->globalCellNumbers;
8089   PetscFunctionReturn(0);
8090 }
8091 
8092 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8093 {
8094   PetscInt       vStart, vEnd;
8095 
8096   PetscFunctionBegin;
8097   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8098   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8099   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8100   PetscFunctionReturn(0);
8101 }
8102 
8103 /*@
8104   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8105 
8106   Input Parameter:
8107 . dm   - The DMPlex object
8108 
8109   Output Parameter:
8110 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8111 
8112   Level: developer
8113 
8114 .seealso `DMPlexGetCellNumbering()`
8115 @*/
8116 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8117 {
8118   DM_Plex       *mesh = (DM_Plex*) dm->data;
8119 
8120   PetscFunctionBegin;
8121   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8122   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8123   *globalVertexNumbers = mesh->globalVertexNumbers;
8124   PetscFunctionReturn(0);
8125 }
8126 
8127 /*@
8128   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
8129 
8130   Input Parameter:
8131 . dm   - The DMPlex object
8132 
8133   Output Parameter:
8134 . globalPointNumbers - Global numbers for all points on this process
8135 
8136   Level: developer
8137 
8138 .seealso `DMPlexGetCellNumbering()`
8139 @*/
8140 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8141 {
8142   IS             nums[4];
8143   PetscInt       depths[4], gdepths[4], starts[4];
8144   PetscInt       depth, d, shift = 0;
8145 
8146   PetscFunctionBegin;
8147   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8148   PetscCall(DMPlexGetDepth(dm, &depth));
8149   /* For unstratified meshes use dim instead of depth */
8150   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8151   for (d = 0; d <= depth; ++d) {
8152     PetscInt end;
8153 
8154     depths[d] = depth-d;
8155     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8156     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8157   }
8158   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8159   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8160   for (d = 0; d <= depth; ++d) {
8161     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]);
8162   }
8163   for (d = 0; d <= depth; ++d) {
8164     PetscInt pStart, pEnd, gsize;
8165 
8166     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8167     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8168     shift += gsize;
8169   }
8170   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8171   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8172   PetscFunctionReturn(0);
8173 }
8174 
8175 /*@
8176   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8177 
8178   Input Parameter:
8179 . dm - The DMPlex object
8180 
8181   Output Parameter:
8182 . ranks - The rank field
8183 
8184   Options Database Keys:
8185 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8186 
8187   Level: intermediate
8188 
8189 .seealso: `DMView()`
8190 @*/
8191 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8192 {
8193   DM             rdm;
8194   PetscFE        fe;
8195   PetscScalar   *r;
8196   PetscMPIInt    rank;
8197   DMPolytopeType ct;
8198   PetscInt       dim, cStart, cEnd, c;
8199   PetscBool      simplex;
8200 
8201   PetscFunctionBeginUser;
8202   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8203   PetscValidPointer(ranks, 2);
8204   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8205   PetscCall(DMClone(dm, &rdm));
8206   PetscCall(DMGetDimension(rdm, &dim));
8207   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8208   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8209   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8210   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8211   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8212   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8213   PetscCall(PetscFEDestroy(&fe));
8214   PetscCall(DMCreateDS(rdm));
8215   PetscCall(DMCreateGlobalVector(rdm, ranks));
8216   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8217   PetscCall(VecGetArray(*ranks, &r));
8218   for (c = cStart; c < cEnd; ++c) {
8219     PetscScalar *lr;
8220 
8221     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8222     if (lr) *lr = rank;
8223   }
8224   PetscCall(VecRestoreArray(*ranks, &r));
8225   PetscCall(DMDestroy(&rdm));
8226   PetscFunctionReturn(0);
8227 }
8228 
8229 /*@
8230   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8231 
8232   Input Parameters:
8233 + dm    - The DMPlex
8234 - label - The DMLabel
8235 
8236   Output Parameter:
8237 . val - The label value field
8238 
8239   Options Database Keys:
8240 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8241 
8242   Level: intermediate
8243 
8244 .seealso: `DMView()`
8245 @*/
8246 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8247 {
8248   DM             rdm;
8249   PetscFE        fe;
8250   PetscScalar   *v;
8251   PetscInt       dim, cStart, cEnd, c;
8252 
8253   PetscFunctionBeginUser;
8254   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8255   PetscValidPointer(label, 2);
8256   PetscValidPointer(val, 3);
8257   PetscCall(DMClone(dm, &rdm));
8258   PetscCall(DMGetDimension(rdm, &dim));
8259   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8260   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8261   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8262   PetscCall(PetscFEDestroy(&fe));
8263   PetscCall(DMCreateDS(rdm));
8264   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8265   PetscCall(DMCreateGlobalVector(rdm, val));
8266   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8267   PetscCall(VecGetArray(*val, &v));
8268   for (c = cStart; c < cEnd; ++c) {
8269     PetscScalar *lv;
8270     PetscInt     cval;
8271 
8272     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8273     PetscCall(DMLabelGetValue(label, c, &cval));
8274     *lv = cval;
8275   }
8276   PetscCall(VecRestoreArray(*val, &v));
8277   PetscCall(DMDestroy(&rdm));
8278   PetscFunctionReturn(0);
8279 }
8280 
8281 /*@
8282   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8283 
8284   Input Parameter:
8285 . dm - The DMPlex object
8286 
8287   Notes:
8288   This is a useful diagnostic when creating meshes programmatically.
8289 
8290   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8291 
8292   Level: developer
8293 
8294 .seealso: `DMCreate()`, `DMSetFromOptions()`
8295 @*/
8296 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8297 {
8298   PetscSection    coneSection, supportSection;
8299   const PetscInt *cone, *support;
8300   PetscInt        coneSize, c, supportSize, s;
8301   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8302   PetscBool       storagecheck = PETSC_TRUE;
8303 
8304   PetscFunctionBegin;
8305   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8306   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8307   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8308   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8309   /* Check that point p is found in the support of its cone points, and vice versa */
8310   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8311   for (p = pStart; p < pEnd; ++p) {
8312     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8313     PetscCall(DMPlexGetCone(dm, p, &cone));
8314     for (c = 0; c < coneSize; ++c) {
8315       PetscBool dup = PETSC_FALSE;
8316       PetscInt  d;
8317       for (d = c-1; d >= 0; --d) {
8318         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8319       }
8320       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8321       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8322       for (s = 0; s < supportSize; ++s) {
8323         if (support[s] == p) break;
8324       }
8325       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8326         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8327         for (s = 0; s < coneSize; ++s) {
8328           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8329         }
8330         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8331         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8332         for (s = 0; s < supportSize; ++s) {
8333           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8334         }
8335         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8336         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]);
8337         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8338       }
8339     }
8340     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8341     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8342     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8343     PetscCall(DMPlexGetSupport(dm, p, &support));
8344     for (s = 0; s < supportSize; ++s) {
8345       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8346       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8347       for (c = 0; c < coneSize; ++c) {
8348         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8349         if (cone[c] != pp) { c = 0; break; }
8350         if (cone[c] == p) break;
8351       }
8352       if (c >= coneSize) {
8353         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8354         for (c = 0; c < supportSize; ++c) {
8355           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8356         }
8357         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8358         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8359         for (c = 0; c < coneSize; ++c) {
8360           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8361         }
8362         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8363         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8364       }
8365     }
8366   }
8367   if (storagecheck) {
8368     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8369     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8370     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8371   }
8372   PetscFunctionReturn(0);
8373 }
8374 
8375 /*
8376   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.
8377 */
8378 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8379 {
8380   DMPolytopeType  cct;
8381   PetscInt        ptpoints[4];
8382   const PetscInt *cone, *ccone, *ptcone;
8383   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8384 
8385   PetscFunctionBegin;
8386   *unsplit = 0;
8387   switch (ct) {
8388     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8389       ptpoints[npt++] = c;
8390       break;
8391     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8392       PetscCall(DMPlexGetCone(dm, c, &cone));
8393       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8394       for (cp = 0; cp < coneSize; ++cp) {
8395         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8396         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8397       }
8398       break;
8399     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8400     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8401       PetscCall(DMPlexGetCone(dm, c, &cone));
8402       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8403       for (cp = 0; cp < coneSize; ++cp) {
8404         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8405         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8406         for (ccp = 0; ccp < cconeSize; ++ccp) {
8407           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8408           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8409             PetscInt p;
8410             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8411             if (p == npt) ptpoints[npt++] = ccone[ccp];
8412           }
8413         }
8414       }
8415       break;
8416     default: break;
8417   }
8418   for (pt = 0; pt < npt; ++pt) {
8419     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8420     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8421   }
8422   PetscFunctionReturn(0);
8423 }
8424 
8425 /*@
8426   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8427 
8428   Input Parameters:
8429 + dm - The DMPlex object
8430 - cellHeight - Normally 0
8431 
8432   Notes:
8433   This is a useful diagnostic when creating meshes programmatically.
8434   Currently applicable only to homogeneous simplex or tensor meshes.
8435 
8436   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8437 
8438   Level: developer
8439 
8440 .seealso: `DMCreate()`, `DMSetFromOptions()`
8441 @*/
8442 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8443 {
8444   DMPlexInterpolatedFlag interp;
8445   DMPolytopeType         ct;
8446   PetscInt               vStart, vEnd, cStart, cEnd, c;
8447 
8448   PetscFunctionBegin;
8449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8450   PetscCall(DMPlexIsInterpolated(dm, &interp));
8451   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8452   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8453   for (c = cStart; c < cEnd; ++c) {
8454     PetscInt *closure = NULL;
8455     PetscInt  coneSize, closureSize, cl, Nv = 0;
8456 
8457     PetscCall(DMPlexGetCellType(dm, c, &ct));
8458     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8459     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8460     if (interp == DMPLEX_INTERPOLATED_FULL) {
8461       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8462       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));
8463     }
8464     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8465     for (cl = 0; cl < closureSize*2; cl += 2) {
8466       const PetscInt p = closure[cl];
8467       if ((p >= vStart) && (p < vEnd)) ++Nv;
8468     }
8469     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8470     /* Special Case: Tensor faces with identified vertices */
8471     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8472       PetscInt unsplit;
8473 
8474       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8475       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8476     }
8477     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));
8478   }
8479   PetscFunctionReturn(0);
8480 }
8481 
8482 /*@
8483   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8484 
8485   Collective
8486 
8487   Input Parameters:
8488 + dm - The DMPlex object
8489 - cellHeight - Normally 0
8490 
8491   Notes:
8492   This is a useful diagnostic when creating meshes programmatically.
8493   This routine is only relevant for meshes that are fully interpolated across all ranks.
8494   It will error out if a partially interpolated mesh is given on some rank.
8495   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8496 
8497   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8498 
8499   Level: developer
8500 
8501 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8502 @*/
8503 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8504 {
8505   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8506   DMPlexInterpolatedFlag interpEnum;
8507 
8508   PetscFunctionBegin;
8509   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8510   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8511   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8512   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8513     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8514     PetscFunctionReturn(0);
8515   }
8516 
8517   PetscCall(DMGetDimension(dm, &dim));
8518   PetscCall(DMPlexGetDepth(dm, &depth));
8519   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8520   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8521     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8522     for (c = cStart; c < cEnd; ++c) {
8523       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8524       const DMPolytopeType *faceTypes;
8525       DMPolytopeType        ct;
8526       PetscInt              numFaces, coneSize, f;
8527       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8528 
8529       PetscCall(DMPlexGetCellType(dm, c, &ct));
8530       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8531       if (unsplit) continue;
8532       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8533       PetscCall(DMPlexGetCone(dm, c, &cone));
8534       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8535       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8536       for (cl = 0; cl < closureSize*2; cl += 2) {
8537         const PetscInt p = closure[cl];
8538         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8539       }
8540       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8541       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);
8542       for (f = 0; f < numFaces; ++f) {
8543         DMPolytopeType fct;
8544         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8545 
8546         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8547         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8548         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8549           const PetscInt p = fclosure[cl];
8550           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8551         }
8552         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]);
8553         for (v = 0; v < fnumCorners; ++v) {
8554           if (fclosure[v] != faces[fOff+v]) {
8555             PetscInt v1;
8556 
8557             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8558             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8559             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8560             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff+v1]));
8561             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8562             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]);
8563           }
8564         }
8565         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8566         fOff += faceSizes[f];
8567       }
8568       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8569       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8570     }
8571   }
8572   PetscFunctionReturn(0);
8573 }
8574 
8575 /*@
8576   DMPlexCheckGeometry - Check the geometry of mesh cells
8577 
8578   Input Parameter:
8579 . dm - The DMPlex object
8580 
8581   Notes:
8582   This is a useful diagnostic when creating meshes programmatically.
8583 
8584   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8585 
8586   Level: developer
8587 
8588 .seealso: `DMCreate()`, `DMSetFromOptions()`
8589 @*/
8590 PetscErrorCode DMPlexCheckGeometry(DM dm)
8591 {
8592   Vec       coordinates;
8593   PetscReal detJ, J[9], refVol = 1.0;
8594   PetscReal vol;
8595   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8596 
8597   PetscFunctionBegin;
8598   PetscCall(DMGetDimension(dm, &dim));
8599   PetscCall(DMGetCoordinateDim(dm, &dE));
8600   if (dim != dE) PetscFunctionReturn(0);
8601   PetscCall(DMPlexGetDepth(dm, &depth));
8602   for (d = 0; d < dim; ++d) refVol *= 2.0;
8603   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8604   /* Make sure local coordinates are created, because that step is collective */
8605   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8606   for (c = cStart; c < cEnd; ++c) {
8607     DMPolytopeType ct;
8608     PetscInt       unsplit;
8609     PetscBool      ignoreZeroVol = PETSC_FALSE;
8610 
8611     PetscCall(DMPlexGetCellType(dm, c, &ct));
8612     switch (ct) {
8613       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8614       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8615       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8616         ignoreZeroVol = PETSC_TRUE; break;
8617       default: break;
8618     }
8619     switch (ct) {
8620       case DM_POLYTOPE_TRI_PRISM:
8621       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8622       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8623       case DM_POLYTOPE_PYRAMID:
8624         continue;
8625       default: break;
8626     }
8627     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8628     if (unsplit) continue;
8629     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8630     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);
8631     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ*refVol)));
8632     /* This should work with periodicity since DG coordinates should be used */
8633     if (depth > 1) {
8634       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8635       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);
8636       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double) vol));
8637     }
8638   }
8639   PetscFunctionReturn(0);
8640 }
8641 
8642 /*@
8643   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8644 
8645   Collective
8646 
8647   Input Parameters:
8648 + dm - The DMPlex object
8649 - pointSF - The Point SF, or NULL for Point SF attached to DM
8650 
8651   Notes:
8652   This is mainly intended for debugging/testing purposes.
8653 
8654   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8655 
8656   Level: developer
8657 
8658 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8659 @*/
8660 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF)
8661 {
8662   PetscInt        l, nleaves, nroots, overlap;
8663   const PetscInt *locals;
8664   const PetscSFNode *remotes;
8665   PetscBool       distributed;
8666   MPI_Comm        comm;
8667   PetscMPIInt     rank;
8668 
8669   PetscFunctionBegin;
8670   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8671   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8672   else         pointSF = dm->sf;
8673   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8674   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8675   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8676   {
8677     PetscMPIInt    mpiFlag;
8678 
8679     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF),&mpiFlag));
8680     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)",mpiFlag);
8681   }
8682   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8683   PetscCall(DMPlexIsDistributed(dm, &distributed));
8684   if (!distributed) {
8685     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);
8686     PetscFunctionReturn(0);
8687   }
8688   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);
8689   PetscCall(DMPlexGetOverlap(dm, &overlap));
8690 
8691   /* Check SF graph is compatible with DMPlex chart */
8692   {
8693     PetscInt pStart, pEnd, maxLeaf;
8694 
8695     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8696     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8697     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd-pStart, nroots);
8698     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8699   }
8700 
8701   /* Check Point SF has no local points referenced */
8702   for (l = 0; l < nleaves; l++) {
8703     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);
8704   }
8705 
8706   /* Check there are no cells in interface */
8707   if (!overlap) {
8708     PetscInt cellHeight, cStart, cEnd;
8709 
8710     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8711     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8712     for (l = 0; l < nleaves; ++l) {
8713       const PetscInt point = locals ? locals[l] : l;
8714 
8715       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8716     }
8717   }
8718 
8719   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8720   {
8721     const PetscInt *rootdegree;
8722 
8723     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8724     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8725     for (l = 0; l < nleaves; ++l) {
8726       const PetscInt  point = locals ? locals[l] : l;
8727       const PetscInt *cone;
8728       PetscInt        coneSize, c, idx;
8729 
8730       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8731       PetscCall(DMPlexGetCone(dm, point, &cone));
8732       for (c = 0; c < coneSize; ++c) {
8733         if (!rootdegree[cone[c]]) {
8734           if (locals) {
8735             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8736           } else {
8737             idx = (cone[c] < nleaves) ? cone[c] : -1;
8738           }
8739           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8740         }
8741       }
8742     }
8743   }
8744   PetscFunctionReturn(0);
8745 }
8746 
8747 /*@
8748   DMPlexCheck - Perform various checks of Plex sanity
8749 
8750   Input Parameter:
8751 . dm - The DMPlex object
8752 
8753   Notes:
8754   This is a useful diagnostic when creating meshes programmatically.
8755 
8756   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8757 
8758   Currently does not include DMPlexCheckCellShape().
8759 
8760   Level: developer
8761 
8762 .seealso: DMCreate(), DMSetFromOptions()
8763 @*/
8764 PetscErrorCode DMPlexCheck(DM dm)
8765 {
8766   PetscInt cellHeight;
8767 
8768   PetscFunctionBegin;
8769   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8770   PetscCall(DMPlexCheckSymmetry(dm));
8771   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8772   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8773   PetscCall(DMPlexCheckGeometry(dm));
8774   PetscCall(DMPlexCheckPointSF(dm, NULL));
8775   PetscCall(DMPlexCheckInterfaceCones(dm));
8776   PetscFunctionReturn(0);
8777 }
8778 
8779 typedef struct cell_stats
8780 {
8781   PetscReal min, max, sum, squaresum;
8782   PetscInt  count;
8783 } cell_stats_t;
8784 
8785 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8786 {
8787   PetscInt i, N = *len;
8788 
8789   for (i = 0; i < N; i++) {
8790     cell_stats_t *A = (cell_stats_t *) a;
8791     cell_stats_t *B = (cell_stats_t *) b;
8792 
8793     B->min = PetscMin(A->min,B->min);
8794     B->max = PetscMax(A->max,B->max);
8795     B->sum += A->sum;
8796     B->squaresum += A->squaresum;
8797     B->count += A->count;
8798   }
8799 }
8800 
8801 /*@
8802   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8803 
8804   Collective on dm
8805 
8806   Input Parameters:
8807 + dm        - The DMPlex object
8808 . output    - If true, statistics will be displayed on stdout
8809 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8810 
8811   Notes:
8812   This is mainly intended for debugging/testing purposes.
8813 
8814   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8815 
8816   Level: developer
8817 
8818 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8819 @*/
8820 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8821 {
8822   DM             dmCoarse;
8823   cell_stats_t   stats, globalStats;
8824   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8825   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8826   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8827   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8828   PetscMPIInt    rank,size;
8829 
8830   PetscFunctionBegin;
8831   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8832   stats.min   = PETSC_MAX_REAL;
8833   stats.max   = PETSC_MIN_REAL;
8834   stats.sum   = stats.squaresum = 0.;
8835   stats.count = 0;
8836 
8837   PetscCallMPI(MPI_Comm_size(comm, &size));
8838   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8839   PetscCall(DMGetCoordinateDim(dm,&cdim));
8840   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8841   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8842   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8843   for (c = cStart; c < cEnd; c++) {
8844     PetscInt  i;
8845     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8846 
8847     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8848     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8849     for (i = 0; i < PetscSqr(cdim); ++i) {
8850       frobJ    += J[i] * J[i];
8851       frobInvJ += invJ[i] * invJ[i];
8852     }
8853     cond2 = frobJ * frobInvJ;
8854     cond  = PetscSqrtReal(cond2);
8855 
8856     stats.min        = PetscMin(stats.min,cond);
8857     stats.max        = PetscMax(stats.max,cond);
8858     stats.sum       += cond;
8859     stats.squaresum += cond2;
8860     stats.count++;
8861     if (output && cond > limit) {
8862       PetscSection coordSection;
8863       Vec          coordsLocal;
8864       PetscScalar *coords = NULL;
8865       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8866 
8867       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8868       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8869       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8870       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double) cond));
8871       for (i = 0; i < Nv/cdim; ++i) {
8872         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8873         for (d = 0; d < cdim; ++d) {
8874           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8875           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8876         }
8877         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8878       }
8879       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8880       for (cl = 0; cl < clSize*2; cl += 2) {
8881         const PetscInt edge = closure[cl];
8882 
8883         if ((edge >= eStart) && (edge < eEnd)) {
8884           PetscReal len;
8885 
8886           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8887           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double) len));
8888         }
8889       }
8890       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8891       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8892     }
8893   }
8894   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8895 
8896   if (size > 1) {
8897     PetscMPIInt   blockLengths[2] = {4,1};
8898     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8899     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8900     MPI_Op        statReduce;
8901 
8902     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8903     PetscCallMPI(MPI_Type_commit(&statType));
8904     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8905     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8906     PetscCallMPI(MPI_Op_free(&statReduce));
8907     PetscCallMPI(MPI_Type_free(&statType));
8908   } else {
8909     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8910   }
8911   if (rank == 0) {
8912     count = globalStats.count;
8913     min   = globalStats.min;
8914     max   = globalStats.max;
8915     mean  = globalStats.sum / globalStats.count;
8916     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8917   }
8918 
8919   if (output) {
8920     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));
8921   }
8922   PetscCall(PetscFree2(J,invJ));
8923 
8924   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8925   if (dmCoarse) {
8926     PetscBool isplex;
8927 
8928     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8929     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8930   }
8931   PetscFunctionReturn(0);
8932 }
8933 
8934 /*@
8935   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8936   orthogonal quality below given tolerance.
8937 
8938   Collective on dm
8939 
8940   Input Parameters:
8941 + dm   - The DMPlex object
8942 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8943 - atol - [0, 1] Absolute tolerance for tagging cells.
8944 
8945   Output Parameters:
8946 + OrthQual      - Vec containing orthogonal quality per cell
8947 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8948 
8949   Options Database Keys:
8950 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8951 supported.
8952 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8953 
8954   Notes:
8955   Orthogonal quality is given by the following formula:
8956 
8957   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8958 
8959   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
8960   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8961   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8962   calculating the cosine of the angle between these vectors.
8963 
8964   Orthogonal quality ranges from 1 (best) to 0 (worst).
8965 
8966   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8967   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8968 
8969   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8970 
8971   Level: intermediate
8972 
8973 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8974 @*/
8975 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8976 {
8977   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8978   PetscInt                *idx;
8979   PetscScalar             *oqVals;
8980   const PetscScalar       *cellGeomArr, *faceGeomArr;
8981   PetscReal               *ci, *fi, *Ai;
8982   MPI_Comm                comm;
8983   Vec                     cellgeom, facegeom;
8984   DM                      dmFace, dmCell;
8985   IS                      glob;
8986   ISLocalToGlobalMapping  ltog;
8987   PetscViewer             vwr;
8988 
8989   PetscFunctionBegin;
8990   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8991   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
8992   PetscValidPointer(OrthQual, 4);
8993   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
8994   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
8995   PetscCall(DMGetDimension(dm, &nc));
8996   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
8997   {
8998     DMPlexInterpolatedFlag interpFlag;
8999 
9000     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9001     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9002       PetscMPIInt rank;
9003 
9004       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9005       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9006     }
9007   }
9008   if (OrthQualLabel) {
9009     PetscValidPointer(OrthQualLabel, 5);
9010     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9011     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9012   } else {*OrthQualLabel = NULL;}
9013   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9014   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9015   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9016   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9017   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9018   PetscCall(VecCreate(comm, OrthQual));
9019   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9020   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
9021   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9022   PetscCall(VecSetUp(*OrthQual));
9023   PetscCall(ISDestroy(&glob));
9024   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9025   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9026   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9027   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9028   PetscCall(VecGetDM(cellgeom, &dmCell));
9029   PetscCall(VecGetDM(facegeom, &dmFace));
9030   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9031   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
9032     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9033     PetscInt           cellarr[2], *adj = NULL;
9034     PetscScalar        *cArr, *fArr;
9035     PetscReal          minvalc = 1.0, minvalf = 1.0;
9036     PetscFVCellGeom    *cg;
9037 
9038     idx[cellIter] = cell-cStart;
9039     cellarr[0] = cell;
9040     /* Make indexing into cellGeom easier */
9041     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9042     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9043     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9044     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9045     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
9046       PetscInt         i;
9047       const PetscInt   neigh = adj[cellneigh];
9048       PetscReal        normci = 0, normfi = 0, normai = 0;
9049       PetscFVCellGeom  *cgneigh;
9050       PetscFVFaceGeom  *fg;
9051 
9052       /* Don't count ourselves in the neighbor list */
9053       if (neigh == cell) continue;
9054       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9055       cellarr[1] = neigh;
9056       {
9057         PetscInt       numcovpts;
9058         const PetscInt *covpts;
9059 
9060         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9061         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9062         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9063       }
9064 
9065       /* Compute c_i, f_i and their norms */
9066       for (i = 0; i < nc; i++) {
9067         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9068         fi[i] = fg->centroid[i] - cg->centroid[i];
9069         Ai[i] = fg->normal[i];
9070         normci += PetscPowReal(ci[i], 2);
9071         normfi += PetscPowReal(fi[i], 2);
9072         normai += PetscPowReal(Ai[i], 2);
9073       }
9074       normci = PetscSqrtReal(normci);
9075       normfi = PetscSqrtReal(normfi);
9076       normai = PetscSqrtReal(normai);
9077 
9078       /* Normalize and compute for each face-cell-normal pair */
9079       for (i = 0; i < nc; i++) {
9080         ci[i] = ci[i]/normci;
9081         fi[i] = fi[i]/normfi;
9082         Ai[i] = Ai[i]/normai;
9083         /* PetscAbs because I don't know if normals are guaranteed to point out */
9084         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9085         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9086       }
9087       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9088         minvalc = PetscRealPart(cArr[cellneighiter]);
9089       }
9090       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9091         minvalf = PetscRealPart(fArr[cellneighiter]);
9092       }
9093     }
9094     PetscCall(PetscFree(adj));
9095     PetscCall(PetscFree2(cArr, fArr));
9096     /* Defer to cell if they're equal */
9097     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9098     if (OrthQualLabel) {
9099       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9100     }
9101   }
9102   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9103   PetscCall(VecAssemblyBegin(*OrthQual));
9104   PetscCall(VecAssemblyEnd(*OrthQual));
9105   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9106   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9107   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9108   if (OrthQualLabel) {
9109     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9110   }
9111   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9112   PetscCall(PetscViewerDestroy(&vwr));
9113   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9114   PetscFunctionReturn(0);
9115 }
9116 
9117 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9118  * interpolator construction */
9119 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9120 {
9121   PetscSection   section, newSection, gsection;
9122   PetscSF        sf;
9123   PetscBool      hasConstraints, ghasConstraints;
9124 
9125   PetscFunctionBegin;
9126   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9127   PetscValidPointer(odm,2);
9128   PetscCall(DMGetLocalSection(dm, &section));
9129   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9130   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9131   if (!ghasConstraints) {
9132     PetscCall(PetscObjectReference((PetscObject)dm));
9133     *odm = dm;
9134     PetscFunctionReturn(0);
9135   }
9136   PetscCall(DMClone(dm, odm));
9137   PetscCall(DMCopyFields(dm, *odm));
9138   PetscCall(DMGetLocalSection(*odm, &newSection));
9139   PetscCall(DMGetPointSF(*odm, &sf));
9140   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9141   PetscCall(DMSetGlobalSection(*odm, gsection));
9142   PetscCall(PetscSectionDestroy(&gsection));
9143   PetscFunctionReturn(0);
9144 }
9145 
9146 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9147 {
9148   DM             dmco, dmfo;
9149   Mat            interpo;
9150   Vec            rscale;
9151   Vec            cglobalo, clocal;
9152   Vec            fglobal, fglobalo, flocal;
9153   PetscBool      regular;
9154 
9155   PetscFunctionBegin;
9156   PetscCall(DMGetFullDM(dmc, &dmco));
9157   PetscCall(DMGetFullDM(dmf, &dmfo));
9158   PetscCall(DMSetCoarseDM(dmfo, dmco));
9159   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9160   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9161   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9162   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9163   PetscCall(DMCreateLocalVector(dmc, &clocal));
9164   PetscCall(VecSet(cglobalo, 0.));
9165   PetscCall(VecSet(clocal, 0.));
9166   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9167   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9168   PetscCall(DMCreateLocalVector(dmf, &flocal));
9169   PetscCall(VecSet(fglobal, 0.));
9170   PetscCall(VecSet(fglobalo, 0.));
9171   PetscCall(VecSet(flocal, 0.));
9172   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9173   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9174   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9175   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9176   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9177   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9178   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9179   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9180   *shift = fglobal;
9181   PetscCall(VecDestroy(&flocal));
9182   PetscCall(VecDestroy(&fglobalo));
9183   PetscCall(VecDestroy(&clocal));
9184   PetscCall(VecDestroy(&cglobalo));
9185   PetscCall(VecDestroy(&rscale));
9186   PetscCall(MatDestroy(&interpo));
9187   PetscCall(DMDestroy(&dmfo));
9188   PetscCall(DMDestroy(&dmco));
9189   PetscFunctionReturn(0);
9190 }
9191 
9192 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9193 {
9194   PetscObject    shifto;
9195   Vec            shift;
9196 
9197   PetscFunctionBegin;
9198   if (!interp) {
9199     Vec rscale;
9200 
9201     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9202     PetscCall(VecDestroy(&rscale));
9203   } else {
9204     PetscCall(PetscObjectReference((PetscObject)interp));
9205   }
9206   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9207   if (!shifto) {
9208     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9209     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9210     shifto = (PetscObject) shift;
9211     PetscCall(VecDestroy(&shift));
9212   }
9213   shift = (Vec) shifto;
9214   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9215   PetscCall(VecAXPY(fineSol, 1.0, shift));
9216   PetscCall(MatDestroy(&interp));
9217   PetscFunctionReturn(0);
9218 }
9219 
9220 /* Pointwise interpolation
9221      Just code FEM for now
9222      u^f = I u^c
9223      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9224      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9225      I_{ij} = psi^f_i phi^c_j
9226 */
9227 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9228 {
9229   PetscSection   gsc, gsf;
9230   PetscInt       m, n;
9231   void          *ctx;
9232   DM             cdm;
9233   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9234 
9235   PetscFunctionBegin;
9236   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9237   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9238   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9239   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9240 
9241   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9242   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9243   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9244   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9245   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9246 
9247   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9248   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9249   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9250   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9251   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9252   if (scaling) {
9253     /* Use naive scaling */
9254     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9255   }
9256   PetscFunctionReturn(0);
9257 }
9258 
9259 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9260 {
9261   VecScatter     ctx;
9262 
9263   PetscFunctionBegin;
9264   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9265   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9266   PetscCall(VecScatterDestroy(&ctx));
9267   PetscFunctionReturn(0);
9268 }
9269 
9270 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9271                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9272                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9273                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9274 {
9275   const PetscInt Nc = uOff[1] - uOff[0];
9276   PetscInt       c;
9277   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9278 }
9279 
9280 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9281 {
9282   DM             dmc;
9283   PetscDS        ds;
9284   Vec            ones, locmass;
9285   IS             cellIS;
9286   PetscFormKey   key;
9287   PetscInt       depth;
9288 
9289   PetscFunctionBegin;
9290   PetscCall(DMClone(dm, &dmc));
9291   PetscCall(DMCopyDisc(dm, dmc));
9292   PetscCall(DMGetDS(dmc, &ds));
9293   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9294   PetscCall(DMCreateGlobalVector(dmc, mass));
9295   PetscCall(DMGetLocalVector(dmc, &ones));
9296   PetscCall(DMGetLocalVector(dmc, &locmass));
9297   PetscCall(DMPlexGetDepth(dmc, &depth));
9298   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9299   PetscCall(VecSet(locmass, 0.0));
9300   PetscCall(VecSet(ones, 1.0));
9301   key.label = NULL;
9302   key.value = 0;
9303   key.field = 0;
9304   key.part  = 0;
9305   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9306   PetscCall(ISDestroy(&cellIS));
9307   PetscCall(VecSet(*mass, 0.0));
9308   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9309   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9310   PetscCall(DMRestoreLocalVector(dmc, &ones));
9311   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9312   PetscCall(DMDestroy(&dmc));
9313   PetscFunctionReturn(0);
9314 }
9315 
9316 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9317 {
9318   PetscSection   gsc, gsf;
9319   PetscInt       m, n;
9320   void          *ctx;
9321   DM             cdm;
9322   PetscBool      regular;
9323 
9324   PetscFunctionBegin;
9325   if (dmFine == dmCoarse) {
9326     DM            dmc;
9327     PetscDS       ds;
9328     PetscWeakForm wf;
9329     Vec           u;
9330     IS            cellIS;
9331     PetscFormKey  key;
9332     PetscInt      depth;
9333 
9334     PetscCall(DMClone(dmFine, &dmc));
9335     PetscCall(DMCopyDisc(dmFine, dmc));
9336     PetscCall(DMGetDS(dmc, &ds));
9337     PetscCall(PetscDSGetWeakForm(ds, &wf));
9338     PetscCall(PetscWeakFormClear(wf));
9339     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9340     PetscCall(DMCreateMatrix(dmc, mass));
9341     PetscCall(DMGetLocalVector(dmc, &u));
9342     PetscCall(DMPlexGetDepth(dmc, &depth));
9343     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9344     PetscCall(MatZeroEntries(*mass));
9345     key.label = NULL;
9346     key.value = 0;
9347     key.field = 0;
9348     key.part  = 0;
9349     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9350     PetscCall(ISDestroy(&cellIS));
9351     PetscCall(DMRestoreLocalVector(dmc, &u));
9352     PetscCall(DMDestroy(&dmc));
9353   } else {
9354     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9355     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9356     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9357     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9358 
9359     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9360     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9361     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9362     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9363 
9364     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9365     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9366     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9367     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9368   }
9369   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9370   PetscFunctionReturn(0);
9371 }
9372 
9373 /*@
9374   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9375 
9376   Input Parameter:
9377 . dm - The DMPlex object
9378 
9379   Output Parameter:
9380 . regular - The flag
9381 
9382   Level: intermediate
9383 
9384 .seealso: `DMPlexSetRegularRefinement()`
9385 @*/
9386 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9387 {
9388   PetscFunctionBegin;
9389   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9390   PetscValidBoolPointer(regular, 2);
9391   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9392   PetscFunctionReturn(0);
9393 }
9394 
9395 /*@
9396   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9397 
9398   Input Parameters:
9399 + dm - The DMPlex object
9400 - regular - The flag
9401 
9402   Level: intermediate
9403 
9404 .seealso: `DMPlexGetRegularRefinement()`
9405 @*/
9406 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9407 {
9408   PetscFunctionBegin;
9409   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9410   ((DM_Plex *) dm->data)->regularRefinement = regular;
9411   PetscFunctionReturn(0);
9412 }
9413 
9414 /* anchors */
9415 /*@
9416   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9417   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9418 
9419   not collective
9420 
9421   Input Parameter:
9422 . dm - The DMPlex object
9423 
9424   Output Parameters:
9425 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9426 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9427 
9428   Level: intermediate
9429 
9430 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9431 @*/
9432 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9433 {
9434   DM_Plex *plex = (DM_Plex *)dm->data;
9435 
9436   PetscFunctionBegin;
9437   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9438   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9439   if (anchorSection) *anchorSection = plex->anchorSection;
9440   if (anchorIS) *anchorIS = plex->anchorIS;
9441   PetscFunctionReturn(0);
9442 }
9443 
9444 /*@
9445   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9446   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9447   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9448 
9449   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9450   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9451 
9452   collective on dm
9453 
9454   Input Parameters:
9455 + dm - The DMPlex object
9456 . 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).
9457 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9458 
9459   The reference counts of anchorSection and anchorIS are incremented.
9460 
9461   Level: intermediate
9462 
9463 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9464 @*/
9465 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9466 {
9467   DM_Plex        *plex = (DM_Plex *)dm->data;
9468   PetscMPIInt    result;
9469 
9470   PetscFunctionBegin;
9471   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9472   if (anchorSection) {
9473     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9474     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9475     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9476   }
9477   if (anchorIS) {
9478     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9479     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9480     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9481   }
9482 
9483   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9484   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9485   plex->anchorSection = anchorSection;
9486 
9487   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9488   PetscCall(ISDestroy(&plex->anchorIS));
9489   plex->anchorIS = anchorIS;
9490 
9491   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9492     PetscInt size, a, pStart, pEnd;
9493     const PetscInt *anchors;
9494 
9495     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9496     PetscCall(ISGetLocalSize(anchorIS,&size));
9497     PetscCall(ISGetIndices(anchorIS,&anchors));
9498     for (a = 0; a < size; a++) {
9499       PetscInt p;
9500 
9501       p = anchors[a];
9502       if (p >= pStart && p < pEnd) {
9503         PetscInt dof;
9504 
9505         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9506         if (dof) {
9507 
9508           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9509           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %" PetscInt_FMT " cannot be constrained and an anchor",p);
9510         }
9511       }
9512     }
9513     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9514   }
9515   /* reset the generic constraints */
9516   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9517   PetscFunctionReturn(0);
9518 }
9519 
9520 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9521 {
9522   PetscSection anchorSection;
9523   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9524 
9525   PetscFunctionBegin;
9526   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9527   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9528   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9529   PetscCall(PetscSectionGetNumFields(section,&numFields));
9530   if (numFields) {
9531     PetscInt f;
9532     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9533 
9534     for (f = 0; f < numFields; f++) {
9535       PetscInt numComp;
9536 
9537       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9538       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9539     }
9540   }
9541   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9542   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9543   pStart = PetscMax(pStart,sStart);
9544   pEnd   = PetscMin(pEnd,sEnd);
9545   pEnd   = PetscMax(pStart,pEnd);
9546   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9547   for (p = pStart; p < pEnd; p++) {
9548     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9549     if (dof) {
9550       PetscCall(PetscSectionGetDof(section,p,&dof));
9551       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9552       for (f = 0; f < numFields; f++) {
9553         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9554         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9555       }
9556     }
9557   }
9558   PetscCall(PetscSectionSetUp(*cSec));
9559   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9560   PetscFunctionReturn(0);
9561 }
9562 
9563 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9564 {
9565   PetscSection   aSec;
9566   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9567   const PetscInt *anchors;
9568   PetscInt       numFields, f;
9569   IS             aIS;
9570   MatType        mtype;
9571   PetscBool      iscuda,iskokkos;
9572 
9573   PetscFunctionBegin;
9574   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9575   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9576   PetscCall(PetscSectionGetStorageSize(section, &n));
9577   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9578   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9579   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9580   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9581   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9582   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9583   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9584   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9585   else mtype = MATSEQAIJ;
9586   PetscCall(MatSetType(*cMat,mtype));
9587   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9588   PetscCall(ISGetIndices(aIS,&anchors));
9589   /* cSec will be a subset of aSec and section */
9590   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9591   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9592   PetscCall(PetscMalloc1(m+1,&i));
9593   i[0] = 0;
9594   PetscCall(PetscSectionGetNumFields(section,&numFields));
9595   for (p = pStart; p < pEnd; p++) {
9596     PetscInt rDof, rOff, r;
9597 
9598     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9599     if (!rDof) continue;
9600     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9601     if (numFields) {
9602       for (f = 0; f < numFields; f++) {
9603         annz = 0;
9604         for (r = 0; r < rDof; r++) {
9605           a = anchors[rOff + r];
9606           if (a < sStart || a >= sEnd) continue;
9607           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9608           annz += aDof;
9609         }
9610         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9611         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9612         for (q = 0; q < dof; q++) {
9613           i[off + q + 1] = i[off + q] + annz;
9614         }
9615       }
9616     } else {
9617       annz = 0;
9618       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9619       for (q = 0; q < dof; q++) {
9620         a = anchors[rOff + q];
9621         if (a < sStart || a >= sEnd) continue;
9622         PetscCall(PetscSectionGetDof(section,a,&aDof));
9623         annz += aDof;
9624       }
9625       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9626       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9627       for (q = 0; q < dof; q++) {
9628         i[off + q + 1] = i[off + q] + annz;
9629       }
9630     }
9631   }
9632   nnz = i[m];
9633   PetscCall(PetscMalloc1(nnz,&j));
9634   offset = 0;
9635   for (p = pStart; p < pEnd; p++) {
9636     if (numFields) {
9637       for (f = 0; f < numFields; f++) {
9638         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9639         for (q = 0; q < dof; q++) {
9640           PetscInt rDof, rOff, r;
9641           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9642           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9643           for (r = 0; r < rDof; r++) {
9644             PetscInt s;
9645 
9646             a = anchors[rOff + r];
9647             if (a < sStart || a >= sEnd) continue;
9648             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9649             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9650             for (s = 0; s < aDof; s++) {
9651               j[offset++] = aOff + s;
9652             }
9653           }
9654         }
9655       }
9656     } else {
9657       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9658       for (q = 0; q < dof; q++) {
9659         PetscInt rDof, rOff, r;
9660         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9661         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9662         for (r = 0; r < rDof; r++) {
9663           PetscInt s;
9664 
9665           a = anchors[rOff + r];
9666           if (a < sStart || a >= sEnd) continue;
9667           PetscCall(PetscSectionGetDof(section,a,&aDof));
9668           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9669           for (s = 0; s < aDof; s++) {
9670             j[offset++] = aOff + s;
9671           }
9672         }
9673       }
9674     }
9675   }
9676   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9677   PetscCall(PetscFree(i));
9678   PetscCall(PetscFree(j));
9679   PetscCall(ISRestoreIndices(aIS,&anchors));
9680   PetscFunctionReturn(0);
9681 }
9682 
9683 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9684 {
9685   DM_Plex        *plex = (DM_Plex *)dm->data;
9686   PetscSection   anchorSection, section, cSec;
9687   Mat            cMat;
9688 
9689   PetscFunctionBegin;
9690   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9691   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9692   if (anchorSection) {
9693     PetscInt Nf;
9694 
9695     PetscCall(DMGetLocalSection(dm,&section));
9696     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9697     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9698     PetscCall(DMGetNumFields(dm,&Nf));
9699     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9700     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9701     PetscCall(PetscSectionDestroy(&cSec));
9702     PetscCall(MatDestroy(&cMat));
9703   }
9704   PetscFunctionReturn(0);
9705 }
9706 
9707 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9708 {
9709   IS             subis;
9710   PetscSection   section, subsection;
9711 
9712   PetscFunctionBegin;
9713   PetscCall(DMGetLocalSection(dm, &section));
9714   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9715   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9716   /* Create subdomain */
9717   PetscCall(DMPlexFilter(dm, label, value, subdm));
9718   /* Create submodel */
9719   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9720   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9721   PetscCall(DMSetLocalSection(*subdm, subsection));
9722   PetscCall(PetscSectionDestroy(&subsection));
9723   PetscCall(DMCopyDisc(dm, *subdm));
9724   /* Create map from submodel to global model */
9725   if (is) {
9726     PetscSection    sectionGlobal, subsectionGlobal;
9727     IS              spIS;
9728     const PetscInt *spmap;
9729     PetscInt       *subIndices;
9730     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9731     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9732 
9733     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9734     PetscCall(ISGetIndices(spIS, &spmap));
9735     PetscCall(PetscSectionGetNumFields(section, &Nf));
9736     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9737     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9738     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9739     for (p = pStart; p < pEnd; ++p) {
9740       PetscInt gdof, pSubSize  = 0;
9741 
9742       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9743       if (gdof > 0) {
9744         for (f = 0; f < Nf; ++f) {
9745           PetscInt fdof, fcdof;
9746 
9747           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9748           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9749           pSubSize += fdof-fcdof;
9750         }
9751         subSize += pSubSize;
9752         if (pSubSize) {
9753           if (bs < 0) {
9754             bs = pSubSize;
9755           } else if (bs != pSubSize) {
9756             /* Layout does not admit a pointwise block size */
9757             bs = 1;
9758           }
9759         }
9760       }
9761     }
9762     /* Must have same blocksize on all procs (some might have no points) */
9763     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9764     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9765     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9766     else                            {bs = bsMinMax[0];}
9767     PetscCall(PetscMalloc1(subSize, &subIndices));
9768     for (p = pStart; p < pEnd; ++p) {
9769       PetscInt gdof, goff;
9770 
9771       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9772       if (gdof > 0) {
9773         const PetscInt point = spmap[p];
9774 
9775         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9776         for (f = 0; f < Nf; ++f) {
9777           PetscInt fdof, fcdof, fc, f2, poff = 0;
9778 
9779           /* Can get rid of this loop by storing field information in the global section */
9780           for (f2 = 0; f2 < f; ++f2) {
9781             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9782             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9783             poff += fdof-fcdof;
9784           }
9785           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9786           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9787           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9788             subIndices[subOff] = goff+poff+fc;
9789           }
9790         }
9791       }
9792     }
9793     PetscCall(ISRestoreIndices(spIS, &spmap));
9794     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9795     if (bs > 1) {
9796       /* We need to check that the block size does not come from non-contiguous fields */
9797       PetscInt i, j, set = 1;
9798       for (i = 0; i < subSize; i += bs) {
9799         for (j = 0; j < bs; ++j) {
9800           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9801         }
9802       }
9803       if (set) PetscCall(ISSetBlockSize(*is, bs));
9804     }
9805     /* Attach nullspace */
9806     for (f = 0; f < Nf; ++f) {
9807       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9808       if ((*subdm)->nullspaceConstructors[f]) break;
9809     }
9810     if (f < Nf) {
9811       MatNullSpace nullSpace;
9812       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9813 
9814       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9815       PetscCall(MatNullSpaceDestroy(&nullSpace));
9816     }
9817   }
9818   PetscFunctionReturn(0);
9819 }
9820 
9821 /*@
9822   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9823 
9824   Input Parameter:
9825 - dm - The DM
9826 
9827   Level: developer
9828 
9829   Options Database Keys:
9830 . -dm_plex_monitor_throughput - Activate the monitor
9831 
9832 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9833 @*/
9834 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9835 {
9836 #if defined(PETSC_USE_LOG)
9837   PetscStageLog      stageLog;
9838   PetscLogEvent      event;
9839   PetscLogStage      stage;
9840   PetscEventPerfInfo eventInfo;
9841   PetscReal          cellRate, flopRate;
9842   PetscInt           cStart, cEnd, Nf, N;
9843   const char        *name;
9844 #endif
9845 
9846   PetscFunctionBegin;
9847   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9848 #if defined(PETSC_USE_LOG)
9849   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9850   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9851   PetscCall(DMGetNumFields(dm, &Nf));
9852   PetscCall(PetscLogGetStageLog(&stageLog));
9853   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9854   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9855   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9856   N        = (cEnd - cStart)*Nf*eventInfo.count;
9857   flopRate = eventInfo.flops/eventInfo.time;
9858   cellRate = N/eventInfo.time;
9859   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)));
9860 #else
9861   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9862 #endif
9863   PetscFunctionReturn(0);
9864 }
9865