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