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