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