xref: /petsc/src/dm/impls/plex/plex.c (revision 96b592737fa480036d2a4095db7e1939ed49daf5)
1 #include <petsc/private/dmpleximpl.h>   /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/isimpl.h>
3 #include <petsc/private/vecimpl.h>
4 #include <petsc/private/glvisvecimpl.h>
5 #include <petscsf.h>
6 #include <petscds.h>
7 #include <petscdraw.h>
8 #include <petscdmfield.h>
9 #include <petscdmplextransform.h>
10 
11 /* Logging support */
12 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF,DMPLEX_LocatePoints,DMPLEX_TopologyView,DMPLEX_LabelsView,DMPLEX_CoordinatesView,DMPLEX_SectionView,DMPLEX_GlobalVectorView,DMPLEX_LocalVectorView,DMPLEX_TopologyLoad,DMPLEX_LabelsLoad,DMPLEX_CoordinatesLoad,DMPLEX_SectionLoad,DMPLEX_GlobalVectorLoad,DMPLEX_LocalVectorLoad;
13 PetscLogEvent DMPLEX_RebalBuildGraph,DMPLEX_RebalRewriteSF,DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart;
14 
15 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
16 
17 /*@
18   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
19 
20   Input Parameter:
21 . dm      - The DMPlex object
22 
23   Output Parameter:
24 . simplex - Flag checking for a simplex
25 
26   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
27   If the mesh has no cells, this returns PETSC_FALSE.
28 
29   Level: intermediate
30 
31 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
32 @*/
33 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
34 {
35   DMPolytopeType ct;
36   PetscInt       cStart, cEnd;
37 
38   PetscFunctionBegin;
39   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
40   if (cEnd <= cStart) {*simplex = PETSC_FALSE; PetscFunctionReturn(0);}
41   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
42   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
43   PetscFunctionReturn(0);
44 }
45 
46 /*@
47   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
48 
49   Input Parameters:
50 + dm     - The DMPlex object
51 - height - The cell height in the Plex, 0 is the default
52 
53   Output Parameters:
54 + cStart - The first "normal" cell
55 - cEnd   - The upper bound on "normal"" cells
56 
57   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
58 
59   Level: developer
60 
61 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
62 @*/
63 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
64 {
65   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
66   PetscInt       cS, cE, c;
67 
68   PetscFunctionBegin;
69   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
70   for (c = cS; c < cE; ++c) {
71     DMPolytopeType cct;
72 
73     PetscCall(DMPlexGetCellType(dm, c, &cct));
74     if ((PetscInt) cct < 0) break;
75     switch (cct) {
76       case DM_POLYTOPE_POINT:
77       case DM_POLYTOPE_SEGMENT:
78       case DM_POLYTOPE_TRIANGLE:
79       case DM_POLYTOPE_QUADRILATERAL:
80       case DM_POLYTOPE_TETRAHEDRON:
81       case DM_POLYTOPE_HEXAHEDRON:
82         ct = cct;
83         break;
84       default: break;
85     }
86     if (ct != DM_POLYTOPE_UNKNOWN) break;
87   }
88   if (ct != DM_POLYTOPE_UNKNOWN) {
89     DMLabel ctLabel;
90 
91     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
92     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
93   }
94   if (cStart) *cStart = cS;
95   if (cEnd)   *cEnd   = cE;
96   PetscFunctionReturn(0);
97 }
98 
99 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
100 {
101   PetscInt       cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
102   PetscInt       vcdof[2] = {0,0}, globalvcdof[2];
103 
104   PetscFunctionBegin;
105   *ft  = PETSC_VTK_INVALID;
106   PetscCall(DMGetCoordinateDim(dm, &cdim));
107   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
108   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
109   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
110   if (field >= 0) {
111     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
112     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
113   } else {
114     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
115     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
116   }
117   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
118   if (globalvcdof[0]) {
119     *sStart = vStart;
120     *sEnd   = vEnd;
121     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
122     else                        *ft = PETSC_VTK_POINT_FIELD;
123   } else if (globalvcdof[1]) {
124     *sStart = cStart;
125     *sEnd   = cEnd;
126     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
127     else                        *ft = PETSC_VTK_CELL_FIELD;
128   } else {
129     if (field >= 0) {
130       const char *fieldname;
131 
132       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
133       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
134     } else {
135       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section\n"));
136     }
137   }
138   PetscFunctionReturn(0);
139 }
140 
141 /*@
142   DMPlexVecView1D - Plot many 1D solutions on the same line graph
143 
144   Collective on dm
145 
146   Input Parameters:
147 + dm - The DMPlex
148 . n  - The number of vectors
149 . u  - The array of local vectors
150 - viewer - The Draw viewer
151 
152   Level: advanced
153 
154 .seealso: `VecViewFromOptions()`, `VecView()`
155 @*/
156 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
157 {
158   PetscDS            ds;
159   PetscDraw          draw = NULL;
160   PetscDrawLG        lg;
161   Vec                coordinates;
162   const PetscScalar *coords, **sol;
163   PetscReal         *vals;
164   PetscInt          *Nc;
165   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
166   char             **names;
167 
168   PetscFunctionBegin;
169   PetscCall(DMGetDS(dm, &ds));
170   PetscCall(PetscDSGetNumFields(ds, &Nf));
171   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
172   PetscCall(PetscDSGetComponents(ds, &Nc));
173 
174   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
175   if (!draw) PetscFunctionReturn(0);
176   PetscCall(PetscDrawLGCreate(draw, n*Nl, &lg));
177 
178   PetscCall(PetscMalloc3(n, &sol, n*Nl, &names, n*Nl, &vals));
179   for (i = 0, l = 0; i < n; ++i) {
180     const char *vname;
181 
182     PetscCall(PetscObjectGetName((PetscObject) u[i], &vname));
183     for (f = 0; f < Nf; ++f) {
184       PetscObject disc;
185       const char *fname;
186       char        tmpname[PETSC_MAX_PATH_LEN];
187 
188       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
189       /* TODO Create names for components */
190       for (c = 0; c < Nc[f]; ++c, ++l) {
191         PetscCall(PetscObjectGetName(disc, &fname));
192         PetscCall(PetscStrcpy(tmpname, vname));
193         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
194         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
195         PetscCall(PetscStrallocpy(tmpname, &names[l]));
196       }
197     }
198   }
199   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *) names));
200   /* Just add P_1 support for now */
201   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
202   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
203   PetscCall(VecGetArrayRead(coordinates, &coords));
204   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
205   for (v = vStart; v < vEnd; ++v) {
206     PetscScalar *x, *svals;
207 
208     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
209     for (i = 0; i < n; ++i) {
210       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
211       for (l = 0; l < Nl; ++l) vals[i*Nl + l] = PetscRealPart(svals[l]);
212     }
213     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
214   }
215   PetscCall(VecRestoreArrayRead(coordinates, &coords));
216   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
217   for (l = 0; l < n*Nl; ++l) PetscCall(PetscFree(names[l]));
218   PetscCall(PetscFree3(sol, names, vals));
219 
220   PetscCall(PetscDrawLGDraw(lg));
221   PetscCall(PetscDrawLGDestroy(&lg));
222   PetscFunctionReturn(0);
223 }
224 
225 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
226 {
227   DM             dm;
228 
229   PetscFunctionBegin;
230   PetscCall(VecGetDM(u, &dm));
231   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
232   PetscFunctionReturn(0);
233 }
234 
235 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
236 {
237   DM                 dm;
238   PetscSection       s;
239   PetscDraw          draw, popup;
240   DM                 cdm;
241   PetscSection       coordSection;
242   Vec                coordinates;
243   const PetscScalar *coords, *array;
244   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
245   PetscReal          vbound[2], time;
246   PetscBool          flg;
247   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
248   const char        *name;
249   char               title[PETSC_MAX_PATH_LEN];
250 
251   PetscFunctionBegin;
252   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
253   PetscCall(VecGetDM(v, &dm));
254   PetscCall(DMGetCoordinateDim(dm, &dim));
255   PetscCall(DMGetLocalSection(dm, &s));
256   PetscCall(PetscSectionGetNumFields(s, &Nf));
257   PetscCall(DMGetCoarsenLevel(dm, &level));
258   PetscCall(DMGetCoordinateDM(dm, &cdm));
259   PetscCall(DMGetLocalSection(cdm, &coordSection));
260   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
261   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
262   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
263 
264   PetscCall(PetscObjectGetName((PetscObject) v, &name));
265   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
266 
267   PetscCall(VecGetLocalSize(coordinates, &N));
268   PetscCall(VecGetArrayRead(coordinates, &coords));
269   for (c = 0; c < N; c += dim) {
270     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
271     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
272   }
273   PetscCall(VecRestoreArrayRead(coordinates, &coords));
274   PetscCall(PetscDrawClear(draw));
275 
276   /* Could implement something like DMDASelectFields() */
277   for (f = 0; f < Nf; ++f) {
278     DM   fdm = dm;
279     Vec  fv  = v;
280     IS   fis;
281     char prefix[PETSC_MAX_PATH_LEN];
282     const char *fname;
283 
284     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
285     PetscCall(PetscSectionGetFieldName(s, f, &fname));
286 
287     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix,sizeof(prefix)));
288     else               {prefix[0] = '\0';}
289     if (Nf > 1) {
290       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
291       PetscCall(VecGetSubVector(v, fis, &fv));
292       PetscCall(PetscStrlcat(prefix, fname,sizeof(prefix)));
293       PetscCall(PetscStrlcat(prefix, "_",sizeof(prefix)));
294     }
295     for (comp = 0; comp < Nc; ++comp, ++w) {
296       PetscInt nmax = 2;
297 
298       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
299       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
300       else        PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
301       PetscCall(PetscDrawSetTitle(draw, title));
302 
303       /* TODO Get max and min only for this component */
304       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
305       if (!flg) {
306         PetscCall(VecMin(fv, NULL, &vbound[0]));
307         PetscCall(VecMax(fv, NULL, &vbound[1]));
308         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
309       }
310       PetscCall(PetscDrawGetPopup(draw, &popup));
311       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
312       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
313 
314       PetscCall(VecGetArrayRead(fv, &array));
315       for (c = cStart; c < cEnd; ++c) {
316         PetscScalar *coords = NULL, *a = NULL;
317         PetscInt     numCoords, color[4] = {-1,-1,-1,-1};
318 
319         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
320         if (a) {
321           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
322           color[1] = color[2] = color[3] = color[0];
323         } else {
324           PetscScalar *vals = NULL;
325           PetscInt     numVals, va;
326 
327           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
328           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);
329           switch (numVals/Nc) {
330           case 3: /* P1 Triangle */
331           case 4: /* P1 Quadrangle */
332             for (va = 0; va < numVals/Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp]), vbound[0], vbound[1]);
333             break;
334           case 6: /* P2 Triangle */
335           case 8: /* P2 Quadrangle */
336             for (va = 0; va < numVals/(Nc*2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp + numVals/(Nc*2)]), vbound[0], vbound[1]);
337             break;
338           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals/Nc);
339           }
340           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
341         }
342         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
343         switch (numCoords) {
344         case 6:
345         case 12: /* Localized triangle */
346           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]));
347           break;
348         case 8:
349         case 16: /* Localized quadrilateral */
350           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]));
351           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]));
352           break;
353         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
354         }
355         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
356       }
357       PetscCall(VecRestoreArrayRead(fv, &array));
358       PetscCall(PetscDrawFlush(draw));
359       PetscCall(PetscDrawPause(draw));
360       PetscCall(PetscDrawSave(draw));
361     }
362     if (Nf > 1) {
363       PetscCall(VecRestoreSubVector(v, fis, &fv));
364       PetscCall(ISDestroy(&fis));
365       PetscCall(DMDestroy(&fdm));
366     }
367   }
368   PetscFunctionReturn(0);
369 }
370 
371 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
372 {
373   DM        dm;
374   PetscDraw draw;
375   PetscInt  dim;
376   PetscBool isnull;
377 
378   PetscFunctionBegin;
379   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
380   PetscCall(PetscDrawIsNull(draw, &isnull));
381   if (isnull) PetscFunctionReturn(0);
382 
383   PetscCall(VecGetDM(v, &dm));
384   PetscCall(DMGetCoordinateDim(dm, &dim));
385   switch (dim) {
386   case 1: PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));break;
387   case 2: PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));break;
388   default: SETERRQ(PetscObjectComm((PetscObject) v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
389   }
390   PetscFunctionReturn(0);
391 }
392 
393 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
394 {
395   DM                      dm;
396   Vec                     locv;
397   const char              *name;
398   PetscSection            section;
399   PetscInt                pStart, pEnd;
400   PetscInt                numFields;
401   PetscViewerVTKFieldType ft;
402 
403   PetscFunctionBegin;
404   PetscCall(VecGetDM(v, &dm));
405   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
406   PetscCall(PetscObjectGetName((PetscObject) v, &name));
407   PetscCall(PetscObjectSetName((PetscObject) locv, name));
408   PetscCall(VecCopy(v, locv));
409   PetscCall(DMGetLocalSection(dm, &section));
410   PetscCall(PetscSectionGetNumFields(section, &numFields));
411   if (!numFields) {
412     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
413     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE,(PetscObject) locv));
414   } else {
415     PetscInt f;
416 
417     for (f = 0; f < numFields; f++) {
418       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
419       if (ft == PETSC_VTK_INVALID) continue;
420       PetscCall(PetscObjectReference((PetscObject)locv));
421       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE,(PetscObject) locv));
422     }
423     PetscCall(VecDestroy(&locv));
424   }
425   PetscFunctionReturn(0);
426 }
427 
428 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
429 {
430   DM             dm;
431   PetscBool      isvtk, ishdf5, isdraw, isglvis;
432 
433   PetscFunctionBegin;
434   PetscCall(VecGetDM(v, &dm));
435   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
436   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk));
437   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5));
438   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw));
439   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis));
440   if (isvtk || ishdf5 || isdraw || isglvis) {
441     PetscInt    i,numFields;
442     PetscObject fe;
443     PetscBool   fem = PETSC_FALSE;
444     Vec         locv = v;
445     const char  *name;
446     PetscInt    step;
447     PetscReal   time;
448 
449     PetscCall(DMGetNumFields(dm, &numFields));
450     for (i=0; i<numFields; i++) {
451       PetscCall(DMGetField(dm, i, NULL, &fe));
452       if (fe->classid == PETSCFE_CLASSID) { fem = PETSC_TRUE; break; }
453     }
454     if (fem) {
455       PetscObject isZero;
456 
457       PetscCall(DMGetLocalVector(dm, &locv));
458       PetscCall(PetscObjectGetName((PetscObject) v, &name));
459       PetscCall(PetscObjectSetName((PetscObject) locv, name));
460       PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
461       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
462       PetscCall(VecCopy(v, locv));
463       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
464       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
465     }
466     if (isvtk) {
467       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
468     } else if (ishdf5) {
469 #if defined(PETSC_HAVE_HDF5)
470       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
471 #else
472       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
473 #endif
474     } else if (isdraw) {
475       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
476     } else if (isglvis) {
477       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
478       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
479       PetscCall(VecView_GLVis(locv, viewer));
480     }
481     if (fem) {
482       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
483       PetscCall(DMRestoreLocalVector(dm, &locv));
484     }
485   } else {
486     PetscBool isseq;
487 
488     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
489     if (isseq) PetscCall(VecView_Seq(v, viewer));
490     else       PetscCall(VecView_MPI(v, viewer));
491   }
492   PetscFunctionReturn(0);
493 }
494 
495 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
496 {
497   DM        dm;
498   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii;
499 
500   PetscFunctionBegin;
501   PetscCall(VecGetDM(v, &dm));
502   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
503   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
504   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
505   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
506   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
507   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
508   if (isvtk || isdraw || isglvis) {
509     Vec         locv;
510     PetscObject isZero;
511     const char *name;
512 
513     PetscCall(DMGetLocalVector(dm, &locv));
514     PetscCall(PetscObjectGetName((PetscObject) v, &name));
515     PetscCall(PetscObjectSetName((PetscObject) locv, name));
516     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
517     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
518     PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
519     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
520     PetscCall(VecView_Plex_Local(locv, viewer));
521     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
522     PetscCall(DMRestoreLocalVector(dm, &locv));
523   } else if (ishdf5) {
524 #if defined(PETSC_HAVE_HDF5)
525     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
526 #else
527     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
528 #endif
529   } else if (isexodusii) {
530 #if defined(PETSC_HAVE_EXODUSII)
531     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
532 #else
533     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
534 #endif
535   } else {
536     PetscBool isseq;
537 
538     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
539     if (isseq) PetscCall(VecView_Seq(v, viewer));
540     else       PetscCall(VecView_MPI(v, viewer));
541   }
542   PetscFunctionReturn(0);
543 }
544 
545 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
546 {
547   DM                dm;
548   MPI_Comm          comm;
549   PetscViewerFormat format;
550   Vec               v;
551   PetscBool         isvtk, ishdf5;
552 
553   PetscFunctionBegin;
554   PetscCall(VecGetDM(originalv, &dm));
555   PetscCall(PetscObjectGetComm((PetscObject) originalv, &comm));
556   PetscCheck(dm,comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
557   PetscCall(PetscViewerGetFormat(viewer, &format));
558   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
559   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,  &isvtk));
560   if (format == PETSC_VIEWER_NATIVE) {
561     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
562     /* this need a better fix */
563     if (dm->useNatural) {
564       if (dm->sfNatural) {
565         const char *vecname;
566         PetscInt    n, nroots;
567 
568         PetscCall(VecGetLocalSize(originalv, &n));
569         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
570         if (n == nroots) {
571           PetscCall(DMGetGlobalVector(dm, &v));
572           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
573           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
574           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
575           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
576         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
577       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
578     } else v = originalv;
579   } else v = originalv;
580 
581   if (ishdf5) {
582 #if defined(PETSC_HAVE_HDF5)
583     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
584 #else
585     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
586 #endif
587   } else if (isvtk) {
588     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
589   } else {
590     PetscBool isseq;
591 
592     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
593     if (isseq) PetscCall(VecView_Seq(v, viewer));
594     else       PetscCall(VecView_MPI(v, viewer));
595   }
596   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
597   PetscFunctionReturn(0);
598 }
599 
600 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
601 {
602   DM             dm;
603   PetscBool      ishdf5;
604 
605   PetscFunctionBegin;
606   PetscCall(VecGetDM(v, &dm));
607   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
608   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
609   if (ishdf5) {
610     DM          dmBC;
611     Vec         gv;
612     const char *name;
613 
614     PetscCall(DMGetOutputDM(dm, &dmBC));
615     PetscCall(DMGetGlobalVector(dmBC, &gv));
616     PetscCall(PetscObjectGetName((PetscObject) v, &name));
617     PetscCall(PetscObjectSetName((PetscObject) gv, name));
618     PetscCall(VecLoad_Default(gv, viewer));
619     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
620     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
621     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
622   } else PetscCall(VecLoad_Default(v, viewer));
623   PetscFunctionReturn(0);
624 }
625 
626 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
627 {
628   DM             dm;
629   PetscBool      ishdf5,isexodusii;
630 
631   PetscFunctionBegin;
632   PetscCall(VecGetDM(v, &dm));
633   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
634   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
635   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
636   if (ishdf5) {
637 #if defined(PETSC_HAVE_HDF5)
638     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
639 #else
640     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
641 #endif
642   } else if (isexodusii) {
643 #if defined(PETSC_HAVE_EXODUSII)
644     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
645 #else
646     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
647 #endif
648   } else PetscCall(VecLoad_Default(v, viewer));
649   PetscFunctionReturn(0);
650 }
651 
652 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
653 {
654   DM                dm;
655   PetscViewerFormat format;
656   PetscBool         ishdf5;
657 
658   PetscFunctionBegin;
659   PetscCall(VecGetDM(originalv, &dm));
660   PetscCheck(dm,PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
661   PetscCall(PetscViewerGetFormat(viewer, &format));
662   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
663   if (format == PETSC_VIEWER_NATIVE) {
664     if (dm->useNatural) {
665       if (dm->sfNatural) {
666         if (ishdf5) {
667 #if defined(PETSC_HAVE_HDF5)
668           Vec         v;
669           const char *vecname;
670 
671           PetscCall(DMGetGlobalVector(dm, &v));
672           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
673           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
674           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
675           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
676           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
677           PetscCall(DMRestoreGlobalVector(dm, &v));
678 #else
679           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
680 #endif
681         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
682       }
683     } else PetscCall(VecLoad_Default(originalv, viewer));
684   }
685   PetscFunctionReturn(0);
686 }
687 
688 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
689 {
690   PetscSection       coordSection;
691   Vec                coordinates;
692   DMLabel            depthLabel, celltypeLabel;
693   const char        *name[4];
694   const PetscScalar *a;
695   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
696 
697   PetscFunctionBegin;
698   PetscCall(DMGetDimension(dm, &dim));
699   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
700   PetscCall(DMGetCoordinateSection(dm, &coordSection));
701   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
702   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
703   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
704   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
705   PetscCall(VecGetArrayRead(coordinates, &a));
706   name[0]     = "vertex";
707   name[1]     = "edge";
708   name[dim-1] = "face";
709   name[dim]   = "cell";
710   for (c = cStart; c < cEnd; ++c) {
711     PetscInt *closure = NULL;
712     PetscInt  closureSize, cl, ct;
713 
714     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
715     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
716     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
717     PetscCall(PetscViewerASCIIPushTab(viewer));
718     for (cl = 0; cl < closureSize*2; cl += 2) {
719       PetscInt point = closure[cl], depth, dof, off, d, p;
720 
721       if ((point < pStart) || (point >= pEnd)) continue;
722       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
723       if (!dof) continue;
724       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
725       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
726       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
727       for (p = 0; p < dof/dim; ++p) {
728         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
729         for (d = 0; d < dim; ++d) {
730           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
731           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d])));
732         }
733         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
734       }
735       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
736     }
737     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
738     PetscCall(PetscViewerASCIIPopTab(viewer));
739   }
740   PetscCall(VecRestoreArrayRead(coordinates, &a));
741   PetscFunctionReturn(0);
742 }
743 
744 typedef enum {CS_CARTESIAN, CS_POLAR, CS_CYLINDRICAL, CS_SPHERICAL} CoordSystem;
745 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
746 
747 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
748 {
749   PetscInt       i;
750 
751   PetscFunctionBegin;
752   if (dim > 3) {
753     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) PetscRealPart(x[i])));
754   } else {
755     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
756 
757     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
758     switch (cs) {
759       case CS_CARTESIAN: for (i = 0; i < dim; ++i) trcoords[i] = coords[i];break;
760       case CS_POLAR:
761         PetscCheck(dim == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
762         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
763         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
764         break;
765       case CS_CYLINDRICAL:
766         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
767         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
768         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
769         trcoords[2] = coords[2];
770         break;
771       case CS_SPHERICAL:
772         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
773         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
774         trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
775         trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
776         break;
777     }
778     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) trcoords[i]));
779   }
780   PetscFunctionReturn(0);
781 }
782 
783 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
784 {
785   DM_Plex          *mesh = (DM_Plex*) dm->data;
786   DM                cdm, cdmCell;
787   PetscSection      coordSection, coordSectionCell;
788   Vec               coordinates, coordinatesCell;
789   PetscViewerFormat format;
790 
791   PetscFunctionBegin;
792   PetscCall(DMGetCoordinateDM(dm, &cdm));
793   PetscCall(DMGetCoordinateSection(dm, &coordSection));
794   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
795   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
796   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
797   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
798   PetscCall(PetscViewerGetFormat(viewer, &format));
799   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
800     const char *name;
801     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
802     PetscInt    pStart, pEnd, p, numLabels, l;
803     PetscMPIInt rank, size;
804 
805     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
806     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
807     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
808     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
809     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
810     PetscCall(DMGetDimension(dm, &dim));
811     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
812     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
813     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
814     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
815     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
816     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
817     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
818     for (p = pStart; p < pEnd; ++p) {
819       PetscInt dof, off, s;
820 
821       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
822       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
823       for (s = off; s < off+dof; ++s) {
824         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
825       }
826     }
827     PetscCall(PetscViewerFlush(viewer));
828     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
829     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
830     for (p = pStart; p < pEnd; ++p) {
831       PetscInt dof, off, c;
832 
833       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
834       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
835       for (c = off; c < off+dof; ++c) {
836         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
837       }
838     }
839     PetscCall(PetscViewerFlush(viewer));
840     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
841     if (coordSection && coordinates) {
842       CoordSystem        cs = CS_CARTESIAN;
843       const PetscScalar *array, *arrayCell = NULL;
844       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
845       PetscMPIInt        rank;
846       const char        *name;
847 
848       PetscCall(PetscOptionsGetEnum(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *) &cs, NULL));
849       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
850       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
851       PetscCheck(Nf == 1,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
852       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
853       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
854       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
855       pStart =  PetscMin(pvStart, pcStart);
856       pEnd   =  PetscMax(pvEnd,   pcEnd);
857       PetscCall(PetscObjectGetName((PetscObject) coordinates, &name));
858       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
859       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
860       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
861 
862       PetscCall(VecGetArrayRead(coordinates, &array));
863       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
864       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
865       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
866       for (p = pStart; p < pEnd; ++p) {
867         PetscInt dof, off;
868 
869         if (p >= pvStart && p < pvEnd) {
870           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
871           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
872           if (dof) {
873             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
874             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
875             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
876           }
877         }
878         if (cdmCell && p >= pcStart && p < pcEnd) {
879           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
880           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
881           if (dof) {
882             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
883             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
884             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
885           }
886         }
887       }
888       PetscCall(PetscViewerFlush(viewer));
889       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
890       PetscCall(VecRestoreArrayRead(coordinates, &array));
891       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
892     }
893     PetscCall(DMGetNumLabels(dm, &numLabels));
894     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
895     for (l = 0; l < numLabels; ++l) {
896       DMLabel     label;
897       PetscBool   isdepth;
898       const char *name;
899 
900       PetscCall(DMGetLabelName(dm, l, &name));
901       PetscCall(PetscStrcmp(name, "depth", &isdepth));
902       if (isdepth) continue;
903       PetscCall(DMGetLabel(dm, name, &label));
904       PetscCall(DMLabelView(label, viewer));
905     }
906     if (size > 1) {
907       PetscSF sf;
908 
909       PetscCall(DMGetPointSF(dm, &sf));
910       PetscCall(PetscSFView(sf, viewer));
911     }
912     PetscCall(PetscViewerFlush(viewer));
913   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
914     const char  *name, *color;
915     const char  *defcolors[3]  = {"gray", "orange", "green"};
916     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
917     char         lname[PETSC_MAX_PATH_LEN];
918     PetscReal    scale         = 2.0;
919     PetscReal    tikzscale     = 1.0;
920     PetscBool    useNumbers    = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
921     double       tcoords[3];
922     PetscScalar *coords;
923     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
924     PetscMPIInt  rank, size;
925     char         **names, **colors, **lcolors;
926     PetscBool    flg, lflg;
927     PetscBT      wp = NULL;
928     PetscInt     pEnd, pStart;
929 
930     PetscCall(DMGetDimension(dm, &dim));
931     PetscCall(DMPlexGetDepth(dm, &depth));
932     PetscCall(DMGetNumLabels(dm, &numLabels));
933     numLabels  = PetscMax(numLabels, 10);
934     numColors  = 10;
935     numLColors = 10;
936     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
937     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
938     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
939     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
940     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
941     for (d = 0; d < 4; ++d) drawColors[d]  = PETSC_TRUE;
942     n = 4;
943     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
944     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
945     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
946     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
947     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
948     if (!useLabels) numLabels = 0;
949     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
950     if (!useColors) {
951       numColors = 3;
952       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
953     }
954     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
955     if (!useColors) {
956       numLColors = 4;
957       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
958     }
959     PetscCall(PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
960     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
961     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
962     PetscCheck(!flg || !plotEdges || depth >= dim,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
963     if (depth < dim) plotEdges = PETSC_FALSE;
964     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
965 
966     /* filter points with labelvalue != labeldefaultvalue */
967     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
968     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
969     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
970     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
971     if (lflg) {
972       DMLabel lbl;
973 
974       PetscCall(DMGetLabel(dm, lname, &lbl));
975       if (lbl) {
976         PetscInt val, defval;
977 
978         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
979         PetscCall(PetscBTCreate(pEnd-pStart, &wp));
980         for (c = pStart;  c < pEnd; c++) {
981           PetscInt *closure = NULL;
982           PetscInt  closureSize;
983 
984           PetscCall(DMLabelGetValue(lbl, c, &val));
985           if (val == defval) continue;
986 
987           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
988           for (p = 0; p < closureSize*2; p += 2) {
989             PetscCall(PetscBTSet(wp, closure[p] - pStart));
990           }
991           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
992         }
993       }
994     }
995 
996     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
997     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
998     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
999     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1000 \\documentclass[tikz]{standalone}\n\n\
1001 \\usepackage{pgflibraryshapes}\n\
1002 \\usetikzlibrary{backgrounds}\n\
1003 \\usetikzlibrary{arrows}\n\
1004 \\begin{document}\n"));
1005     if (size > 1) {
1006       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1007       for (p = 0; p < size; ++p) {
1008         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size-1) ? ", and " :  ", "));
1009         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p%numColors], p));
1010       }
1011       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1012     }
1013     if (drawHasse) {
1014       PetscInt maxStratum = PetscMax(vEnd-vStart, PetscMax(eEnd-eStart, cEnd-cStart));
1015 
1016       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1017       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd-1));
1018       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd-vStart));
1019       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum-(vEnd-vStart))/2.));
1020       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1021       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd-1));
1022       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum-(eEnd-eStart))/2.));
1023       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd-eStart));
1024       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1025       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd-1));
1026       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd-cStart));
1027       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum-(cEnd-cStart))/2.));
1028     }
1029     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale));
1030 
1031     /* Plot vertices */
1032     PetscCall(VecGetArray(coordinates, &coords));
1033     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1034     for (v = vStart; v < vEnd; ++v) {
1035       PetscInt  off, dof, d;
1036       PetscBool isLabeled = PETSC_FALSE;
1037 
1038       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
1039       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1040       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1041       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1042       PetscCheck(dof <= 3,PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3",v,dof);
1043       for (d = 0; d < dof; ++d) {
1044         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1045         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1046       }
1047       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1048       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1049       for (d = 0; d < dof; ++d) {
1050         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1051         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]));
1052       }
1053       if (drawHasse) color = colors[0%numColors];
1054       else           color = colors[rank%numColors];
1055       for (l = 0; l < numLabels; ++l) {
1056         PetscInt val;
1057         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1058         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1059       }
1060       if (drawNumbers[0]) {
1061         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1062       } else if (drawColors[0]) {
1063         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1064       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1065     }
1066     PetscCall(VecRestoreArray(coordinates, &coords));
1067     PetscCall(PetscViewerFlush(viewer));
1068     /* Plot edges */
1069     if (plotEdges) {
1070       PetscCall(VecGetArray(coordinates, &coords));
1071       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1072       for (e = eStart; e < eEnd; ++e) {
1073         const PetscInt *cone;
1074         PetscInt        coneSize, offA, offB, dof, d;
1075 
1076         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1077         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1078         PetscCheck(coneSize == 2,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1079         PetscCall(DMPlexGetCone(dm, e, &cone));
1080         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1081         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1082         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1083         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1084         for (d = 0; d < dof; ++d) {
1085           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
1086           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1087         }
1088         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1089         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1090         for (d = 0; d < dof; ++d) {
1091           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1092           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1093         }
1094         if (drawHasse) color = colors[1%numColors];
1095         else           color = colors[rank%numColors];
1096         for (l = 0; l < numLabels; ++l) {
1097           PetscInt val;
1098           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1099           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1100         }
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1102       }
1103       PetscCall(VecRestoreArray(coordinates, &coords));
1104       PetscCall(PetscViewerFlush(viewer));
1105       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1106     }
1107     /* Plot cells */
1108     if (dim == 3 || !drawNumbers[1]) {
1109       for (e = eStart; e < eEnd; ++e) {
1110         const PetscInt *cone;
1111 
1112         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1113         color = colors[rank%numColors];
1114         for (l = 0; l < numLabels; ++l) {
1115           PetscInt val;
1116           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1117           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1118         }
1119         PetscCall(DMPlexGetCone(dm, e, &cone));
1120         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1121       }
1122     } else {
1123        DMPolytopeType ct;
1124 
1125       /* Drawing a 2D polygon */
1126       for (c = cStart; c < cEnd; ++c) {
1127         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1128         PetscCall(DMPlexGetCellType(dm, c, &ct));
1129         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR ||
1130             ct == DM_POLYTOPE_TRI_PRISM_TENSOR ||
1131             ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1132           const PetscInt *cone;
1133           PetscInt        coneSize, e;
1134 
1135           PetscCall(DMPlexGetCone(dm, c, &cone));
1136           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1137           for (e = 0; e < coneSize; ++e) {
1138             const PetscInt *econe;
1139 
1140             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1141             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));
1142           }
1143         } else {
1144           PetscInt *closure = NULL;
1145           PetscInt  closureSize, Nv = 0, v;
1146 
1147           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1148           for (p = 0; p < closureSize*2; p += 2) {
1149             const PetscInt point = closure[p];
1150 
1151             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1152           }
1153           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]));
1154           for (v = 0; v <= Nv; ++v) {
1155             const PetscInt vertex = closure[v%Nv];
1156 
1157             if (v > 0) {
1158               if (plotEdges) {
1159                 const PetscInt *edge;
1160                 PetscInt        endpoints[2], ne;
1161 
1162                 endpoints[0] = closure[v-1]; endpoints[1] = vertex;
1163                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1164                 PetscCheck(ne == 1,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1165                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1166                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1167               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1168             }
1169             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1170           }
1171           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1172           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1173         }
1174       }
1175     }
1176     PetscCall(VecGetArray(coordinates, &coords));
1177     for (c = cStart; c < cEnd; ++c) {
1178       double    ccoords[3] = {0.0, 0.0, 0.0};
1179       PetscBool isLabeled  = PETSC_FALSE;
1180       PetscInt *closure    = NULL;
1181       PetscInt  closureSize, dof, d, n = 0;
1182 
1183       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
1184       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1185       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1186       for (p = 0; p < closureSize*2; p += 2) {
1187         const PetscInt point = closure[p];
1188         PetscInt       off;
1189 
1190         if ((point < vStart) || (point >= vEnd)) continue;
1191         PetscCall(PetscSectionGetDof(coordSection, point, &dof));
1192         PetscCall(PetscSectionGetOffset(coordSection, point, &off));
1193         for (d = 0; d < dof; ++d) {
1194           tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1195           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1196         }
1197         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1198         if (dof == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1199         for (d = 0; d < dof; ++d) {ccoords[d] += tcoords[d];}
1200         ++n;
1201       }
1202       for (d = 0; d < dof; ++d) {ccoords[d] /= n;}
1203       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1204       for (d = 0; d < dof; ++d) {
1205         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1206         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]));
1207       }
1208       if (drawHasse) color = colors[depth%numColors];
1209       else           color = colors[rank%numColors];
1210       for (l = 0; l < numLabels; ++l) {
1211         PetscInt val;
1212         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1213         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1214       }
1215       if (drawNumbers[dim]) {
1216         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1217       } else if (drawColors[dim]) {
1218         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1219       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1220     }
1221     PetscCall(VecRestoreArray(coordinates, &coords));
1222     if (drawHasse) {
1223       color = colors[depth%numColors];
1224       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1225       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1226       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1227       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1228       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1229 
1230       color = colors[1%numColors];
1231       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1232       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1233       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1234       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1235       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1236 
1237       color = colors[0%numColors];
1238       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1239       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1241       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1242       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1243 
1244       for (p = pStart; p < pEnd; ++p) {
1245         const PetscInt *cone;
1246         PetscInt        coneSize, cp;
1247 
1248         PetscCall(DMPlexGetCone(dm, p, &cone));
1249         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1250         for (cp = 0; cp < coneSize; ++cp) {
1251           PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1252         }
1253       }
1254     }
1255     PetscCall(PetscViewerFlush(viewer));
1256     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1257     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1258     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1259     for (l = 0; l < numLabels;  ++l) PetscCall(PetscFree(names[l]));
1260     for (c = 0; c < numColors;  ++c) PetscCall(PetscFree(colors[c]));
1261     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1262     PetscCall(PetscFree3(names, colors, lcolors));
1263     PetscCall(PetscBTDestroy(&wp));
1264   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1265     Vec                    cown,acown;
1266     VecScatter             sct;
1267     ISLocalToGlobalMapping g2l;
1268     IS                     gid,acis;
1269     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
1270     MPI_Group              ggroup,ngroup;
1271     PetscScalar            *array,nid;
1272     const PetscInt         *idxs;
1273     PetscInt               *idxs2,*start,*adjacency,*work;
1274     PetscInt64             lm[3],gm[3];
1275     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
1276     PetscMPIInt            d1,d2,rank;
1277 
1278     PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
1279     PetscCallMPI(MPI_Comm_rank(comm,&rank));
1280 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1281     PetscCallMPI(MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm));
1282 #endif
1283     if (ncomm != MPI_COMM_NULL) {
1284       PetscCallMPI(MPI_Comm_group(comm,&ggroup));
1285       PetscCallMPI(MPI_Comm_group(ncomm,&ngroup));
1286       d1   = 0;
1287       PetscCallMPI(MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2));
1288       nid  = d2;
1289       PetscCallMPI(MPI_Group_free(&ggroup));
1290       PetscCallMPI(MPI_Group_free(&ngroup));
1291       PetscCallMPI(MPI_Comm_free(&ncomm));
1292     } else nid = 0.0;
1293 
1294     /* Get connectivity */
1295     PetscCall(DMPlexGetVTKCellHeight(dm,&cellHeight));
1296     PetscCall(DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid));
1297 
1298     /* filter overlapped local cells */
1299     PetscCall(DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd));
1300     PetscCall(ISGetIndices(gid,&idxs));
1301     PetscCall(ISGetLocalSize(gid,&cum));
1302     PetscCall(PetscMalloc1(cum,&idxs2));
1303     for (c = cStart, cum = 0; c < cEnd; c++) {
1304       if (idxs[c-cStart] < 0) continue;
1305       idxs2[cum++] = idxs[c-cStart];
1306     }
1307     PetscCall(ISRestoreIndices(gid,&idxs));
1308     PetscCheck(numVertices == cum,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %" PetscInt_FMT " != %" PetscInt_FMT,numVertices,cum);
1309     PetscCall(ISDestroy(&gid));
1310     PetscCall(ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid));
1311 
1312     /* support for node-aware cell locality */
1313     PetscCall(ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis));
1314     PetscCall(VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown));
1315     PetscCall(VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown));
1316     PetscCall(VecGetArray(cown,&array));
1317     for (c = 0; c < numVertices; c++) array[c] = nid;
1318     PetscCall(VecRestoreArray(cown,&array));
1319     PetscCall(VecScatterCreate(cown,acis,acown,NULL,&sct));
1320     PetscCall(VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1321     PetscCall(VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1322     PetscCall(ISDestroy(&acis));
1323     PetscCall(VecScatterDestroy(&sct));
1324     PetscCall(VecDestroy(&cown));
1325 
1326     /* compute edgeCut */
1327     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
1328     PetscCall(PetscMalloc1(cum,&work));
1329     PetscCall(ISLocalToGlobalMappingCreateIS(gid,&g2l));
1330     PetscCall(ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH));
1331     PetscCall(ISDestroy(&gid));
1332     PetscCall(VecGetArray(acown,&array));
1333     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1334       PetscInt totl;
1335 
1336       totl = start[c+1]-start[c];
1337       PetscCall(ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work));
1338       for (i = 0; i < totl; i++) {
1339         if (work[i] < 0) {
1340           ect  += 1;
1341           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1342         }
1343       }
1344     }
1345     PetscCall(PetscFree(work));
1346     PetscCall(VecRestoreArray(acown,&array));
1347     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1348     lm[1] = -numVertices;
1349     PetscCall(MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm));
1350     PetscCall(PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT,-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]));
1351     lm[0] = ect; /* edgeCut */
1352     lm[1] = ectn; /* node-aware edgeCut */
1353     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1354     PetscCall(MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm));
1355     PetscCall(PetscViewerASCIIPrintf(viewer,", empty %" PetscInt_FMT ")\n",(PetscInt)gm[2]));
1356 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1357     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),gm[0] ? ((double)(gm[1]))/((double)gm[0]) : 1.));
1358 #else
1359     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0));
1360 #endif
1361     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1362     PetscCall(PetscFree(start));
1363     PetscCall(PetscFree(adjacency));
1364     PetscCall(VecDestroy(&acown));
1365   } else {
1366     const char    *name;
1367     PetscInt      *sizes, *hybsizes, *ghostsizes;
1368     PetscInt       locDepth, depth, cellHeight, dim, d;
1369     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1370     PetscInt       numLabels, l, maxSize = 17;
1371     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1372     MPI_Comm       comm;
1373     PetscMPIInt    size, rank;
1374 
1375     PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
1376     PetscCallMPI(MPI_Comm_size(comm, &size));
1377     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1378     PetscCall(DMGetDimension(dm, &dim));
1379     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1380     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1381     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1382     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1383     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1384     PetscCall(DMPlexGetDepth(dm, &locDepth));
1385     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1386     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1387     gcNum = gcEnd - gcStart;
1388     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1389     else                PetscCall(PetscCalloc3(3,    &sizes, 3,    &hybsizes, 3,    &ghostsizes));
1390     for (d = 0; d <= depth; d++) {
1391       PetscInt Nc[2] = {0, 0}, ict;
1392 
1393       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1394       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1395       ict  = ct0;
1396       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1397       ct0  = (DMPolytopeType) ict;
1398       for (p = pStart; p < pEnd; ++p) {
1399         DMPolytopeType ct;
1400 
1401         PetscCall(DMPlexGetCellType(dm, p, &ct));
1402         if (ct == ct0) ++Nc[0];
1403         else           ++Nc[1];
1404       }
1405       if (size < maxSize) {
1406         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm));
1407         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1408         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1409         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1410         for (p = 0; p < size; ++p) {
1411           if (rank == 0) {
1412             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p]+hybsizes[p]));
1413             if (hybsizes[p]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1414             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1415           }
1416         }
1417       } else {
1418         PetscInt locMinMax[2];
1419 
1420         locMinMax[0] = Nc[0]+Nc[1]; locMinMax[1] = Nc[0]+Nc[1];
1421         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1422         locMinMax[0] = Nc[1]; locMinMax[1] = Nc[1];
1423         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1424         if (d == depth) {
1425           locMinMax[0] = gcNum; locMinMax[1] = gcNum;
1426           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1427         }
1428         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1429         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1430         if (hybsizes[0]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1431         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1432       }
1433       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1434     }
1435     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1436     {
1437       const PetscReal *maxCell;
1438       const PetscReal *L;
1439       PetscBool        localized;
1440 
1441       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1442       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1443       if (L || localized) {
1444         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1445         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1446         if (L) {
1447           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1448           for (d = 0; d < dim; ++d) {
1449             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1450             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1451           }
1452           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1453         }
1454         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1455         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1456       }
1457     }
1458     PetscCall(DMGetNumLabels(dm, &numLabels));
1459     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1460     for (l = 0; l < numLabels; ++l) {
1461       DMLabel         label;
1462       const char     *name;
1463       IS              valueIS;
1464       const PetscInt *values;
1465       PetscInt        numValues, v;
1466 
1467       PetscCall(DMGetLabelName(dm, l, &name));
1468       PetscCall(DMGetLabel(dm, name, &label));
1469       PetscCall(DMLabelGetNumValues(label, &numValues));
1470       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1471       PetscCall(DMLabelGetValueIS(label, &valueIS));
1472       PetscCall(ISGetIndices(valueIS, &values));
1473       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1474       for (v = 0; v < numValues; ++v) {
1475         PetscInt size;
1476 
1477         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1478         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1479         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1480       }
1481       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1482       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1483       PetscCall(ISRestoreIndices(valueIS, &values));
1484       PetscCall(ISDestroy(&valueIS));
1485     }
1486     {
1487       char    **labelNames;
1488       PetscInt  Nl = numLabels;
1489       PetscBool flg;
1490 
1491       PetscCall(PetscMalloc1(Nl, &labelNames));
1492       PetscCall(PetscOptionsGetStringArray(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1493       for (l = 0; l < Nl; ++l) {
1494         DMLabel label;
1495 
1496         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1497         if (flg) {
1498           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1499           PetscCall(DMLabelView(label, viewer));
1500         }
1501         PetscCall(PetscFree(labelNames[l]));
1502       }
1503       PetscCall(PetscFree(labelNames));
1504     }
1505     /* If no fields are specified, people do not want to see adjacency */
1506     if (dm->Nf) {
1507       PetscInt f;
1508 
1509       for (f = 0; f < dm->Nf; ++f) {
1510         const char *name;
1511 
1512         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1513         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1514         PetscCall(PetscViewerASCIIPushTab(viewer));
1515         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1516         if (dm->fields[f].adjacency[0]) {
1517           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1518           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1519         } else {
1520           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1521           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1522         }
1523         PetscCall(PetscViewerASCIIPopTab(viewer));
1524       }
1525     }
1526     PetscCall(DMGetCoarseDM(dm, &cdm));
1527     if (cdm) {
1528       PetscCall(PetscViewerASCIIPushTab(viewer));
1529       PetscCall(DMPlexView_Ascii(cdm, viewer));
1530       PetscCall(PetscViewerASCIIPopTab(viewer));
1531     }
1532   }
1533   PetscFunctionReturn(0);
1534 }
1535 
1536 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1537 {
1538   DMPolytopeType ct;
1539   PetscMPIInt    rank;
1540   PetscInt       cdim;
1541 
1542   PetscFunctionBegin;
1543   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1544   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1545   PetscCall(DMGetCoordinateDim(dm, &cdim));
1546   switch (ct) {
1547   case DM_POLYTOPE_SEGMENT:
1548   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1549     switch (cdim) {
1550     case 1:
1551     {
1552       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1553       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1554 
1555       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y,    PetscRealPart(coords[1]), y,    PETSC_DRAW_BLACK));
1556       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y+dy, PetscRealPart(coords[0]), y-dy, PETSC_DRAW_BLACK));
1557       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y+dy, PetscRealPart(coords[1]), y-dy, PETSC_DRAW_BLACK));
1558     }
1559     break;
1560     case 2:
1561     {
1562       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1563       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1564       const PetscReal l  = 0.1/PetscSqrtReal(dx*dx + dy*dy);
1565 
1566       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1567       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));
1568       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));
1569     }
1570     break;
1571     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1572     }
1573     break;
1574   case DM_POLYTOPE_TRIANGLE:
1575     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1576                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1577                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1578                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1579     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1580     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1581     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1582     break;
1583   case DM_POLYTOPE_QUADRILATERAL:
1584     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1585                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1586                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1587                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1588     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1589                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1590                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1591                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1592     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1593     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1594     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1595     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1596     break;
1597   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1598   }
1599   PetscFunctionReturn(0);
1600 }
1601 
1602 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1603 {
1604   DMPolytopeType ct;
1605   PetscReal      centroid[2] = {0., 0.};
1606   PetscMPIInt    rank;
1607   PetscInt       fillColor, v, e, d;
1608 
1609   PetscFunctionBegin;
1610   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1611   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1612   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2;
1613   switch (ct) {
1614   case DM_POLYTOPE_TRIANGLE:
1615     {
1616       PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1617 
1618       for (v = 0; v < 3; ++v) {centroid[0] += PetscRealPart(coords[v*2+0])/3.;centroid[1] += PetscRealPart(coords[v*2+1])/3.;}
1619       for (e = 0; e < 3; ++e) {
1620         refCoords[0] = refVertices[e*2+0];
1621         refCoords[1] = refVertices[e*2+1];
1622         for (d = 1; d <= edgeDiv; ++d) {
1623           refCoords[d*2+0] = refCoords[0] + (refVertices[(e+1)%3 * 2 + 0] - refCoords[0])*d/edgeDiv;
1624           refCoords[d*2+1] = refCoords[1] + (refVertices[(e+1)%3 * 2 + 1] - refCoords[1])*d/edgeDiv;
1625         }
1626         PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv+1, refCoords, edgeCoords));
1627         for (d = 0; d < edgeDiv; ++d) {
1628           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));
1629           PetscCall(PetscDrawLine(draw, edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], PETSC_DRAW_BLACK));
1630         }
1631       }
1632     }
1633     break;
1634   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1635   }
1636   PetscFunctionReturn(0);
1637 }
1638 
1639 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1640 {
1641   PetscDraw          draw;
1642   DM                 cdm;
1643   PetscSection       coordSection;
1644   Vec                coordinates;
1645   const PetscScalar *coords;
1646   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1647   PetscReal         *refCoords, *edgeCoords;
1648   PetscBool          isnull, drawAffine = PETSC_TRUE;
1649   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1650 
1651   PetscFunctionBegin;
1652   PetscCall(DMGetCoordinateDim(dm, &dim));
1653   PetscCheck(dim <= 2,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1654   PetscCall(PetscOptionsGetBool(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1655   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv+1)*dim, &refCoords, (edgeDiv+1)*dim, &edgeCoords));
1656   PetscCall(DMGetCoordinateDM(dm, &cdm));
1657   PetscCall(DMGetLocalSection(cdm, &coordSection));
1658   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1659   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1660   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1661 
1662   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1663   PetscCall(PetscDrawIsNull(draw, &isnull));
1664   if (isnull) PetscFunctionReturn(0);
1665   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1666 
1667   PetscCall(VecGetLocalSize(coordinates, &N));
1668   PetscCall(VecGetArrayRead(coordinates, &coords));
1669   for (c = 0; c < N; c += dim) {
1670     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1671     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1672   }
1673   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1674   PetscCall(MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm)));
1675   PetscCall(MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm)));
1676   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1677   PetscCall(PetscDrawClear(draw));
1678 
1679   for (c = cStart; c < cEnd; ++c) {
1680     PetscScalar *coords = NULL;
1681     PetscInt     numCoords;
1682 
1683     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1684     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1685     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1686     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1687   }
1688   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1689   PetscCall(PetscDrawFlush(draw));
1690   PetscCall(PetscDrawPause(draw));
1691   PetscCall(PetscDrawSave(draw));
1692   PetscFunctionReturn(0);
1693 }
1694 
1695 #if defined(PETSC_HAVE_EXODUSII)
1696 #include <exodusII.h>
1697 #include <petscviewerexodusii.h>
1698 #endif
1699 
1700 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1701 {
1702   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus;
1703   char           name[PETSC_MAX_PATH_LEN];
1704 
1705   PetscFunctionBegin;
1706   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1707   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1708   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii));
1709   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
1710   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
1711   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
1712   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
1713   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus));
1714   if (iascii) {
1715     PetscViewerFormat format;
1716     PetscCall(PetscViewerGetFormat(viewer, &format));
1717     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1718     else PetscCall(DMPlexView_Ascii(dm, viewer));
1719   } else if (ishdf5) {
1720 #if defined(PETSC_HAVE_HDF5)
1721     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1722 #else
1723     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1724 #endif
1725   } else if (isvtk) {
1726     PetscCall(DMPlexVTKWriteAll((PetscObject) dm,viewer));
1727   } else if (isdraw) {
1728     PetscCall(DMPlexView_Draw(dm, viewer));
1729   } else if (isglvis) {
1730     PetscCall(DMPlexView_GLVis(dm, viewer));
1731 #if defined(PETSC_HAVE_EXODUSII)
1732   } else if (isexodus) {
1733 /*
1734       exodusII requires that all sets be part of exactly one cell set.
1735       If the dm does not have a "Cell Sets" label defined, we create one
1736       with ID 1, containig all cells.
1737       Note that if the Cell Sets label is defined but does not cover all cells,
1738       we may still have a problem. This should probably be checked here or in the viewer;
1739     */
1740     PetscInt numCS;
1741     PetscCall(DMGetLabelSize(dm,"Cell Sets",&numCS));
1742     if (!numCS) {
1743       PetscInt cStart, cEnd, c;
1744       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1745       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1746       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1747     }
1748     PetscCall(DMView_PlexExodusII(dm, viewer));
1749 #endif
1750   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1751 
1752   /* Optionally view the partition */
1753   PetscCall(PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg));
1754   if (flg) {
1755     Vec ranks;
1756     PetscCall(DMPlexCreateRankField(dm, &ranks));
1757     PetscCall(VecView(ranks, viewer));
1758     PetscCall(VecDestroy(&ranks));
1759   }
1760   /* Optionally view a label */
1761   PetscCall(PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1762   if (flg) {
1763     DMLabel label;
1764     Vec     val;
1765 
1766     PetscCall(DMGetLabel(dm, name, &label));
1767     PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1768     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1769     PetscCall(VecView(val, viewer));
1770     PetscCall(VecDestroy(&val));
1771   }
1772   PetscFunctionReturn(0);
1773 }
1774 
1775 /*@
1776   DMPlexTopologyView - Saves a DMPlex topology into a file
1777 
1778   Collective on DM
1779 
1780   Input Parameters:
1781 + dm     - The DM whose topology is to be saved
1782 - viewer - The PetscViewer for saving
1783 
1784   Level: advanced
1785 
1786 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1787 @*/
1788 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1789 {
1790   PetscBool      ishdf5;
1791 
1792   PetscFunctionBegin;
1793   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1794   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1795   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1796   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView,viewer,0,0,0));
1797   if (ishdf5) {
1798 #if defined(PETSC_HAVE_HDF5)
1799     PetscViewerFormat format;
1800     PetscCall(PetscViewerGetFormat(viewer, &format));
1801     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1802       IS globalPointNumbering;
1803 
1804       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1805       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1806       PetscCall(ISDestroy(&globalPointNumbering));
1807     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1808 #else
1809     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1810 #endif
1811   }
1812   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView,viewer,0,0,0));
1813   PetscFunctionReturn(0);
1814 }
1815 
1816 /*@
1817   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1818 
1819   Collective on DM
1820 
1821   Input Parameters:
1822 + dm     - The DM whose coordinates are to be saved
1823 - viewer - The PetscViewer for saving
1824 
1825   Level: advanced
1826 
1827 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1828 @*/
1829 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1830 {
1831   PetscBool      ishdf5;
1832 
1833   PetscFunctionBegin;
1834   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1835   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1836   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1837   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView,viewer,0,0,0));
1838   if (ishdf5) {
1839 #if defined(PETSC_HAVE_HDF5)
1840     PetscViewerFormat format;
1841     PetscCall(PetscViewerGetFormat(viewer, &format));
1842     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1843       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1844     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1845 #else
1846     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1847 #endif
1848   }
1849   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView,viewer,0,0,0));
1850   PetscFunctionReturn(0);
1851 }
1852 
1853 /*@
1854   DMPlexLabelsView - Saves DMPlex labels into a file
1855 
1856   Collective on DM
1857 
1858   Input Parameters:
1859 + dm     - The DM whose labels are to be saved
1860 - viewer - The PetscViewer for saving
1861 
1862   Level: advanced
1863 
1864 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1865 @*/
1866 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1867 {
1868   PetscBool      ishdf5;
1869 
1870   PetscFunctionBegin;
1871   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1872   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1873   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1874   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView,viewer,0,0,0));
1875   if (ishdf5) {
1876 #if defined(PETSC_HAVE_HDF5)
1877     IS                globalPointNumbering;
1878     PetscViewerFormat format;
1879 
1880     PetscCall(PetscViewerGetFormat(viewer, &format));
1881     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1882       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1883       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1884       PetscCall(ISDestroy(&globalPointNumbering));
1885     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1886 #else
1887     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1888 #endif
1889   }
1890   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView,viewer,0,0,0));
1891   PetscFunctionReturn(0);
1892 }
1893 
1894 /*@
1895   DMPlexSectionView - Saves a section associated with a DMPlex
1896 
1897   Collective on DM
1898 
1899   Input Parameters:
1900 + dm         - The DM that contains the topology on which the section to be saved is defined
1901 . viewer     - The PetscViewer for saving
1902 - sectiondm  - The DM that contains the section to be saved
1903 
1904   Level: advanced
1905 
1906   Notes:
1907   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.
1908 
1909   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.
1910 
1911 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1912 @*/
1913 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1914 {
1915   PetscBool      ishdf5;
1916 
1917   PetscFunctionBegin;
1918   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1919   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1920   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1921   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
1922   PetscCall(PetscLogEventBegin(DMPLEX_SectionView,viewer,0,0,0));
1923   if (ishdf5) {
1924 #if defined(PETSC_HAVE_HDF5)
1925     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1926 #else
1927     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1928 #endif
1929   }
1930   PetscCall(PetscLogEventEnd(DMPLEX_SectionView,viewer,0,0,0));
1931   PetscFunctionReturn(0);
1932 }
1933 
1934 /*@
1935   DMPlexGlobalVectorView - Saves a global vector
1936 
1937   Collective on DM
1938 
1939   Input Parameters:
1940 + dm        - The DM that represents the topology
1941 . viewer    - The PetscViewer to save data with
1942 . sectiondm - The DM that contains the global section on which vec is defined
1943 - vec       - The global vector to be saved
1944 
1945   Level: advanced
1946 
1947   Notes:
1948   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.
1949 
1950   Typical calling sequence
1951 $       DMCreate(PETSC_COMM_WORLD, &dm);
1952 $       DMSetType(dm, DMPLEX);
1953 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1954 $       DMClone(dm, &sectiondm);
1955 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1956 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1957 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1958 $       PetscSectionSetChart(section, pStart, pEnd);
1959 $       PetscSectionSetUp(section);
1960 $       DMSetLocalSection(sectiondm, section);
1961 $       PetscSectionDestroy(&section);
1962 $       DMGetGlobalVector(sectiondm, &vec);
1963 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1964 $       DMPlexTopologyView(dm, viewer);
1965 $       DMPlexSectionView(dm, viewer, sectiondm);
1966 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1967 $       DMRestoreGlobalVector(sectiondm, &vec);
1968 $       DMDestroy(&sectiondm);
1969 $       DMDestroy(&dm);
1970 
1971 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1972 @*/
1973 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
1974 {
1975   PetscBool       ishdf5;
1976 
1977   PetscFunctionBegin;
1978   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1979   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1980   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1981   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1982   /* Check consistency */
1983   {
1984     PetscSection  section;
1985     PetscBool     includesConstraints;
1986     PetscInt      m, m1;
1987 
1988     PetscCall(VecGetLocalSize(vec, &m1));
1989     PetscCall(DMGetGlobalSection(sectiondm, &section));
1990     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1991     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
1992     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
1993     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
1994   }
1995   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1996   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView,viewer,0,0,0));
1997   if (ishdf5) {
1998 #if defined(PETSC_HAVE_HDF5)
1999     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2000 #else
2001     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2002 #endif
2003   }
2004   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView,viewer,0,0,0));
2005   PetscFunctionReturn(0);
2006 }
2007 
2008 /*@
2009   DMPlexLocalVectorView - Saves a local vector
2010 
2011   Collective on DM
2012 
2013   Input Parameters:
2014 + dm        - The DM that represents the topology
2015 . viewer    - The PetscViewer to save data with
2016 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2017 - vec       - The local vector to be saved
2018 
2019   Level: advanced
2020 
2021   Notes:
2022   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.
2023 
2024   Typical calling sequence
2025 $       DMCreate(PETSC_COMM_WORLD, &dm);
2026 $       DMSetType(dm, DMPLEX);
2027 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2028 $       DMClone(dm, &sectiondm);
2029 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2030 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2031 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2032 $       PetscSectionSetChart(section, pStart, pEnd);
2033 $       PetscSectionSetUp(section);
2034 $       DMSetLocalSection(sectiondm, section);
2035 $       DMGetLocalVector(sectiondm, &vec);
2036 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2037 $       DMPlexTopologyView(dm, viewer);
2038 $       DMPlexSectionView(dm, viewer, sectiondm);
2039 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2040 $       DMRestoreLocalVector(sectiondm, &vec);
2041 $       DMDestroy(&sectiondm);
2042 $       DMDestroy(&dm);
2043 
2044 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2045 @*/
2046 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2047 {
2048   PetscBool       ishdf5;
2049 
2050   PetscFunctionBegin;
2051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2052   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2053   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2054   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2055   /* Check consistency */
2056   {
2057     PetscSection  section;
2058     PetscBool     includesConstraints;
2059     PetscInt      m, m1;
2060 
2061     PetscCall(VecGetLocalSize(vec, &m1));
2062     PetscCall(DMGetLocalSection(sectiondm, &section));
2063     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2064     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2065     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2066     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2067   }
2068   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2069   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView,viewer,0,0,0));
2070   if (ishdf5) {
2071 #if defined(PETSC_HAVE_HDF5)
2072     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2073 #else
2074     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2075 #endif
2076   }
2077   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView,viewer,0,0,0));
2078   PetscFunctionReturn(0);
2079 }
2080 
2081 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2082 {
2083   PetscBool      ishdf5;
2084 
2085   PetscFunctionBegin;
2086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2087   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2088   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5));
2089   if (ishdf5) {
2090 #if defined(PETSC_HAVE_HDF5)
2091     PetscViewerFormat format;
2092     PetscCall(PetscViewerGetFormat(viewer, &format));
2093     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2094       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2095     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2096       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2097     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2098     PetscFunctionReturn(0);
2099 #else
2100     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2101 #endif
2102   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2103 }
2104 
2105 /*@
2106   DMPlexTopologyLoad - Loads a topology into a DMPlex
2107 
2108   Collective on DM
2109 
2110   Input Parameters:
2111 + dm     - The DM into which the topology is loaded
2112 - viewer - The PetscViewer for the saved topology
2113 
2114   Output Parameters:
2115 . 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
2116 
2117   Level: advanced
2118 
2119 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2120 @*/
2121 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2122 {
2123   PetscBool      ishdf5;
2124 
2125   PetscFunctionBegin;
2126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2127   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2128   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2129   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2130   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad,viewer,0,0,0));
2131   if (ishdf5) {
2132 #if defined(PETSC_HAVE_HDF5)
2133     PetscViewerFormat format;
2134     PetscCall(PetscViewerGetFormat(viewer, &format));
2135     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2136       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2137     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2138 #else
2139     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2140 #endif
2141   }
2142   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad,viewer,0,0,0));
2143   PetscFunctionReturn(0);
2144 }
2145 
2146 /*@
2147   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2148 
2149   Collective on DM
2150 
2151   Input Parameters:
2152 + dm     - The DM into which the coordinates are loaded
2153 . viewer - The PetscViewer for the saved coordinates
2154 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2155 
2156   Level: advanced
2157 
2158 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2159 @*/
2160 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2161 {
2162   PetscBool      ishdf5;
2163 
2164   PetscFunctionBegin;
2165   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2166   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2167   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2168   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2169   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2170   if (ishdf5) {
2171 #if defined(PETSC_HAVE_HDF5)
2172     PetscViewerFormat format;
2173     PetscCall(PetscViewerGetFormat(viewer, &format));
2174     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2175       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2176     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2177 #else
2178     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2179 #endif
2180   }
2181   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2182   PetscFunctionReturn(0);
2183 }
2184 
2185 /*@
2186   DMPlexLabelsLoad - Loads labels into a DMPlex
2187 
2188   Collective on DM
2189 
2190   Input Parameters:
2191 + dm     - The DM into which the labels are loaded
2192 . viewer - The PetscViewer for the saved labels
2193 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2194 
2195   Level: advanced
2196 
2197   Notes:
2198   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2199 
2200 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2201 @*/
2202 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2203 {
2204   PetscBool      ishdf5;
2205 
2206   PetscFunctionBegin;
2207   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2208   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2209   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2210   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2211   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad,viewer,0,0,0));
2212   if (ishdf5) {
2213 #if defined(PETSC_HAVE_HDF5)
2214     PetscViewerFormat format;
2215 
2216     PetscCall(PetscViewerGetFormat(viewer, &format));
2217     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2218       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2219     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2220 #else
2221     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2222 #endif
2223   }
2224   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad,viewer,0,0,0));
2225   PetscFunctionReturn(0);
2226 }
2227 
2228 /*@
2229   DMPlexSectionLoad - Loads section into a DMPlex
2230 
2231   Collective on DM
2232 
2233   Input Parameters:
2234 + dm          - The DM that represents the topology
2235 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2236 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2237 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2238 
2239   Output Parameters
2240 + 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)
2241 - 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)
2242 
2243   Level: advanced
2244 
2245   Notes:
2246   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.
2247 
2248   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.
2249 
2250   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.
2251 
2252   Example using 2 processes:
2253 $  NX (number of points on dm): 4
2254 $  sectionA                   : the on-disk section
2255 $  vecA                       : a vector associated with sectionA
2256 $  sectionB                   : sectiondm's local section constructed in this function
2257 $  vecB (local)               : a vector associated with sectiondm's local section
2258 $  vecB (global)              : a vector associated with sectiondm's global section
2259 $
2260 $                                     rank 0    rank 1
2261 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2262 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2263 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2264 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2265 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2266 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2267 $  sectionB->atlasDof             :     1 0 1 | 1 3
2268 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2269 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2270 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2271 $
2272 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2273 
2274 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2275 @*/
2276 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2277 {
2278   PetscBool      ishdf5;
2279 
2280   PetscFunctionBegin;
2281   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2282   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2283   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2284   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2285   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2286   if (localDofSF) PetscValidPointer(localDofSF, 6);
2287   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2288   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad,viewer,0,0,0));
2289   if (ishdf5) {
2290 #if defined(PETSC_HAVE_HDF5)
2291     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2292 #else
2293     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2294 #endif
2295   }
2296   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad,viewer,0,0,0));
2297   PetscFunctionReturn(0);
2298 }
2299 
2300 /*@
2301   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2302 
2303   Collective on DM
2304 
2305   Input Parameters:
2306 + dm        - The DM that represents the topology
2307 . viewer    - The PetscViewer that represents the on-disk vector data
2308 . sectiondm - The DM that contains the global section on which vec is defined
2309 . sf        - The SF that migrates the on-disk vector data into vec
2310 - vec       - The global vector to set values of
2311 
2312   Level: advanced
2313 
2314   Notes:
2315   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.
2316 
2317   Typical calling sequence
2318 $       DMCreate(PETSC_COMM_WORLD, &dm);
2319 $       DMSetType(dm, DMPLEX);
2320 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2321 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2322 $       DMClone(dm, &sectiondm);
2323 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2324 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2325 $       DMGetGlobalVector(sectiondm, &vec);
2326 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2327 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2328 $       DMRestoreGlobalVector(sectiondm, &vec);
2329 $       PetscSFDestroy(&gsf);
2330 $       PetscSFDestroy(&sfX);
2331 $       DMDestroy(&sectiondm);
2332 $       DMDestroy(&dm);
2333 
2334 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2335 @*/
2336 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2337 {
2338   PetscBool       ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2344   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2345   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2346   /* Check consistency */
2347   {
2348     PetscSection  section;
2349     PetscBool     includesConstraints;
2350     PetscInt      m, m1;
2351 
2352     PetscCall(VecGetLocalSize(vec, &m1));
2353     PetscCall(DMGetGlobalSection(sectiondm, &section));
2354     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2355     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2356     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2357     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2358   }
2359   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2360   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2361   if (ishdf5) {
2362 #if defined(PETSC_HAVE_HDF5)
2363     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2364 #else
2365     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2366 #endif
2367   }
2368   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2369   PetscFunctionReturn(0);
2370 }
2371 
2372 /*@
2373   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2374 
2375   Collective on DM
2376 
2377   Input Parameters:
2378 + dm        - The DM that represents the topology
2379 . viewer    - The PetscViewer that represents the on-disk vector data
2380 . sectiondm - The DM that contains the local section on which vec is defined
2381 . sf        - The SF that migrates the on-disk vector data into vec
2382 - vec       - The local vector to set values of
2383 
2384   Level: advanced
2385 
2386   Notes:
2387   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.
2388 
2389   Typical calling sequence
2390 $       DMCreate(PETSC_COMM_WORLD, &dm);
2391 $       DMSetType(dm, DMPLEX);
2392 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2393 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2394 $       DMClone(dm, &sectiondm);
2395 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2396 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2397 $       DMGetLocalVector(sectiondm, &vec);
2398 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2399 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2400 $       DMRestoreLocalVector(sectiondm, &vec);
2401 $       PetscSFDestroy(&lsf);
2402 $       PetscSFDestroy(&sfX);
2403 $       DMDestroy(&sectiondm);
2404 $       DMDestroy(&dm);
2405 
2406 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2407 @*/
2408 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2409 {
2410   PetscBool       ishdf5;
2411 
2412   PetscFunctionBegin;
2413   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2414   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2415   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2416   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2417   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2418   /* Check consistency */
2419   {
2420     PetscSection  section;
2421     PetscBool     includesConstraints;
2422     PetscInt      m, m1;
2423 
2424     PetscCall(VecGetLocalSize(vec, &m1));
2425     PetscCall(DMGetLocalSection(sectiondm, &section));
2426     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2427     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2428     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2429     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2430   }
2431   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2432   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2433   if (ishdf5) {
2434 #if defined(PETSC_HAVE_HDF5)
2435     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2436 #else
2437     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2438 #endif
2439   }
2440   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2441   PetscFunctionReturn(0);
2442 }
2443 
2444 PetscErrorCode DMDestroy_Plex(DM dm)
2445 {
2446   DM_Plex       *mesh = (DM_Plex*) dm->data;
2447 
2448   PetscFunctionBegin;
2449   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL));
2450   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL));
2451   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL));
2452   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMInterpolateSolution_C", NULL));
2453   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2454   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C", NULL));
2455   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeGetDefault_C", NULL));
2456   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeSetDefault_C", NULL));
2457   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"MatComputeNeumannOverlap_C",NULL));
2458   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderGetDefault_C", NULL));
2459   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderSetDefault_C", NULL));
2460   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C",NULL));
2461   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexSetOverlap_C",NULL));
2462   if (--mesh->refct > 0) PetscFunctionReturn(0);
2463   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2464   PetscCall(PetscFree(mesh->cones));
2465   PetscCall(PetscFree(mesh->coneOrientations));
2466   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2467   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2468   PetscCall(PetscFree(mesh->supports));
2469   PetscCall(PetscFree(mesh->facesTmp));
2470   PetscCall(PetscFree(mesh->tetgenOpts));
2471   PetscCall(PetscFree(mesh->triangleOpts));
2472   PetscCall(PetscFree(mesh->transformType));
2473   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2474   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2475   PetscCall(ISDestroy(&mesh->subpointIS));
2476   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2477   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2478   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2479   PetscCall(ISDestroy(&mesh->anchorIS));
2480   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2481   PetscCall(PetscFree(mesh->parents));
2482   PetscCall(PetscFree(mesh->childIDs));
2483   PetscCall(PetscSectionDestroy(&mesh->childSection));
2484   PetscCall(PetscFree(mesh->children));
2485   PetscCall(DMDestroy(&mesh->referenceTree));
2486   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2487   PetscCall(PetscFree(mesh->neighbors));
2488   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2489   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2490   PetscCall(PetscFree(mesh));
2491   PetscFunctionReturn(0);
2492 }
2493 
2494 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2495 {
2496   PetscSection           sectionGlobal;
2497   PetscInt               bs = -1, mbs;
2498   PetscInt               localSize, localStart = 0;
2499   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2500   MatType                mtype;
2501   ISLocalToGlobalMapping ltog;
2502 
2503   PetscFunctionBegin;
2504   PetscCall(MatInitializePackage());
2505   mtype = dm->mattype;
2506   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2507   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2508   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2509   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject) dm)));
2510   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2511   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2512   PetscCall(MatSetType(*J, mtype));
2513   PetscCall(MatSetFromOptions(*J));
2514   PetscCall(MatGetBlockSize(*J, &mbs));
2515   if (mbs > 1) bs = mbs;
2516   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2517   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2518   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2519   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2520   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2521   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2522   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2523   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2524   if (!isShell) {
2525     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2526     PetscInt  *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2527     PetscInt  pStart, pEnd, p, dof, cdof;
2528 
2529     PetscCall(DMGetLocalToGlobalMapping(dm,&ltog));
2530 
2531     PetscCall(PetscCalloc1(localSize, &pblocks));
2532     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2533     for (p = pStart; p < pEnd; ++p) {
2534       PetscInt bdof, offset;
2535 
2536       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2537       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2538       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2539       for (PetscInt i=0; i < dof - cdof; i++)
2540         pblocks[offset - localStart + i] = dof - cdof;
2541       dof  = dof < 0 ? -(dof+1) : dof;
2542       bdof = cdof && (dof-cdof) ? 1 : dof;
2543       if (dof) {
2544         if (bs < 0)          {bs = bdof;}
2545         else if (bs != bdof) {bs = 1;}
2546       }
2547     }
2548     /* Must have same blocksize on all procs (some might have no points) */
2549     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2550     bsLocal[1] = bs;
2551     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
2552     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2553     else bs = bsMinMax[0];
2554     bs = PetscMax(1,bs);
2555     PetscCall(MatSetLocalToGlobalMapping(*J,ltog,ltog));
2556     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2557       PetscCall(MatSetBlockSize(*J, bs));
2558       PetscCall(MatSetUp(*J));
2559     } else {
2560       PetscCall(PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu));
2561       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2562       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2563     }
2564     { // Consolidate blocks
2565       PetscInt nblocks = 0;
2566       for (PetscInt i=0; i<localSize; i += PetscMax(1, pblocks[i])) {
2567         if (pblocks[i] == 0) continue;
2568         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2569         for (PetscInt j=1; j<pblocks[i]; j++) {
2570            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]);
2571         }
2572       }
2573       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2574     }
2575     PetscCall(PetscFree(pblocks));
2576   }
2577   PetscCall(MatSetDM(*J, dm));
2578   PetscFunctionReturn(0);
2579 }
2580 
2581 /*@
2582   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2583 
2584   Not collective
2585 
2586   Input Parameter:
2587 . mesh - The DMPlex
2588 
2589   Output Parameters:
2590 . subsection - The subdomain section
2591 
2592   Level: developer
2593 
2594 .seealso:
2595 @*/
2596 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2597 {
2598   DM_Plex       *mesh = (DM_Plex*) dm->data;
2599 
2600   PetscFunctionBegin;
2601   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2602   if (!mesh->subdomainSection) {
2603     PetscSection section;
2604     PetscSF      sf;
2605 
2606     PetscCall(PetscSFCreate(PETSC_COMM_SELF,&sf));
2607     PetscCall(DMGetLocalSection(dm,&section));
2608     PetscCall(PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection));
2609     PetscCall(PetscSFDestroy(&sf));
2610   }
2611   *subsection = mesh->subdomainSection;
2612   PetscFunctionReturn(0);
2613 }
2614 
2615 /*@
2616   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2617 
2618   Not collective
2619 
2620   Input Parameter:
2621 . mesh - The DMPlex
2622 
2623   Output Parameters:
2624 + pStart - The first mesh point
2625 - pEnd   - The upper bound for mesh points
2626 
2627   Level: beginner
2628 
2629 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2630 @*/
2631 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2632 {
2633   DM_Plex       *mesh = (DM_Plex*) dm->data;
2634 
2635   PetscFunctionBegin;
2636   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2637   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2638   PetscFunctionReturn(0);
2639 }
2640 
2641 /*@
2642   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2643 
2644   Not collective
2645 
2646   Input Parameters:
2647 + mesh - The DMPlex
2648 . pStart - The first mesh point
2649 - pEnd   - The upper bound for mesh points
2650 
2651   Output Parameters:
2652 
2653   Level: beginner
2654 
2655 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2656 @*/
2657 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2658 {
2659   DM_Plex       *mesh = (DM_Plex*) dm->data;
2660 
2661   PetscFunctionBegin;
2662   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2663   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2664   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2665   PetscFunctionReturn(0);
2666 }
2667 
2668 /*@
2669   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2670 
2671   Not collective
2672 
2673   Input Parameters:
2674 + mesh - The DMPlex
2675 - p - The point, which must lie in the chart set with DMPlexSetChart()
2676 
2677   Output Parameter:
2678 . size - The cone size for point p
2679 
2680   Level: beginner
2681 
2682 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2683 @*/
2684 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2685 {
2686   DM_Plex       *mesh = (DM_Plex*) dm->data;
2687 
2688   PetscFunctionBegin;
2689   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2690   PetscValidIntPointer(size, 3);
2691   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2692   PetscFunctionReturn(0);
2693 }
2694 
2695 /*@
2696   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2697 
2698   Not collective
2699 
2700   Input Parameters:
2701 + mesh - The DMPlex
2702 . p - The point, which must lie in the chart set with DMPlexSetChart()
2703 - size - The cone size for point p
2704 
2705   Output Parameter:
2706 
2707   Note:
2708   This should be called after DMPlexSetChart().
2709 
2710   Level: beginner
2711 
2712 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2713 @*/
2714 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2715 {
2716   DM_Plex       *mesh = (DM_Plex*) dm->data;
2717 
2718   PetscFunctionBegin;
2719   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2720   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2721   PetscFunctionReturn(0);
2722 }
2723 
2724 /*@
2725   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2726 
2727   Not collective
2728 
2729   Input Parameters:
2730 + mesh - The DMPlex
2731 . p - The point, which must lie in the chart set with DMPlexSetChart()
2732 - size - The additional cone size for point p
2733 
2734   Output Parameter:
2735 
2736   Note:
2737   This should be called after DMPlexSetChart().
2738 
2739   Level: beginner
2740 
2741 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2742 @*/
2743 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2744 {
2745   DM_Plex       *mesh = (DM_Plex*) dm->data;
2746   PetscFunctionBegin;
2747   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2748   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2749   PetscFunctionReturn(0);
2750 }
2751 
2752 /*@C
2753   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2754 
2755   Not collective
2756 
2757   Input Parameters:
2758 + dm - The DMPlex
2759 - p - The point, which must lie in the chart set with DMPlexSetChart()
2760 
2761   Output Parameter:
2762 . cone - An array of points which are on the in-edges for point p
2763 
2764   Level: beginner
2765 
2766   Fortran Notes:
2767   Since it returns an array, this routine is only available in Fortran 90, and you must
2768   include petsc.h90 in your code.
2769   You must also call DMPlexRestoreCone() after you finish using the returned array.
2770   DMPlexRestoreCone() is not needed/available in C.
2771 
2772 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2773 @*/
2774 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2775 {
2776   DM_Plex       *mesh = (DM_Plex*) dm->data;
2777   PetscInt       off;
2778 
2779   PetscFunctionBegin;
2780   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2781   PetscValidPointer(cone, 3);
2782   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2783   *cone = &mesh->cones[off];
2784   PetscFunctionReturn(0);
2785 }
2786 
2787 /*@C
2788   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2789 
2790   Not collective
2791 
2792   Input Parameters:
2793 + dm - The DMPlex
2794 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2795 
2796   Output Parameters:
2797 + pConesSection - PetscSection describing the layout of pCones
2798 - pCones - An array of points which are on the in-edges for the point set p
2799 
2800   Level: intermediate
2801 
2802 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2803 @*/
2804 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2805 {
2806   PetscSection        cs, newcs;
2807   PetscInt            *cones;
2808   PetscInt            *newarr=NULL;
2809   PetscInt            n;
2810 
2811   PetscFunctionBegin;
2812   PetscCall(DMPlexGetCones(dm, &cones));
2813   PetscCall(DMPlexGetConeSection(dm, &cs));
2814   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL));
2815   if (pConesSection) *pConesSection = newcs;
2816   if (pCones) {
2817     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2818     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2819   }
2820   PetscFunctionReturn(0);
2821 }
2822 
2823 /*@
2824   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2825 
2826   Not collective
2827 
2828   Input Parameters:
2829 + dm - The DMPlex
2830 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2831 
2832   Output Parameter:
2833 . expandedPoints - An array of vertices recursively expanded from input points
2834 
2835   Level: advanced
2836 
2837   Notes:
2838   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2839   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2840 
2841 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2842 @*/
2843 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2844 {
2845   IS                  *expandedPointsAll;
2846   PetscInt            depth;
2847 
2848   PetscFunctionBegin;
2849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2850   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2851   PetscValidPointer(expandedPoints, 3);
2852   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2853   *expandedPoints = expandedPointsAll[0];
2854   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2855   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2856   PetscFunctionReturn(0);
2857 }
2858 
2859 /*@
2860   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).
2861 
2862   Not collective
2863 
2864   Input Parameters:
2865 + dm - The DMPlex
2866 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2867 
2868   Output Parameters:
2869 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2870 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2871 - sections - (optional) An array of sections which describe mappings from points to their cone points
2872 
2873   Level: advanced
2874 
2875   Notes:
2876   Like DMPlexGetConeTuple() but recursive.
2877 
2878   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.
2879   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2880 
2881   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:
2882   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2883   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2884 
2885 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2886 @*/
2887 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2888 {
2889   const PetscInt      *arr0=NULL, *cone=NULL;
2890   PetscInt            *arr=NULL, *newarr=NULL;
2891   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
2892   IS                  *expandedPoints_;
2893   PetscSection        *sections_;
2894 
2895   PetscFunctionBegin;
2896   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2897   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2898   if (depth) PetscValidIntPointer(depth, 3);
2899   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2900   if (sections) PetscValidPointer(sections, 5);
2901   PetscCall(ISGetLocalSize(points, &n));
2902   PetscCall(ISGetIndices(points, &arr0));
2903   PetscCall(DMPlexGetDepth(dm, &depth_));
2904   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2905   PetscCall(PetscCalloc1(depth_, &sections_));
2906   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
2907   for (d=depth_-1; d>=0; d--) {
2908     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2909     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2910     for (i=0; i<n; i++) {
2911       PetscCall(DMPlexGetDepthStratum(dm, d+1, &start, &end));
2912       if (arr[i] >= start && arr[i] < end) {
2913         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2914         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2915       } else {
2916         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2917       }
2918     }
2919     PetscCall(PetscSectionSetUp(sections_[d]));
2920     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2921     PetscCall(PetscMalloc1(newn, &newarr));
2922     for (i=0; i<n; i++) {
2923       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2924       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2925       if (cn > 1) {
2926         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2927         PetscCall(PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt)));
2928       } else {
2929         newarr[co] = arr[i];
2930       }
2931     }
2932     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2933     arr = newarr;
2934     n = newn;
2935   }
2936   PetscCall(ISRestoreIndices(points, &arr0));
2937   *depth = depth_;
2938   if (expandedPoints) *expandedPoints = expandedPoints_;
2939   else {
2940     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2941     PetscCall(PetscFree(expandedPoints_));
2942   }
2943   if (sections) *sections = sections_;
2944   else {
2945     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2946     PetscCall(PetscFree(sections_));
2947   }
2948   PetscFunctionReturn(0);
2949 }
2950 
2951 /*@
2952   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2953 
2954   Not collective
2955 
2956   Input Parameters:
2957 + dm - The DMPlex
2958 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2959 
2960   Output Parameters:
2961 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2962 . expandedPoints - (optional) An array of recursively expanded cones
2963 - sections - (optional) An array of sections which describe mappings from points to their cone points
2964 
2965   Level: advanced
2966 
2967   Notes:
2968   See DMPlexGetConeRecursive() for details.
2969 
2970 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2971 @*/
2972 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2973 {
2974   PetscInt            d, depth_;
2975 
2976   PetscFunctionBegin;
2977   PetscCall(DMPlexGetDepth(dm, &depth_));
2978   PetscCheck(!depth || *depth == depth_,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2979   if (depth) *depth = 0;
2980   if (expandedPoints) {
2981     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2982     PetscCall(PetscFree(*expandedPoints));
2983   }
2984   if (sections)  {
2985     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2986     PetscCall(PetscFree(*sections));
2987   }
2988   PetscFunctionReturn(0);
2989 }
2990 
2991 /*@
2992   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
2993 
2994   Not collective
2995 
2996   Input Parameters:
2997 + mesh - The DMPlex
2998 . p - The point, which must lie in the chart set with DMPlexSetChart()
2999 - cone - An array of points which are on the in-edges for point p
3000 
3001   Output Parameter:
3002 
3003   Note:
3004   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3005 
3006   Level: beginner
3007 
3008 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3009 @*/
3010 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3011 {
3012   DM_Plex       *mesh = (DM_Plex*) dm->data;
3013   PetscInt       pStart, pEnd;
3014   PetscInt       dof, off, c;
3015 
3016   PetscFunctionBegin;
3017   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3018   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3019   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3020   if (dof) PetscValidIntPointer(cone, 3);
3021   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3022   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);
3023   for (c = 0; c < dof; ++c) {
3024     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);
3025     mesh->cones[off+c] = cone[c];
3026   }
3027   PetscFunctionReturn(0);
3028 }
3029 
3030 /*@C
3031   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3032 
3033   Not collective
3034 
3035   Input Parameters:
3036 + mesh - The DMPlex
3037 - p - The point, which must lie in the chart set with DMPlexSetChart()
3038 
3039   Output Parameter:
3040 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3041                     integer giving the prescription for cone traversal.
3042 
3043   Level: beginner
3044 
3045   Notes:
3046   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3047   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3048   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3049   with the identity.
3050 
3051   Fortran Notes:
3052   Since it returns an array, this routine is only available in Fortran 90, and you must
3053   include petsc.h90 in your code.
3054   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3055   DMPlexRestoreConeOrientation() is not needed/available in C.
3056 
3057 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3058 @*/
3059 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3060 {
3061   DM_Plex       *mesh = (DM_Plex*) dm->data;
3062   PetscInt       off;
3063 
3064   PetscFunctionBegin;
3065   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3066   if (PetscDefined(USE_DEBUG)) {
3067     PetscInt dof;
3068     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3069     if (dof) PetscValidPointer(coneOrientation, 3);
3070   }
3071   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3072 
3073   *coneOrientation = &mesh->coneOrientations[off];
3074   PetscFunctionReturn(0);
3075 }
3076 
3077 /*@
3078   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3079 
3080   Not collective
3081 
3082   Input Parameters:
3083 + mesh - The DMPlex
3084 . p - The point, which must lie in the chart set with DMPlexSetChart()
3085 - coneOrientation - An array of orientations
3086   Output Parameter:
3087 
3088   Notes:
3089   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3090 
3091   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3092 
3093   Level: beginner
3094 
3095 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3096 @*/
3097 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3098 {
3099   DM_Plex       *mesh = (DM_Plex*) dm->data;
3100   PetscInt       pStart, pEnd;
3101   PetscInt       dof, off, c;
3102 
3103   PetscFunctionBegin;
3104   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3105   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3106   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3107   if (dof) PetscValidIntPointer(coneOrientation, 3);
3108   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3109   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);
3110   for (c = 0; c < dof; ++c) {
3111     PetscInt cdof, o = coneOrientation[c];
3112 
3113     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof));
3114     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);
3115     mesh->coneOrientations[off+c] = o;
3116   }
3117   PetscFunctionReturn(0);
3118 }
3119 
3120 /*@
3121   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3122 
3123   Not collective
3124 
3125   Input Parameters:
3126 + mesh - The DMPlex
3127 . p - The point, which must lie in the chart set with DMPlexSetChart()
3128 . conePos - The local index in the cone where the point should be put
3129 - conePoint - The mesh point to insert
3130 
3131   Level: beginner
3132 
3133 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3134 @*/
3135 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3136 {
3137   DM_Plex       *mesh = (DM_Plex*) dm->data;
3138   PetscInt       pStart, pEnd;
3139   PetscInt       dof, off;
3140 
3141   PetscFunctionBegin;
3142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3143   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3144   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);
3145   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);
3146   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3147   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3148   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);
3149   mesh->cones[off+conePos] = conePoint;
3150   PetscFunctionReturn(0);
3151 }
3152 
3153 /*@
3154   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3155 
3156   Not collective
3157 
3158   Input Parameters:
3159 + mesh - The DMPlex
3160 . p - The point, which must lie in the chart set with DMPlexSetChart()
3161 . conePos - The local index in the cone where the point should be put
3162 - coneOrientation - The point orientation to insert
3163 
3164   Level: beginner
3165 
3166   Notes:
3167   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3168 
3169 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3170 @*/
3171 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3172 {
3173   DM_Plex       *mesh = (DM_Plex*) dm->data;
3174   PetscInt       pStart, pEnd;
3175   PetscInt       dof, off;
3176 
3177   PetscFunctionBegin;
3178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3179   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3180   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);
3181   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3182   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3183   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);
3184   mesh->coneOrientations[off+conePos] = coneOrientation;
3185   PetscFunctionReturn(0);
3186 }
3187 
3188 /*@
3189   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3190 
3191   Not collective
3192 
3193   Input Parameters:
3194 + mesh - The DMPlex
3195 - p - The point, which must lie in the chart set with DMPlexSetChart()
3196 
3197   Output Parameter:
3198 . size - The support size for point p
3199 
3200   Level: beginner
3201 
3202 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3203 @*/
3204 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3205 {
3206   DM_Plex       *mesh = (DM_Plex*) dm->data;
3207 
3208   PetscFunctionBegin;
3209   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3210   PetscValidIntPointer(size, 3);
3211   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3212   PetscFunctionReturn(0);
3213 }
3214 
3215 /*@
3216   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3217 
3218   Not collective
3219 
3220   Input Parameters:
3221 + mesh - The DMPlex
3222 . p - The point, which must lie in the chart set with DMPlexSetChart()
3223 - size - The support size for point p
3224 
3225   Output Parameter:
3226 
3227   Note:
3228   This should be called after DMPlexSetChart().
3229 
3230   Level: beginner
3231 
3232 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3233 @*/
3234 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3235 {
3236   DM_Plex       *mesh = (DM_Plex*) dm->data;
3237 
3238   PetscFunctionBegin;
3239   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3240   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3241   PetscFunctionReturn(0);
3242 }
3243 
3244 /*@C
3245   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3246 
3247   Not collective
3248 
3249   Input Parameters:
3250 + mesh - The DMPlex
3251 - p - The point, which must lie in the chart set with DMPlexSetChart()
3252 
3253   Output Parameter:
3254 . support - An array of points which are on the out-edges for point p
3255 
3256   Level: beginner
3257 
3258   Fortran Notes:
3259   Since it returns an array, this routine is only available in Fortran 90, and you must
3260   include petsc.h90 in your code.
3261   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3262   DMPlexRestoreSupport() is not needed/available in C.
3263 
3264 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3265 @*/
3266 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3267 {
3268   DM_Plex       *mesh = (DM_Plex*) dm->data;
3269   PetscInt       off;
3270 
3271   PetscFunctionBegin;
3272   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3273   PetscValidPointer(support, 3);
3274   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3275   *support = &mesh->supports[off];
3276   PetscFunctionReturn(0);
3277 }
3278 
3279 /*@
3280   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3281 
3282   Not collective
3283 
3284   Input Parameters:
3285 + mesh - The DMPlex
3286 . p - The point, which must lie in the chart set with DMPlexSetChart()
3287 - support - An array of points which are on the out-edges for point p
3288 
3289   Output Parameter:
3290 
3291   Note:
3292   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3293 
3294   Level: beginner
3295 
3296 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3297 @*/
3298 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3299 {
3300   DM_Plex       *mesh = (DM_Plex*) dm->data;
3301   PetscInt       pStart, pEnd;
3302   PetscInt       dof, off, c;
3303 
3304   PetscFunctionBegin;
3305   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3306   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3307   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3308   if (dof) PetscValidIntPointer(support, 3);
3309   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3310   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);
3311   for (c = 0; c < dof; ++c) {
3312     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);
3313     mesh->supports[off+c] = support[c];
3314   }
3315   PetscFunctionReturn(0);
3316 }
3317 
3318 /*@
3319   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3320 
3321   Not collective
3322 
3323   Input Parameters:
3324 + mesh - The DMPlex
3325 . p - The point, which must lie in the chart set with DMPlexSetChart()
3326 . supportPos - The local index in the cone where the point should be put
3327 - supportPoint - The mesh point to insert
3328 
3329   Level: beginner
3330 
3331 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3332 @*/
3333 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3334 {
3335   DM_Plex       *mesh = (DM_Plex*) dm->data;
3336   PetscInt       pStart, pEnd;
3337   PetscInt       dof, off;
3338 
3339   PetscFunctionBegin;
3340   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3341   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3342   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3343   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3344   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);
3345   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);
3346   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);
3347   mesh->supports[off+supportPos] = supportPoint;
3348   PetscFunctionReturn(0);
3349 }
3350 
3351 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3352 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3353 {
3354   switch (ct) {
3355     case DM_POLYTOPE_SEGMENT:
3356       if (o == -1) return -2;
3357       break;
3358     case DM_POLYTOPE_TRIANGLE:
3359       if (o == -3) return -1;
3360       if (o == -2) return -3;
3361       if (o == -1) return -2;
3362       break;
3363     case DM_POLYTOPE_QUADRILATERAL:
3364       if (o == -4) return -2;
3365       if (o == -3) return -1;
3366       if (o == -2) return -4;
3367       if (o == -1) return -3;
3368       break;
3369     default: return o;
3370   }
3371   return o;
3372 }
3373 
3374 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3375 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3376 {
3377   switch (ct) {
3378     case DM_POLYTOPE_SEGMENT:
3379       if ((o == -2) || (o == 1)) return -1;
3380       if (o == -1) return 0;
3381       break;
3382     case DM_POLYTOPE_TRIANGLE:
3383       if (o == -3) return -2;
3384       if (o == -2) return -1;
3385       if (o == -1) return -3;
3386       break;
3387     case DM_POLYTOPE_QUADRILATERAL:
3388       if (o == -4) return -2;
3389       if (o == -3) return -1;
3390       if (o == -2) return -4;
3391       if (o == -1) return -3;
3392       break;
3393     default: return o;
3394   }
3395   return o;
3396 }
3397 
3398 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3399 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3400 {
3401   PetscInt       pStart, pEnd, p;
3402 
3403   PetscFunctionBegin;
3404   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3405   for (p = pStart; p < pEnd; ++p) {
3406     const PetscInt *cone, *ornt;
3407     PetscInt        coneSize, c;
3408 
3409     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3410     PetscCall(DMPlexGetCone(dm, p, &cone));
3411     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3412     for (c = 0; c < coneSize; ++c) {
3413       DMPolytopeType ct;
3414       const PetscInt o = ornt[c];
3415 
3416       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3417       switch (ct) {
3418         case DM_POLYTOPE_SEGMENT:
3419           if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3420           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3421           break;
3422         case DM_POLYTOPE_TRIANGLE:
3423           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3424           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3425           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3426           break;
3427         case DM_POLYTOPE_QUADRILATERAL:
3428           if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3429           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3430           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3431           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3432           break;
3433         default: break;
3434       }
3435     }
3436   }
3437   PetscFunctionReturn(0);
3438 }
3439 
3440 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3441 {
3442   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3443   PetscInt       *closure;
3444   const PetscInt *tmp = NULL, *tmpO = NULL;
3445   PetscInt        off = 0, tmpSize, t;
3446 
3447   PetscFunctionBeginHot;
3448   if (ornt) {
3449     PetscCall(DMPlexGetCellType(dm, p, &ct));
3450     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3451   }
3452   if (*points) {
3453     closure = *points;
3454   } else {
3455     PetscInt maxConeSize, maxSupportSize;
3456     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3457     PetscCall(DMGetWorkArray(dm, 2*(PetscMax(maxConeSize, maxSupportSize)+1), MPIU_INT, &closure));
3458   }
3459   if (useCone) {
3460     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3461     PetscCall(DMPlexGetCone(dm, p, &tmp));
3462     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3463   } else {
3464     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3465     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3466   }
3467   if (ct == DM_POLYTOPE_UNKNOWN) {
3468     closure[off++] = p;
3469     closure[off++] = 0;
3470     for (t = 0; t < tmpSize; ++t) {
3471       closure[off++] = tmp[t];
3472       closure[off++] = tmpO ? tmpO[t] : 0;
3473     }
3474   } else {
3475     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3476 
3477     /* We assume that cells with a valid type have faces with a valid type */
3478     closure[off++] = p;
3479     closure[off++] = ornt;
3480     for (t = 0; t < tmpSize; ++t) {
3481       DMPolytopeType ft;
3482 
3483       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3484       closure[off++] = tmp[arr[t]];
3485       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3486     }
3487   }
3488   if (numPoints) *numPoints = tmpSize+1;
3489   if (points)    *points    = closure;
3490   PetscFunctionReturn(0);
3491 }
3492 
3493 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3494 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3495 {
3496   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3497   const PetscInt *cone, *ornt;
3498   PetscInt       *pts,  *closure = NULL;
3499   DMPolytopeType  ft;
3500   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3501   PetscInt        dim, coneSize, c, d, clSize, cl;
3502 
3503   PetscFunctionBeginHot;
3504   PetscCall(DMGetDimension(dm, &dim));
3505   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3506   PetscCall(DMPlexGetCone(dm, point, &cone));
3507   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3508   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3509   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    dim+1)-1)/(maxConeSize-1))    : dim+1;
3510   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim+1)-1)/(maxSupportSize-1)) : dim+1;
3511   maxSize       = PetscMax(coneSeries, supportSeries);
3512   if (*points) {pts  = *points;}
3513   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &pts));
3514   c    = 0;
3515   pts[c++] = point;
3516   pts[c++] = o;
3517   PetscCall(DMPlexGetCellType(dm, cone[arr[0*2+0]], &ft));
3518   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[0*2+1], ornt[0]), useCone, &clSize, &closure));
3519   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3520   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[1*2+1], ornt[1]), useCone, &clSize, &closure));
3521   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3522   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3523   for (d = 2; d < coneSize; ++d) {
3524     PetscCall(DMPlexGetCellType(dm, cone[arr[d*2+0]], &ft));
3525     pts[c++] = cone[arr[d*2+0]];
3526     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]);
3527   }
3528   if (dim >= 3) {
3529     for (d = 2; d < coneSize; ++d) {
3530       const PetscInt  fpoint = cone[arr[d*2+0]];
3531       const PetscInt *fcone, *fornt;
3532       PetscInt        fconeSize, fc, i;
3533 
3534       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3535       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]));
3536       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3537       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3538       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3539       for (fc = 0; fc < fconeSize; ++fc) {
3540         const PetscInt cp = fcone[farr[fc*2+0]];
3541         const PetscInt co = farr[fc*2+1];
3542 
3543         for (i = 0; i < c; i += 2) if (pts[i] == cp) break;
3544         if (i == c) {
3545           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3546           pts[c++] = cp;
3547           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc*2+0]]);
3548         }
3549       }
3550     }
3551   }
3552   *numPoints = c/2;
3553   *points    = pts;
3554   PetscFunctionReturn(0);
3555 }
3556 
3557 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3558 {
3559   DMPolytopeType ct;
3560   PetscInt      *closure, *fifo;
3561   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3562   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3563   PetscInt       depth, maxSize;
3564 
3565   PetscFunctionBeginHot;
3566   PetscCall(DMPlexGetDepth(dm, &depth));
3567   if (depth == 1) {
3568     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3569     PetscFunctionReturn(0);
3570   }
3571   PetscCall(DMPlexGetCellType(dm, p, &ct));
3572   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3573   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3574     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3575     PetscFunctionReturn(0);
3576   }
3577   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3578   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    depth+1)-1)/(maxConeSize-1))    : depth+1;
3579   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth+1)-1)/(maxSupportSize-1)) : depth+1;
3580   maxSize       = PetscMax(coneSeries, supportSeries);
3581   PetscCall(DMGetWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3582   if (*points) {closure = *points;}
3583   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &closure));
3584   closure[closureSize++] = p;
3585   closure[closureSize++] = ornt;
3586   fifo[fifoSize++]       = p;
3587   fifo[fifoSize++]       = ornt;
3588   fifo[fifoSize++]       = ct;
3589   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3590   while (fifoSize - fifoStart) {
3591     const PetscInt       q    = fifo[fifoStart++];
3592     const PetscInt       o    = fifo[fifoStart++];
3593     const DMPolytopeType qt   = (DMPolytopeType) fifo[fifoStart++];
3594     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3595     const PetscInt      *tmp, *tmpO;
3596     PetscInt             tmpSize, t;
3597 
3598     if (PetscDefined(USE_DEBUG)) {
3599       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt)/2;
3600       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);
3601     }
3602     if (useCone) {
3603       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3604       PetscCall(DMPlexGetCone(dm, q, &tmp));
3605       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3606     } else {
3607       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3608       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3609       tmpO = NULL;
3610     }
3611     for (t = 0; t < tmpSize; ++t) {
3612       const PetscInt ip = useCone && qarr ? qarr[t*2]   : t;
3613       const PetscInt io = useCone && qarr ? qarr[t*2+1] : 0;
3614       const PetscInt cp = tmp[ip];
3615       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3616       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3617       PetscInt       c;
3618 
3619       /* Check for duplicate */
3620       for (c = 0; c < closureSize; c += 2) {
3621         if (closure[c] == cp) break;
3622       }
3623       if (c == closureSize) {
3624         closure[closureSize++] = cp;
3625         closure[closureSize++] = co;
3626         fifo[fifoSize++]       = cp;
3627         fifo[fifoSize++]       = co;
3628         fifo[fifoSize++]       = ct;
3629       }
3630     }
3631   }
3632   PetscCall(DMRestoreWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3633   if (numPoints) *numPoints = closureSize/2;
3634   if (points)    *points    = closure;
3635   PetscFunctionReturn(0);
3636 }
3637 
3638 /*@C
3639   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3640 
3641   Not collective
3642 
3643   Input Parameters:
3644 + dm      - The DMPlex
3645 . p       - The mesh point
3646 - useCone - PETSC_TRUE for the closure, otherwise return the star
3647 
3648   Input/Output Parameter:
3649 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3650            if NULL on input, internal storage will be returned, otherwise the provided array is used
3651 
3652   Output Parameter:
3653 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3654 
3655   Note:
3656   If using internal storage (points is NULL on input), each call overwrites the last output.
3657 
3658   Fortran Notes:
3659   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3660 
3661   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3662 
3663   Level: beginner
3664 
3665 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3666 @*/
3667 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3668 {
3669   PetscFunctionBeginHot;
3670   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3671   if (numPoints) PetscValidIntPointer(numPoints, 4);
3672   if (points)    PetscValidPointer(points, 5);
3673   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3674   PetscFunctionReturn(0);
3675 }
3676 
3677 /*@C
3678   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3679 
3680   Not collective
3681 
3682   Input Parameters:
3683 + dm        - The DMPlex
3684 . p         - The mesh point
3685 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3686 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3687 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3688 
3689   Note:
3690   If not using internal storage (points is not NULL on input), this call is unnecessary
3691 
3692   Fortran Notes:
3693   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3694 
3695   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3696 
3697   Level: beginner
3698 
3699 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3700 @*/
3701 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3702 {
3703   PetscFunctionBeginHot;
3704   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3705   if (numPoints) *numPoints = 0;
3706   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3707   PetscFunctionReturn(0);
3708 }
3709 
3710 /*@
3711   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3712 
3713   Not collective
3714 
3715   Input Parameter:
3716 . mesh - The DMPlex
3717 
3718   Output Parameters:
3719 + maxConeSize - The maximum number of in-edges
3720 - maxSupportSize - The maximum number of out-edges
3721 
3722   Level: beginner
3723 
3724 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3725 @*/
3726 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3727 {
3728   DM_Plex *mesh = (DM_Plex*) dm->data;
3729 
3730   PetscFunctionBegin;
3731   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3732   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3733   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3734   PetscFunctionReturn(0);
3735 }
3736 
3737 PetscErrorCode DMSetUp_Plex(DM dm)
3738 {
3739   DM_Plex       *mesh = (DM_Plex*) dm->data;
3740   PetscInt       size, maxSupportSize;
3741 
3742   PetscFunctionBegin;
3743   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3744   PetscCall(PetscSectionSetUp(mesh->coneSection));
3745   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3746   PetscCall(PetscMalloc1(size, &mesh->cones));
3747   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3748   PetscCall(PetscLogObjectMemory((PetscObject) dm, size*2*sizeof(PetscInt)));
3749   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3750   if (maxSupportSize) {
3751     PetscCall(PetscSectionSetUp(mesh->supportSection));
3752     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3753     PetscCall(PetscMalloc1(size, &mesh->supports));
3754     PetscCall(PetscLogObjectMemory((PetscObject) dm, size*sizeof(PetscInt)));
3755   }
3756   PetscFunctionReturn(0);
3757 }
3758 
3759 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3760 {
3761   PetscFunctionBegin;
3762   if (subdm) PetscCall(DMClone(dm, subdm));
3763   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3764   if (subdm) {(*subdm)->useNatural = dm->useNatural;}
3765   if (dm->useNatural && dm->sfMigration) {
3766     PetscSF        sfMigrationInv,sfNatural;
3767     PetscSection   section, sectionSeq;
3768 
3769     (*subdm)->sfMigration = dm->sfMigration;
3770     PetscCall(PetscObjectReference((PetscObject) dm->sfMigration));
3771     PetscCall(DMGetLocalSection((*subdm), &section));
3772     PetscCall(PetscSFCreateInverseSF((*subdm)->sfMigration, &sfMigrationInv));
3773     PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*subdm)), &sectionSeq));
3774     PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3775 
3776     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, sectionSeq, (*subdm)->sfMigration, &sfNatural));
3777     (*subdm)->sfNatural = sfNatural;
3778     PetscCall(PetscSectionDestroy(&sectionSeq));
3779     PetscCall(PetscSFDestroy(&sfMigrationInv));
3780   }
3781   PetscFunctionReturn(0);
3782 }
3783 
3784 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3785 {
3786   PetscInt       i = 0;
3787 
3788   PetscFunctionBegin;
3789   PetscCall(DMClone(dms[0], superdm));
3790   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3791   (*superdm)->useNatural = PETSC_FALSE;
3792   for (i = 0; i < len; i++) {
3793     if (dms[i]->useNatural && dms[i]->sfMigration) {
3794       PetscSF        sfMigrationInv,sfNatural;
3795       PetscSection   section, sectionSeq;
3796 
3797       (*superdm)->sfMigration = dms[i]->sfMigration;
3798       PetscCall(PetscObjectReference((PetscObject) dms[i]->sfMigration));
3799       (*superdm)->useNatural = PETSC_TRUE;
3800       PetscCall(DMGetLocalSection((*superdm), &section));
3801       PetscCall(PetscSFCreateInverseSF((*superdm)->sfMigration, &sfMigrationInv));
3802       PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*superdm)), &sectionSeq));
3803       PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3804 
3805       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, sectionSeq, (*superdm)->sfMigration, &sfNatural));
3806       (*superdm)->sfNatural = sfNatural;
3807       PetscCall(PetscSectionDestroy(&sectionSeq));
3808       PetscCall(PetscSFDestroy(&sfMigrationInv));
3809       break;
3810     }
3811   }
3812   PetscFunctionReturn(0);
3813 }
3814 
3815 /*@
3816   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3817 
3818   Not collective
3819 
3820   Input Parameter:
3821 . mesh - The DMPlex
3822 
3823   Output Parameter:
3824 
3825   Note:
3826   This should be called after all calls to DMPlexSetCone()
3827 
3828   Level: beginner
3829 
3830 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3831 @*/
3832 PetscErrorCode DMPlexSymmetrize(DM dm)
3833 {
3834   DM_Plex       *mesh = (DM_Plex*) dm->data;
3835   PetscInt      *offsets;
3836   PetscInt       supportSize;
3837   PetscInt       pStart, pEnd, p;
3838 
3839   PetscFunctionBegin;
3840   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3841   PetscCheck(!mesh->supports,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3842   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0));
3843   /* Calculate support sizes */
3844   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3845   for (p = pStart; p < pEnd; ++p) {
3846     PetscInt dof, off, c;
3847 
3848     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3849     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3850     for (c = off; c < off+dof; ++c) {
3851       PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3852     }
3853   }
3854   PetscCall(PetscSectionSetUp(mesh->supportSection));
3855   /* Calculate supports */
3856   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3857   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3858   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3859   for (p = pStart; p < pEnd; ++p) {
3860     PetscInt dof, off, c;
3861 
3862     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3863     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3864     for (c = off; c < off+dof; ++c) {
3865       const PetscInt q = mesh->cones[c];
3866       PetscInt       offS;
3867 
3868       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3869 
3870       mesh->supports[offS+offsets[q]] = p;
3871       ++offsets[q];
3872     }
3873   }
3874   PetscCall(PetscFree(offsets));
3875   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0));
3876   PetscFunctionReturn(0);
3877 }
3878 
3879 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3880 {
3881   IS             stratumIS;
3882 
3883   PetscFunctionBegin;
3884   if (pStart >= pEnd) PetscFunctionReturn(0);
3885   if (PetscDefined(USE_DEBUG)) {
3886     PetscInt  qStart, qEnd, numLevels, level;
3887     PetscBool overlap = PETSC_FALSE;
3888     PetscCall(DMLabelGetNumValues(label, &numLevels));
3889     for (level = 0; level < numLevels; level++) {
3890       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3891       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
3892     }
3893     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);
3894   }
3895   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS));
3896   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3897   PetscCall(ISDestroy(&stratumIS));
3898   PetscFunctionReturn(0);
3899 }
3900 
3901 /*@
3902   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3903   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3904   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3905   the DAG.
3906 
3907   Collective on dm
3908 
3909   Input Parameter:
3910 . mesh - The DMPlex
3911 
3912   Output Parameter:
3913 
3914   Notes:
3915   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3916   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3917   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3918   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3919   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3920 
3921   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3922   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3923   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
3924   to interpolate only that one (e0), so that
3925 $  cone(c0) = {e0, v2}
3926 $  cone(e0) = {v0, v1}
3927   If DMPlexStratify() is run on this mesh, it will give depths
3928 $  depth 0 = {v0, v1, v2}
3929 $  depth 1 = {e0, c0}
3930   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3931 
3932   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3933 
3934   Level: beginner
3935 
3936 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3937 @*/
3938 PetscErrorCode DMPlexStratify(DM dm)
3939 {
3940   DM_Plex       *mesh = (DM_Plex*) dm->data;
3941   DMLabel        label;
3942   PetscInt       pStart, pEnd, p;
3943   PetscInt       numRoots = 0, numLeaves = 0;
3944 
3945   PetscFunctionBegin;
3946   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3947   PetscCall(PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0));
3948 
3949   /* Create depth label */
3950   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3951   PetscCall(DMCreateLabel(dm, "depth"));
3952   PetscCall(DMPlexGetDepthLabel(dm, &label));
3953 
3954   {
3955     /* Initialize roots and count leaves */
3956     PetscInt sMin = PETSC_MAX_INT;
3957     PetscInt sMax = PETSC_MIN_INT;
3958     PetscInt coneSize, supportSize;
3959 
3960     for (p = pStart; p < pEnd; ++p) {
3961       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3962       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3963       if (!coneSize && supportSize) {
3964         sMin = PetscMin(p, sMin);
3965         sMax = PetscMax(p, sMax);
3966         ++numRoots;
3967       } else if (!supportSize && coneSize) {
3968         ++numLeaves;
3969       } else if (!supportSize && !coneSize) {
3970         /* Isolated points */
3971         sMin = PetscMin(p, sMin);
3972         sMax = PetscMax(p, sMax);
3973       }
3974     }
3975     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1));
3976   }
3977 
3978   if (numRoots + numLeaves == (pEnd - pStart)) {
3979     PetscInt sMin = PETSC_MAX_INT;
3980     PetscInt sMax = PETSC_MIN_INT;
3981     PetscInt coneSize, supportSize;
3982 
3983     for (p = pStart; p < pEnd; ++p) {
3984       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3985       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3986       if (!supportSize && coneSize) {
3987         sMin = PetscMin(p, sMin);
3988         sMax = PetscMax(p, sMax);
3989       }
3990     }
3991     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1));
3992   } else {
3993     PetscInt level = 0;
3994     PetscInt qStart, qEnd, q;
3995 
3996     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3997     while (qEnd > qStart) {
3998       PetscInt sMin = PETSC_MAX_INT;
3999       PetscInt sMax = PETSC_MIN_INT;
4000 
4001       for (q = qStart; q < qEnd; ++q) {
4002         const PetscInt *support;
4003         PetscInt        supportSize, s;
4004 
4005         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4006         PetscCall(DMPlexGetSupport(dm, q, &support));
4007         for (s = 0; s < supportSize; ++s) {
4008           sMin = PetscMin(support[s], sMin);
4009           sMax = PetscMax(support[s], sMax);
4010         }
4011       }
4012       PetscCall(DMLabelGetNumValues(label, &level));
4013       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1));
4014       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4015     }
4016   }
4017   { /* just in case there is an empty process */
4018     PetscInt numValues, maxValues = 0, v;
4019 
4020     PetscCall(DMLabelGetNumValues(label, &numValues));
4021     PetscCallMPI(MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm)));
4022     for (v = numValues; v < maxValues; v++) {
4023       PetscCall(DMLabelAddStratum(label, v));
4024     }
4025   }
4026   PetscCall(PetscObjectStateGet((PetscObject) label, &mesh->depthState));
4027   PetscCall(PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0));
4028   PetscFunctionReturn(0);
4029 }
4030 
4031 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4032 {
4033   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4034   PetscInt       dim, depth, pheight, coneSize;
4035 
4036   PetscFunctionBeginHot;
4037   PetscCall(DMGetDimension(dm, &dim));
4038   PetscCall(DMPlexGetDepth(dm, &depth));
4039   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4040   pheight = depth - pdepth;
4041   if (depth <= 1) {
4042     switch (pdepth) {
4043       case 0: ct = DM_POLYTOPE_POINT;break;
4044       case 1:
4045         switch (coneSize) {
4046           case 2: ct = DM_POLYTOPE_SEGMENT;break;
4047           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4048           case 4:
4049           switch (dim) {
4050             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
4051             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
4052             default: break;
4053           }
4054           break;
4055         case 5: ct = DM_POLYTOPE_PYRAMID;break;
4056         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4057         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
4058         default: break;
4059       }
4060     }
4061   } else {
4062     if (pdepth == 0) {
4063       ct = DM_POLYTOPE_POINT;
4064     } else if (pheight == 0) {
4065       switch (dim) {
4066         case 1:
4067           switch (coneSize) {
4068             case 2: ct = DM_POLYTOPE_SEGMENT;break;
4069             default: break;
4070           }
4071           break;
4072         case 2:
4073           switch (coneSize) {
4074             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4075             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4076             default: break;
4077           }
4078           break;
4079         case 3:
4080           switch (coneSize) {
4081             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
4082             case 5:
4083             {
4084               const PetscInt *cone;
4085               PetscInt        faceConeSize;
4086 
4087               PetscCall(DMPlexGetCone(dm, p, &cone));
4088               PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4089               switch (faceConeSize) {
4090                 case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4091                 case 4: ct = DM_POLYTOPE_PYRAMID;break;
4092               }
4093             }
4094             break;
4095             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
4096             default: break;
4097           }
4098           break;
4099         default: break;
4100       }
4101     } else if (pheight > 0) {
4102       switch (coneSize) {
4103         case 2: ct = DM_POLYTOPE_SEGMENT;break;
4104         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4105         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4106         default: break;
4107       }
4108     }
4109   }
4110   *pt = ct;
4111   PetscFunctionReturn(0);
4112 }
4113 
4114 /*@
4115   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4116 
4117   Collective on dm
4118 
4119   Input Parameter:
4120 . mesh - The DMPlex
4121 
4122   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4123 
4124   Level: developer
4125 
4126   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4127   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4128   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4129 
4130 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4131 @*/
4132 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4133 {
4134   DM_Plex       *mesh;
4135   DMLabel        ctLabel;
4136   PetscInt       pStart, pEnd, p;
4137 
4138   PetscFunctionBegin;
4139   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4140   mesh = (DM_Plex *) dm->data;
4141   PetscCall(DMCreateLabel(dm, "celltype"));
4142   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4143   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4144   for (p = pStart; p < pEnd; ++p) {
4145     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4146     PetscInt       pdepth;
4147 
4148     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4149     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4150     PetscCheck(ct != DM_POLYTOPE_UNKNOWN,PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4151     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4152   }
4153   PetscCall(PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState));
4154   PetscCall(PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view"));
4155   PetscFunctionReturn(0);
4156 }
4157 
4158 /*@C
4159   DMPlexGetJoin - Get an array for the join of the set of points
4160 
4161   Not Collective
4162 
4163   Input Parameters:
4164 + dm - The DMPlex object
4165 . numPoints - The number of input points for the join
4166 - points - The input points
4167 
4168   Output Parameters:
4169 + numCoveredPoints - The number of points in the join
4170 - coveredPoints - The points in the join
4171 
4172   Level: intermediate
4173 
4174   Note: Currently, this is restricted to a single level join
4175 
4176   Fortran Notes:
4177   Since it returns an array, this routine is only available in Fortran 90, and you must
4178   include petsc.h90 in your code.
4179 
4180   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4181 
4182 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4183 @*/
4184 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4185 {
4186   DM_Plex       *mesh = (DM_Plex*) dm->data;
4187   PetscInt      *join[2];
4188   PetscInt       joinSize, i = 0;
4189   PetscInt       dof, off, p, c, m;
4190   PetscInt       maxSupportSize;
4191 
4192   PetscFunctionBegin;
4193   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4194   PetscValidIntPointer(points, 3);
4195   PetscValidIntPointer(numCoveredPoints, 4);
4196   PetscValidPointer(coveredPoints, 5);
4197   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4198   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4199   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4200   /* Copy in support of first point */
4201   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4202   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4203   for (joinSize = 0; joinSize < dof; ++joinSize) {
4204     join[i][joinSize] = mesh->supports[off+joinSize];
4205   }
4206   /* Check each successive support */
4207   for (p = 1; p < numPoints; ++p) {
4208     PetscInt newJoinSize = 0;
4209 
4210     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4211     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4212     for (c = 0; c < dof; ++c) {
4213       const PetscInt point = mesh->supports[off+c];
4214 
4215       for (m = 0; m < joinSize; ++m) {
4216         if (point == join[i][m]) {
4217           join[1-i][newJoinSize++] = point;
4218           break;
4219         }
4220       }
4221     }
4222     joinSize = newJoinSize;
4223     i        = 1-i;
4224   }
4225   *numCoveredPoints = joinSize;
4226   *coveredPoints    = join[i];
4227   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1-i]));
4228   PetscFunctionReturn(0);
4229 }
4230 
4231 /*@C
4232   DMPlexRestoreJoin - Restore an array for the join of the set of points
4233 
4234   Not Collective
4235 
4236   Input Parameters:
4237 + dm - The DMPlex object
4238 . numPoints - The number of input points for the join
4239 - points - The input points
4240 
4241   Output Parameters:
4242 + numCoveredPoints - The number of points in the join
4243 - coveredPoints - The points in the join
4244 
4245   Fortran Notes:
4246   Since it returns an array, this routine is only available in Fortran 90, and you must
4247   include petsc.h90 in your code.
4248 
4249   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4250 
4251   Level: intermediate
4252 
4253 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4254 @*/
4255 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4256 {
4257   PetscFunctionBegin;
4258   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4259   if (points) PetscValidIntPointer(points,3);
4260   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4261   PetscValidPointer(coveredPoints, 5);
4262   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4263   if (numCoveredPoints) *numCoveredPoints = 0;
4264   PetscFunctionReturn(0);
4265 }
4266 
4267 /*@C
4268   DMPlexGetFullJoin - Get an array for the join of the set of points
4269 
4270   Not Collective
4271 
4272   Input Parameters:
4273 + dm - The DMPlex object
4274 . numPoints - The number of input points for the join
4275 - points - The input points
4276 
4277   Output Parameters:
4278 + numCoveredPoints - The number of points in the join
4279 - coveredPoints - The points in the join
4280 
4281   Fortran Notes:
4282   Since it returns an array, this routine is only available in Fortran 90, and you must
4283   include petsc.h90 in your code.
4284 
4285   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4286 
4287   Level: intermediate
4288 
4289 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4290 @*/
4291 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4292 {
4293   PetscInt      *offsets, **closures;
4294   PetscInt      *join[2];
4295   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
4296   PetscInt       p, d, c, m, ms;
4297 
4298   PetscFunctionBegin;
4299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4300   PetscValidIntPointer(points, 3);
4301   PetscValidIntPointer(numCoveredPoints, 4);
4302   PetscValidPointer(coveredPoints, 5);
4303 
4304   PetscCall(DMPlexGetDepth(dm, &depth));
4305   PetscCall(PetscCalloc1(numPoints, &closures));
4306   PetscCall(DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4307   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4308   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
4309   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4310   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4311 
4312   for (p = 0; p < numPoints; ++p) {
4313     PetscInt closureSize;
4314 
4315     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4316 
4317     offsets[p*(depth+2)+0] = 0;
4318     for (d = 0; d < depth+1; ++d) {
4319       PetscInt pStart, pEnd, i;
4320 
4321       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4322       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
4323         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4324           offsets[p*(depth+2)+d+1] = i;
4325           break;
4326         }
4327       }
4328       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
4329     }
4330     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);
4331   }
4332   for (d = 0; d < depth+1; ++d) {
4333     PetscInt dof;
4334 
4335     /* Copy in support of first point */
4336     dof = offsets[d+1] - offsets[d];
4337     for (joinSize = 0; joinSize < dof; ++joinSize) {
4338       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
4339     }
4340     /* Check each successive cone */
4341     for (p = 1; p < numPoints && joinSize; ++p) {
4342       PetscInt newJoinSize = 0;
4343 
4344       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
4345       for (c = 0; c < dof; ++c) {
4346         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
4347 
4348         for (m = 0; m < joinSize; ++m) {
4349           if (point == join[i][m]) {
4350             join[1-i][newJoinSize++] = point;
4351             break;
4352           }
4353         }
4354       }
4355       joinSize = newJoinSize;
4356       i        = 1-i;
4357     }
4358     if (joinSize) break;
4359   }
4360   *numCoveredPoints = joinSize;
4361   *coveredPoints    = join[i];
4362   for (p = 0; p < numPoints; ++p) {
4363     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4364   }
4365   PetscCall(PetscFree(closures));
4366   PetscCall(DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4367   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1-i]));
4368   PetscFunctionReturn(0);
4369 }
4370 
4371 /*@C
4372   DMPlexGetMeet - Get an array for the meet of the set of points
4373 
4374   Not Collective
4375 
4376   Input Parameters:
4377 + dm - The DMPlex object
4378 . numPoints - The number of input points for the meet
4379 - points - The input points
4380 
4381   Output Parameters:
4382 + numCoveredPoints - The number of points in the meet
4383 - coveredPoints - The points in the meet
4384 
4385   Level: intermediate
4386 
4387   Note: Currently, this is restricted to a single level meet
4388 
4389   Fortran Notes:
4390   Since it returns an array, this routine is only available in Fortran 90, and you must
4391   include petsc.h90 in your code.
4392 
4393   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4394 
4395 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4396 @*/
4397 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4398 {
4399   DM_Plex       *mesh = (DM_Plex*) dm->data;
4400   PetscInt      *meet[2];
4401   PetscInt       meetSize, i = 0;
4402   PetscInt       dof, off, p, c, m;
4403   PetscInt       maxConeSize;
4404 
4405   PetscFunctionBegin;
4406   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4407   PetscValidIntPointer(points, 3);
4408   PetscValidIntPointer(numCoveringPoints, 4);
4409   PetscValidPointer(coveringPoints, 5);
4410   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4411   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4412   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4413   /* Copy in cone of first point */
4414   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4415   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4416   for (meetSize = 0; meetSize < dof; ++meetSize) {
4417     meet[i][meetSize] = mesh->cones[off+meetSize];
4418   }
4419   /* Check each successive cone */
4420   for (p = 1; p < numPoints; ++p) {
4421     PetscInt newMeetSize = 0;
4422 
4423     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4424     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4425     for (c = 0; c < dof; ++c) {
4426       const PetscInt point = mesh->cones[off+c];
4427 
4428       for (m = 0; m < meetSize; ++m) {
4429         if (point == meet[i][m]) {
4430           meet[1-i][newMeetSize++] = point;
4431           break;
4432         }
4433       }
4434     }
4435     meetSize = newMeetSize;
4436     i        = 1-i;
4437   }
4438   *numCoveringPoints = meetSize;
4439   *coveringPoints    = meet[i];
4440   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1-i]));
4441   PetscFunctionReturn(0);
4442 }
4443 
4444 /*@C
4445   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4446 
4447   Not Collective
4448 
4449   Input Parameters:
4450 + dm - The DMPlex object
4451 . numPoints - The number of input points for the meet
4452 - points - The input points
4453 
4454   Output Parameters:
4455 + numCoveredPoints - The number of points in the meet
4456 - coveredPoints - The points in the meet
4457 
4458   Level: intermediate
4459 
4460   Fortran Notes:
4461   Since it returns an array, this routine is only available in Fortran 90, and you must
4462   include petsc.h90 in your code.
4463 
4464   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4465 
4466 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4467 @*/
4468 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4469 {
4470   PetscFunctionBegin;
4471   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4472   if (points) PetscValidIntPointer(points,3);
4473   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4474   PetscValidPointer(coveredPoints,5);
4475   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4476   if (numCoveredPoints) *numCoveredPoints = 0;
4477   PetscFunctionReturn(0);
4478 }
4479 
4480 /*@C
4481   DMPlexGetFullMeet - Get an array for the meet of the set of points
4482 
4483   Not Collective
4484 
4485   Input Parameters:
4486 + dm - The DMPlex object
4487 . numPoints - The number of input points for the meet
4488 - points - The input points
4489 
4490   Output Parameters:
4491 + numCoveredPoints - The number of points in the meet
4492 - coveredPoints - The points in the meet
4493 
4494   Level: intermediate
4495 
4496   Fortran Notes:
4497   Since it returns an array, this routine is only available in Fortran 90, and you must
4498   include petsc.h90 in your code.
4499 
4500   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4501 
4502 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4503 @*/
4504 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4505 {
4506   PetscInt      *offsets, **closures;
4507   PetscInt      *meet[2];
4508   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
4509   PetscInt       p, h, c, m, mc;
4510 
4511   PetscFunctionBegin;
4512   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4513   PetscValidIntPointer(points, 3);
4514   PetscValidIntPointer(numCoveredPoints, 4);
4515   PetscValidPointer(coveredPoints, 5);
4516 
4517   PetscCall(DMPlexGetDepth(dm, &height));
4518   PetscCall(PetscMalloc1(numPoints, &closures));
4519   PetscCall(DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4520   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4521   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
4522   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4523   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4524 
4525   for (p = 0; p < numPoints; ++p) {
4526     PetscInt closureSize;
4527 
4528     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4529 
4530     offsets[p*(height+2)+0] = 0;
4531     for (h = 0; h < height+1; ++h) {
4532       PetscInt pStart, pEnd, i;
4533 
4534       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4535       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
4536         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4537           offsets[p*(height+2)+h+1] = i;
4538           break;
4539         }
4540       }
4541       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
4542     }
4543     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);
4544   }
4545   for (h = 0; h < height+1; ++h) {
4546     PetscInt dof;
4547 
4548     /* Copy in cone of first point */
4549     dof = offsets[h+1] - offsets[h];
4550     for (meetSize = 0; meetSize < dof; ++meetSize) {
4551       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
4552     }
4553     /* Check each successive cone */
4554     for (p = 1; p < numPoints && meetSize; ++p) {
4555       PetscInt newMeetSize = 0;
4556 
4557       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
4558       for (c = 0; c < dof; ++c) {
4559         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
4560 
4561         for (m = 0; m < meetSize; ++m) {
4562           if (point == meet[i][m]) {
4563             meet[1-i][newMeetSize++] = point;
4564             break;
4565           }
4566         }
4567       }
4568       meetSize = newMeetSize;
4569       i        = 1-i;
4570     }
4571     if (meetSize) break;
4572   }
4573   *numCoveredPoints = meetSize;
4574   *coveredPoints    = meet[i];
4575   for (p = 0; p < numPoints; ++p) {
4576     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4577   }
4578   PetscCall(PetscFree(closures));
4579   PetscCall(DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4580   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1-i]));
4581   PetscFunctionReturn(0);
4582 }
4583 
4584 /*@C
4585   DMPlexEqual - Determine if two DMs have the same topology
4586 
4587   Not Collective
4588 
4589   Input Parameters:
4590 + dmA - A DMPlex object
4591 - dmB - A DMPlex object
4592 
4593   Output Parameters:
4594 . equal - PETSC_TRUE if the topologies are identical
4595 
4596   Level: intermediate
4597 
4598   Notes:
4599   We are not solving graph isomorphism, so we do not permutation.
4600 
4601 .seealso: `DMPlexGetCone()`
4602 @*/
4603 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4604 {
4605   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4606 
4607   PetscFunctionBegin;
4608   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4609   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4610   PetscValidBoolPointer(equal, 3);
4611 
4612   *equal = PETSC_FALSE;
4613   PetscCall(DMPlexGetDepth(dmA, &depth));
4614   PetscCall(DMPlexGetDepth(dmB, &depthB));
4615   if (depth != depthB) PetscFunctionReturn(0);
4616   PetscCall(DMPlexGetChart(dmA, &pStart,  &pEnd));
4617   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4618   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4619   for (p = pStart; p < pEnd; ++p) {
4620     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4621     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4622 
4623     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4624     PetscCall(DMPlexGetCone(dmA, p, &cone));
4625     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4626     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4627     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4628     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4629     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4630     for (c = 0; c < coneSize; ++c) {
4631       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4632       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4633     }
4634     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4635     PetscCall(DMPlexGetSupport(dmA, p, &support));
4636     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4637     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4638     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4639     for (s = 0; s < supportSize; ++s) {
4640       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4641     }
4642   }
4643   *equal = PETSC_TRUE;
4644   PetscFunctionReturn(0);
4645 }
4646 
4647 /*@C
4648   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4649 
4650   Not Collective
4651 
4652   Input Parameters:
4653 + dm         - The DMPlex
4654 . cellDim    - The cell dimension
4655 - numCorners - The number of vertices on a cell
4656 
4657   Output Parameters:
4658 . numFaceVertices - The number of vertices on a face
4659 
4660   Level: developer
4661 
4662   Notes:
4663   Of course this can only work for a restricted set of symmetric shapes
4664 
4665 .seealso: `DMPlexGetCone()`
4666 @*/
4667 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4668 {
4669   MPI_Comm       comm;
4670 
4671   PetscFunctionBegin;
4672   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
4673   PetscValidIntPointer(numFaceVertices,4);
4674   switch (cellDim) {
4675   case 0:
4676     *numFaceVertices = 0;
4677     break;
4678   case 1:
4679     *numFaceVertices = 1;
4680     break;
4681   case 2:
4682     switch (numCorners) {
4683     case 3: /* triangle */
4684       *numFaceVertices = 2; /* Edge has 2 vertices */
4685       break;
4686     case 4: /* quadrilateral */
4687       *numFaceVertices = 2; /* Edge has 2 vertices */
4688       break;
4689     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
4690       *numFaceVertices = 3; /* Edge has 3 vertices */
4691       break;
4692     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4693       *numFaceVertices = 3; /* Edge has 3 vertices */
4694       break;
4695     default:
4696       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4697     }
4698     break;
4699   case 3:
4700     switch (numCorners) {
4701     case 4: /* tetradehdron */
4702       *numFaceVertices = 3; /* Face has 3 vertices */
4703       break;
4704     case 6: /* tet cohesive cells */
4705       *numFaceVertices = 4; /* Face has 4 vertices */
4706       break;
4707     case 8: /* hexahedron */
4708       *numFaceVertices = 4; /* Face has 4 vertices */
4709       break;
4710     case 9: /* tet cohesive Lagrange cells */
4711       *numFaceVertices = 6; /* Face has 6 vertices */
4712       break;
4713     case 10: /* quadratic tetrahedron */
4714       *numFaceVertices = 6; /* Face has 6 vertices */
4715       break;
4716     case 12: /* hex cohesive Lagrange cells */
4717       *numFaceVertices = 6; /* Face has 6 vertices */
4718       break;
4719     case 18: /* quadratic tet cohesive Lagrange cells */
4720       *numFaceVertices = 6; /* Face has 6 vertices */
4721       break;
4722     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4723       *numFaceVertices = 9; /* Face has 9 vertices */
4724       break;
4725     default:
4726       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4727     }
4728     break;
4729   default:
4730     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4731   }
4732   PetscFunctionReturn(0);
4733 }
4734 
4735 /*@
4736   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4737 
4738   Not Collective
4739 
4740   Input Parameter:
4741 . dm    - The DMPlex object
4742 
4743   Output Parameter:
4744 . depthLabel - The DMLabel recording point depth
4745 
4746   Level: developer
4747 
4748 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4749 @*/
4750 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4751 {
4752   PetscFunctionBegin;
4753   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4754   PetscValidPointer(depthLabel, 2);
4755   *depthLabel = dm->depthLabel;
4756   PetscFunctionReturn(0);
4757 }
4758 
4759 /*@
4760   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4761 
4762   Not Collective
4763 
4764   Input Parameter:
4765 . dm    - The DMPlex object
4766 
4767   Output Parameter:
4768 . depth - The number of strata (breadth first levels) in the DAG
4769 
4770   Level: developer
4771 
4772   Notes:
4773   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4774   The point depth is described more in detail in DMPlexGetDepthStratum().
4775   An empty mesh gives -1.
4776 
4777 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4778 @*/
4779 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4780 {
4781   DMLabel        label;
4782   PetscInt       d = 0;
4783 
4784   PetscFunctionBegin;
4785   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4786   PetscValidIntPointer(depth, 2);
4787   PetscCall(DMPlexGetDepthLabel(dm, &label));
4788   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4789   *depth = d-1;
4790   PetscFunctionReturn(0);
4791 }
4792 
4793 /*@
4794   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4795 
4796   Not Collective
4797 
4798   Input Parameters:
4799 + dm    - The DMPlex object
4800 - depth - The requested depth
4801 
4802   Output Parameters:
4803 + start - The first point at this depth
4804 - end   - One beyond the last point at this depth
4805 
4806   Notes:
4807   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4808   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4809   higher dimension, e.g., "edges".
4810 
4811   Level: developer
4812 
4813 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4814 @*/
4815 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4816 {
4817   DMLabel        label;
4818   PetscInt       pStart, pEnd;
4819 
4820   PetscFunctionBegin;
4821   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4822   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4823   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4824   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4825   if (pStart == pEnd) PetscFunctionReturn(0);
4826   if (depth < 0) {
4827     if (start) *start = pStart;
4828     if (end)   *end   = pEnd;
4829     PetscFunctionReturn(0);
4830   }
4831   PetscCall(DMPlexGetDepthLabel(dm, &label));
4832   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4833   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4834   PetscFunctionReturn(0);
4835 }
4836 
4837 /*@
4838   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4839 
4840   Not Collective
4841 
4842   Input Parameters:
4843 + dm     - The DMPlex object
4844 - height - The requested height
4845 
4846   Output Parameters:
4847 + start - The first point at this height
4848 - end   - One beyond the last point at this height
4849 
4850   Notes:
4851   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4852   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4853   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4854 
4855   Level: developer
4856 
4857 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4858 @*/
4859 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4860 {
4861   DMLabel        label;
4862   PetscInt       depth, pStart, pEnd;
4863 
4864   PetscFunctionBegin;
4865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4866   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4867   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4868   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4869   if (pStart == pEnd) PetscFunctionReturn(0);
4870   if (height < 0) {
4871     if (start) *start = pStart;
4872     if (end)   *end   = pEnd;
4873     PetscFunctionReturn(0);
4874   }
4875   PetscCall(DMPlexGetDepthLabel(dm, &label));
4876   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4877   PetscCall(DMLabelGetNumValues(label, &depth));
4878   PetscCall(DMLabelGetStratumBounds(label, depth-1-height, start, end));
4879   PetscFunctionReturn(0);
4880 }
4881 
4882 /*@
4883   DMPlexGetPointDepth - Get the depth of a given point
4884 
4885   Not Collective
4886 
4887   Input Parameters:
4888 + dm    - The DMPlex object
4889 - point - The point
4890 
4891   Output Parameter:
4892 . depth - The depth of the point
4893 
4894   Level: intermediate
4895 
4896 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4897 @*/
4898 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4899 {
4900   PetscFunctionBegin;
4901   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4902   PetscValidIntPointer(depth, 3);
4903   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4904   PetscFunctionReturn(0);
4905 }
4906 
4907 /*@
4908   DMPlexGetPointHeight - Get the height of a given point
4909 
4910   Not Collective
4911 
4912   Input Parameters:
4913 + dm    - The DMPlex object
4914 - point - The point
4915 
4916   Output Parameter:
4917 . height - The height of the point
4918 
4919   Level: intermediate
4920 
4921 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4922 @*/
4923 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4924 {
4925   PetscInt       n, pDepth;
4926 
4927   PetscFunctionBegin;
4928   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4929   PetscValidIntPointer(height, 3);
4930   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4931   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4932   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
4933   PetscFunctionReturn(0);
4934 }
4935 
4936 /*@
4937   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4938 
4939   Not Collective
4940 
4941   Input Parameter:
4942 . dm - The DMPlex object
4943 
4944   Output Parameter:
4945 . celltypeLabel - The DMLabel recording cell polytope type
4946 
4947   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4948   DMCreateLabel(dm, "celltype") beforehand.
4949 
4950   Level: developer
4951 
4952 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4953 @*/
4954 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
4955 {
4956   PetscFunctionBegin;
4957   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4958   PetscValidPointer(celltypeLabel, 2);
4959   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4960   *celltypeLabel = dm->celltypeLabel;
4961   PetscFunctionReturn(0);
4962 }
4963 
4964 /*@
4965   DMPlexGetCellType - Get the polytope type of a given cell
4966 
4967   Not Collective
4968 
4969   Input Parameters:
4970 + dm   - The DMPlex object
4971 - cell - The cell
4972 
4973   Output Parameter:
4974 . celltype - The polytope type of the cell
4975 
4976   Level: intermediate
4977 
4978 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4979 @*/
4980 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
4981 {
4982   DMLabel        label;
4983   PetscInt       ct;
4984 
4985   PetscFunctionBegin;
4986   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4987   PetscValidPointer(celltype, 3);
4988   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4989   PetscCall(DMLabelGetValue(label, cell, &ct));
4990   PetscCheck(ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4991   *celltype = (DMPolytopeType) ct;
4992   PetscFunctionReturn(0);
4993 }
4994 
4995 /*@
4996   DMPlexSetCellType - Set the polytope type of a given cell
4997 
4998   Not Collective
4999 
5000   Input Parameters:
5001 + dm   - The DMPlex object
5002 . cell - The cell
5003 - celltype - The polytope type of the cell
5004 
5005   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
5006   is executed. This function will override the computed type. However, if automatic classification will not succeed
5007   and a user wants to manually specify all types, the classification must be disabled by calling
5008   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5009 
5010   Level: advanced
5011 
5012 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5013 @*/
5014 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5015 {
5016   DMLabel        label;
5017 
5018   PetscFunctionBegin;
5019   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5020   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5021   PetscCall(DMLabelSetValue(label, cell, celltype));
5022   PetscFunctionReturn(0);
5023 }
5024 
5025 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5026 {
5027   PetscSection   section, s;
5028   Mat            m;
5029   PetscInt       maxHeight;
5030 
5031   PetscFunctionBegin;
5032   PetscCall(DMClone(dm, cdm));
5033   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5034   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5035   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5036   PetscCall(DMSetLocalSection(*cdm, section));
5037   PetscCall(PetscSectionDestroy(&section));
5038   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5039   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5040   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5041   PetscCall(PetscSectionDestroy(&s));
5042   PetscCall(MatDestroy(&m));
5043 
5044   PetscCall(DMSetNumFields(*cdm, 1));
5045   PetscCall(DMCreateDS(*cdm));
5046   PetscFunctionReturn(0);
5047 }
5048 
5049 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5050 {
5051   Vec coordsLocal, cellCoordsLocal;
5052   DM  coordsDM,    cellCoordsDM;
5053 
5054   PetscFunctionBegin;
5055   *field = NULL;
5056   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5057   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5058   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5059   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5060   if (coordsLocal && coordsDM) {
5061     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5062     else                                 PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5063   }
5064   PetscFunctionReturn(0);
5065 }
5066 
5067 /*@C
5068   DMPlexGetConeSection - Return a section which describes the layout of cone data
5069 
5070   Not Collective
5071 
5072   Input Parameters:
5073 . dm        - The DMPlex object
5074 
5075   Output Parameter:
5076 . section - The PetscSection object
5077 
5078   Level: developer
5079 
5080 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5081 @*/
5082 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5083 {
5084   DM_Plex *mesh = (DM_Plex*) dm->data;
5085 
5086   PetscFunctionBegin;
5087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5088   if (section) *section = mesh->coneSection;
5089   PetscFunctionReturn(0);
5090 }
5091 
5092 /*@C
5093   DMPlexGetSupportSection - Return a section which describes the layout of support data
5094 
5095   Not Collective
5096 
5097   Input Parameters:
5098 . dm        - The DMPlex object
5099 
5100   Output Parameter:
5101 . section - The PetscSection object
5102 
5103   Level: developer
5104 
5105 .seealso: `DMPlexGetConeSection()`
5106 @*/
5107 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5108 {
5109   DM_Plex *mesh = (DM_Plex*) dm->data;
5110 
5111   PetscFunctionBegin;
5112   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5113   if (section) *section = mesh->supportSection;
5114   PetscFunctionReturn(0);
5115 }
5116 
5117 /*@C
5118   DMPlexGetCones - Return cone data
5119 
5120   Not Collective
5121 
5122   Input Parameters:
5123 . dm        - The DMPlex object
5124 
5125   Output Parameter:
5126 . cones - The cone for each point
5127 
5128   Level: developer
5129 
5130 .seealso: `DMPlexGetConeSection()`
5131 @*/
5132 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5133 {
5134   DM_Plex *mesh = (DM_Plex*) dm->data;
5135 
5136   PetscFunctionBegin;
5137   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5138   if (cones) *cones = mesh->cones;
5139   PetscFunctionReturn(0);
5140 }
5141 
5142 /*@C
5143   DMPlexGetConeOrientations - Return cone orientation data
5144 
5145   Not Collective
5146 
5147   Input Parameters:
5148 . dm        - The DMPlex object
5149 
5150   Output Parameter:
5151 . coneOrientations - The array of cone orientations for all points
5152 
5153   Level: developer
5154 
5155   Notes:
5156   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5157 
5158   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5159 
5160 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5161 @*/
5162 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5163 {
5164   DM_Plex *mesh = (DM_Plex*) dm->data;
5165 
5166   PetscFunctionBegin;
5167   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5168   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5169   PetscFunctionReturn(0);
5170 }
5171 
5172 /******************************** FEM Support **********************************/
5173 
5174 /*
5175  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5176  representing a line in the section.
5177 */
5178 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
5179 {
5180   PetscFunctionBeginHot;
5181   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5182   if (line < 0) {
5183     *k = 0;
5184     *Nc = 0;
5185   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
5186     *k = 1;
5187   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
5188     /* An order k SEM disc has k-1 dofs on an edge */
5189     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5190     *k = *k / *Nc + 1;
5191   }
5192   PetscFunctionReturn(0);
5193 }
5194 
5195 /*@
5196 
5197   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5198   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5199   section provided (or the section of the DM).
5200 
5201   Input Parameters:
5202 + dm      - The DM
5203 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5204 - section - The PetscSection to reorder, or NULL for the default section
5205 
5206   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5207   degree of the basis.
5208 
5209   Example:
5210   A typical interpolated single-quad mesh might order points as
5211 .vb
5212   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5213 
5214   v4 -- e6 -- v3
5215   |           |
5216   e7    c0    e8
5217   |           |
5218   v1 -- e5 -- v2
5219 .ve
5220 
5221   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5222   dofs in the order of points, e.g.,
5223 .vb
5224     c0 -> [0,1,2,3]
5225     v1 -> [4]
5226     ...
5227     e5 -> [8, 9]
5228 .ve
5229 
5230   which corresponds to the dofs
5231 .vb
5232     6   10  11  7
5233     13  2   3   15
5234     12  0   1   14
5235     4   8   9   5
5236 .ve
5237 
5238   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5239 .vb
5240   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5241 .ve
5242 
5243   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5244 .vb
5245    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5246 .ve
5247 
5248   Level: developer
5249 
5250 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5251 @*/
5252 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5253 {
5254   DMLabel        label;
5255   PetscInt       dim, depth = -1, eStart = -1, Nf;
5256   PetscBool      vertexchart;
5257 
5258   PetscFunctionBegin;
5259   PetscCall(DMGetDimension(dm, &dim));
5260   if (dim < 1) PetscFunctionReturn(0);
5261   if (point < 0) {
5262     PetscInt sStart,sEnd;
5263 
5264     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5265     point = sEnd-sStart ? sStart : point;
5266   }
5267   PetscCall(DMPlexGetDepthLabel(dm, &label));
5268   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5269   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5270   if (depth == 1) {eStart = point;}
5271   else if  (depth == dim) {
5272     const PetscInt *cone;
5273 
5274     PetscCall(DMPlexGetCone(dm, point, &cone));
5275     if (dim == 2) eStart = cone[0];
5276     else if (dim == 3) {
5277       const PetscInt *cone2;
5278       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5279       eStart = cone2[0];
5280     } 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);
5281   } 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);
5282   {                             /* Determine whether the chart covers all points or just vertices. */
5283     PetscInt pStart,pEnd,cStart,cEnd;
5284     PetscCall(DMPlexGetDepthStratum(dm,0,&pStart,&pEnd));
5285     PetscCall(PetscSectionGetChart(section,&cStart,&cEnd));
5286     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5287     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5288     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5289   }
5290   PetscCall(PetscSectionGetNumFields(section, &Nf));
5291   for (PetscInt d=1; d<=dim; d++) {
5292     PetscInt k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5293     PetscInt *perm;
5294 
5295     for (f = 0; f < Nf; ++f) {
5296       PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5297       size += PetscPowInt(k+1, d)*Nc;
5298     }
5299     PetscCall(PetscMalloc1(size, &perm));
5300     for (f = 0; f < Nf; ++f) {
5301       switch (d) {
5302       case 1:
5303         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5304         /*
5305          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5306          We want              [ vtx0; edge of length k-1; vtx1 ]
5307          */
5308         for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
5309         for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
5310         for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
5311         foffset = offset;
5312         break;
5313       case 2:
5314         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5315         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5316         /* The SEM order is
5317 
5318          v_lb, {e_b}, v_rb,
5319          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5320          v_lt, reverse {e_t}, v_rt
5321          */
5322         {
5323           const PetscInt of   = 0;
5324           const PetscInt oeb  = of   + PetscSqr(k-1);
5325           const PetscInt oer  = oeb  + (k-1);
5326           const PetscInt oet  = oer  + (k-1);
5327           const PetscInt oel  = oet  + (k-1);
5328           const PetscInt ovlb = oel  + (k-1);
5329           const PetscInt ovrb = ovlb + 1;
5330           const PetscInt ovrt = ovrb + 1;
5331           const PetscInt ovlt = ovrt + 1;
5332           PetscInt       o;
5333 
5334           /* bottom */
5335           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
5336           for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5337           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
5338           /* middle */
5339           for (i = 0; i < k-1; ++i) {
5340             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
5341             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;
5342             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
5343           }
5344           /* top */
5345           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
5346           for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5347           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
5348           foffset = offset;
5349         }
5350         break;
5351       case 3:
5352         /* The original hex closure is
5353 
5354          {c,
5355          f_b, f_t, f_f, f_b, f_r, f_l,
5356          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5357          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5358          */
5359         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5360         /* The SEM order is
5361          Bottom Slice
5362          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5363          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5364          v_blb, {e_bb}, v_brb,
5365 
5366          Middle Slice (j)
5367          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5368          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5369          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5370 
5371          Top Slice
5372          v_tlf, {e_tf}, v_trf,
5373          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5374          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5375          */
5376         {
5377           const PetscInt oc    = 0;
5378           const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
5379           const PetscInt oft   = ofb   + PetscSqr(k-1);
5380           const PetscInt off   = oft   + PetscSqr(k-1);
5381           const PetscInt ofk   = off   + PetscSqr(k-1);
5382           const PetscInt ofr   = ofk   + PetscSqr(k-1);
5383           const PetscInt ofl   = ofr   + PetscSqr(k-1);
5384           const PetscInt oebl  = ofl   + PetscSqr(k-1);
5385           const PetscInt oebb  = oebl  + (k-1);
5386           const PetscInt oebr  = oebb  + (k-1);
5387           const PetscInt oebf  = oebr  + (k-1);
5388           const PetscInt oetf  = oebf  + (k-1);
5389           const PetscInt oetr  = oetf  + (k-1);
5390           const PetscInt oetb  = oetr  + (k-1);
5391           const PetscInt oetl  = oetb  + (k-1);
5392           const PetscInt oerf  = oetl  + (k-1);
5393           const PetscInt oelf  = oerf  + (k-1);
5394           const PetscInt oelb  = oelf  + (k-1);
5395           const PetscInt oerb  = oelb  + (k-1);
5396           const PetscInt ovblf = oerb  + (k-1);
5397           const PetscInt ovblb = ovblf + 1;
5398           const PetscInt ovbrb = ovblb + 1;
5399           const PetscInt ovbrf = ovbrb + 1;
5400           const PetscInt ovtlf = ovbrf + 1;
5401           const PetscInt ovtrf = ovtlf + 1;
5402           const PetscInt ovtrb = ovtrf + 1;
5403           const PetscInt ovtlb = ovtrb + 1;
5404           PetscInt       o, n;
5405 
5406           /* Bottom Slice */
5407           /*   bottom */
5408           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
5409           for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5410           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
5411           /*   middle */
5412           for (i = 0; i < k-1; ++i) {
5413             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
5414             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;}
5415             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
5416           }
5417           /*   top */
5418           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
5419           for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5420           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
5421 
5422           /* Middle Slice */
5423           for (j = 0; j < k-1; ++j) {
5424             /*   bottom */
5425             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
5426             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;
5427             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
5428             /*   middle */
5429             for (i = 0; i < k-1; ++i) {
5430               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
5431               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;
5432               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
5433             }
5434             /*   top */
5435             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
5436             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;
5437             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
5438           }
5439 
5440           /* Top Slice */
5441           /*   bottom */
5442           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
5443           for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5444           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
5445           /*   middle */
5446           for (i = 0; i < k-1; ++i) {
5447             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
5448             for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
5449             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
5450           }
5451           /*   top */
5452           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
5453           for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5454           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
5455 
5456           foffset = offset;
5457         }
5458         break;
5459       default: SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5460       }
5461     }
5462     PetscCheck(offset == size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5463     /* Check permutation */
5464     {
5465       PetscInt *check;
5466 
5467       PetscCall(PetscMalloc1(size, &check));
5468       for (i = 0; i < size; ++i) {
5469         check[i] = -1;
5470         PetscCheck(perm[i] >= 0 && perm[i] < size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5471       }
5472       for (i = 0; i < size; ++i) check[perm[i]] = i;
5473       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5474       PetscCall(PetscFree(check));
5475     }
5476     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size, PETSC_OWN_POINTER, perm));
5477     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5478       PetscInt *loc_perm;
5479       PetscCall(PetscMalloc1(size*2, &loc_perm));
5480       for (PetscInt i=0; i<size; i++) {
5481         loc_perm[i] = perm[i];
5482         loc_perm[size+i] = size + perm[i];
5483       }
5484       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size*2, PETSC_OWN_POINTER, loc_perm));
5485     }
5486   }
5487   PetscFunctionReturn(0);
5488 }
5489 
5490 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5491 {
5492   PetscDS        prob;
5493   PetscInt       depth, Nf, h;
5494   DMLabel        label;
5495 
5496   PetscFunctionBeginHot;
5497   PetscCall(DMGetDS(dm, &prob));
5498   Nf      = prob->Nf;
5499   label   = dm->depthLabel;
5500   *dspace = NULL;
5501   if (field < Nf) {
5502     PetscObject disc = prob->disc[field];
5503 
5504     if (disc->classid == PETSCFE_CLASSID) {
5505       PetscDualSpace dsp;
5506 
5507       PetscCall(PetscFEGetDualSpace((PetscFE)disc,&dsp));
5508       PetscCall(DMLabelGetNumValues(label,&depth));
5509       PetscCall(DMLabelGetValue(label,point,&h));
5510       h    = depth - 1 - h;
5511       if (h) {
5512         PetscCall(PetscDualSpaceGetHeightSubspace(dsp,h,dspace));
5513       } else {
5514         *dspace = dsp;
5515       }
5516     }
5517   }
5518   PetscFunctionReturn(0);
5519 }
5520 
5521 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5522 {
5523   PetscScalar    *array, *vArray;
5524   const PetscInt *cone, *coneO;
5525   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
5526 
5527   PetscFunctionBeginHot;
5528   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5529   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5530   PetscCall(DMPlexGetCone(dm, point, &cone));
5531   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5532   if (!values || !*values) {
5533     if ((point >= pStart) && (point < pEnd)) {
5534       PetscInt dof;
5535 
5536       PetscCall(PetscSectionGetDof(section, point, &dof));
5537       size += dof;
5538     }
5539     for (p = 0; p < numPoints; ++p) {
5540       const PetscInt cp = cone[p];
5541       PetscInt       dof;
5542 
5543       if ((cp < pStart) || (cp >= pEnd)) continue;
5544       PetscCall(PetscSectionGetDof(section, cp, &dof));
5545       size += dof;
5546     }
5547     if (!values) {
5548       if (csize) *csize = size;
5549       PetscFunctionReturn(0);
5550     }
5551     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5552   } else {
5553     array = *values;
5554   }
5555   size = 0;
5556   PetscCall(VecGetArray(v, &vArray));
5557   if ((point >= pStart) && (point < pEnd)) {
5558     PetscInt     dof, off, d;
5559     PetscScalar *varr;
5560 
5561     PetscCall(PetscSectionGetDof(section, point, &dof));
5562     PetscCall(PetscSectionGetOffset(section, point, &off));
5563     varr = &vArray[off];
5564     for (d = 0; d < dof; ++d, ++offset) {
5565       array[offset] = varr[d];
5566     }
5567     size += dof;
5568   }
5569   for (p = 0; p < numPoints; ++p) {
5570     const PetscInt cp = cone[p];
5571     PetscInt       o  = coneO[p];
5572     PetscInt       dof, off, d;
5573     PetscScalar   *varr;
5574 
5575     if ((cp < pStart) || (cp >= pEnd)) continue;
5576     PetscCall(PetscSectionGetDof(section, cp, &dof));
5577     PetscCall(PetscSectionGetOffset(section, cp, &off));
5578     varr = &vArray[off];
5579     if (o >= 0) {
5580       for (d = 0; d < dof; ++d, ++offset) {
5581         array[offset] = varr[d];
5582       }
5583     } else {
5584       for (d = dof-1; d >= 0; --d, ++offset) {
5585         array[offset] = varr[d];
5586       }
5587     }
5588     size += dof;
5589   }
5590   PetscCall(VecRestoreArray(v, &vArray));
5591   if (!*values) {
5592     if (csize) *csize = size;
5593     *values = array;
5594   } else {
5595     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5596     *csize = size;
5597   }
5598   PetscFunctionReturn(0);
5599 }
5600 
5601 /* Compress out points not in the section */
5602 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5603 {
5604   const PetscInt np = *numPoints;
5605   PetscInt       pStart, pEnd, p, q;
5606 
5607   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5608   for (p = 0, q = 0; p < np; ++p) {
5609     const PetscInt r = points[p*2];
5610     if ((r >= pStart) && (r < pEnd)) {
5611       points[q*2]   = r;
5612       points[q*2+1] = points[p*2+1];
5613       ++q;
5614     }
5615   }
5616   *numPoints = q;
5617   return 0;
5618 }
5619 
5620 /* Compressed closure does not apply closure permutation */
5621 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5622 {
5623   const PetscInt *cla = NULL;
5624   PetscInt       np, *pts = NULL;
5625 
5626   PetscFunctionBeginHot;
5627   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints));
5628   if (*clPoints) {
5629     PetscInt dof, off;
5630 
5631     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5632     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5633     PetscCall(ISGetIndices(*clPoints, &cla));
5634     np   = dof/2;
5635     pts  = (PetscInt *) &cla[off];
5636   } else {
5637     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5638     PetscCall(CompressPoints_Private(section, &np, pts));
5639   }
5640   *numPoints = np;
5641   *points    = pts;
5642   *clp       = cla;
5643   PetscFunctionReturn(0);
5644 }
5645 
5646 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5647 {
5648   PetscFunctionBeginHot;
5649   if (!*clPoints) {
5650     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5651   } else {
5652     PetscCall(ISRestoreIndices(*clPoints, clp));
5653   }
5654   *numPoints = 0;
5655   *points    = NULL;
5656   *clSec     = NULL;
5657   *clPoints  = NULL;
5658   *clp       = NULL;
5659   PetscFunctionReturn(0);
5660 }
5661 
5662 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5663 {
5664   PetscInt          offset = 0, p;
5665   const PetscInt    **perms = NULL;
5666   const PetscScalar **flips = NULL;
5667 
5668   PetscFunctionBeginHot;
5669   *size = 0;
5670   PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
5671   for (p = 0; p < numPoints; p++) {
5672     const PetscInt    point = points[2*p];
5673     const PetscInt    *perm = perms ? perms[p] : NULL;
5674     const PetscScalar *flip = flips ? flips[p] : NULL;
5675     PetscInt          dof, off, d;
5676     const PetscScalar *varr;
5677 
5678     PetscCall(PetscSectionGetDof(section, point, &dof));
5679     PetscCall(PetscSectionGetOffset(section, point, &off));
5680     varr = &vArray[off];
5681     if (clperm) {
5682       if (perm) {
5683         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5684       } else {
5685         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5686       }
5687       if (flip) {
5688         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5689       }
5690     } else {
5691       if (perm) {
5692         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5693       } else {
5694         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5695       }
5696       if (flip) {
5697         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5698       }
5699     }
5700     offset += dof;
5701   }
5702   PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
5703   *size = offset;
5704   PetscFunctionReturn(0);
5705 }
5706 
5707 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[])
5708 {
5709   PetscInt          offset = 0, f;
5710 
5711   PetscFunctionBeginHot;
5712   *size = 0;
5713   for (f = 0; f < numFields; ++f) {
5714     PetscInt          p;
5715     const PetscInt    **perms = NULL;
5716     const PetscScalar **flips = NULL;
5717 
5718     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5719     for (p = 0; p < numPoints; p++) {
5720       const PetscInt    point = points[2*p];
5721       PetscInt          fdof, foff, b;
5722       const PetscScalar *varr;
5723       const PetscInt    *perm = perms ? perms[p] : NULL;
5724       const PetscScalar *flip = flips ? flips[p] : NULL;
5725 
5726       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5727       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5728       varr = &vArray[foff];
5729       if (clperm) {
5730         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5731         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5732         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5733       } else {
5734         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5735         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5736         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5737       }
5738       offset += fdof;
5739     }
5740     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5741   }
5742   *size = offset;
5743   PetscFunctionReturn(0);
5744 }
5745 
5746 /*@C
5747   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5748 
5749   Not collective
5750 
5751   Input Parameters:
5752 + dm - The DM
5753 . section - The section describing the layout in v, or NULL to use the default section
5754 . v - The local vector
5755 - point - The point in the DM
5756 
5757   Input/Output Parameters:
5758 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5759 - values - An array to use for the values, or NULL to have it allocated automatically;
5760            if the user provided NULL, it is a borrowed array and should not be freed
5761 
5762 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5763 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5764 $ assembly function, and a user may already have allocated storage for this operation.
5765 $
5766 $ A typical use could be
5767 $
5768 $  values = NULL;
5769 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5770 $  for (cl = 0; cl < clSize; ++cl) {
5771 $    <Compute on closure>
5772 $  }
5773 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5774 $
5775 $ or
5776 $
5777 $  PetscMalloc1(clMaxSize, &values);
5778 $  for (p = pStart; p < pEnd; ++p) {
5779 $    clSize = clMaxSize;
5780 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5781 $    for (cl = 0; cl < clSize; ++cl) {
5782 $      <Compute on closure>
5783 $    }
5784 $  }
5785 $  PetscFree(values);
5786 
5787   Fortran Notes:
5788   Since it returns an array, this routine is only available in Fortran 90, and you must
5789   include petsc.h90 in your code.
5790 
5791   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5792 
5793   Level: intermediate
5794 
5795 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5796 @*/
5797 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5798 {
5799   PetscSection       clSection;
5800   IS                 clPoints;
5801   PetscInt          *points = NULL;
5802   const PetscInt    *clp, *perm;
5803   PetscInt           depth, numFields, numPoints, asize;
5804 
5805   PetscFunctionBeginHot;
5806   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5807   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5808   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5809   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5810   PetscCall(DMPlexGetDepth(dm, &depth));
5811   PetscCall(PetscSectionGetNumFields(section, &numFields));
5812   if (depth == 1 && numFields < 2) {
5813     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5814     PetscFunctionReturn(0);
5815   }
5816   /* Get points */
5817   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5818   /* Get sizes */
5819   asize = 0;
5820   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5821     PetscInt dof;
5822     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5823     asize += dof;
5824   }
5825   if (values) {
5826     const PetscScalar *vArray;
5827     PetscInt          size;
5828 
5829     if (*values) {
5830       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);
5831     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5832     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm));
5833     PetscCall(VecGetArrayRead(v, &vArray));
5834     /* Get values */
5835     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5836     else               PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5837     PetscCheck(asize == size,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5838     /* Cleanup array */
5839     PetscCall(VecRestoreArrayRead(v, &vArray));
5840   }
5841   if (csize) *csize = asize;
5842   /* Cleanup points */
5843   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5844   PetscFunctionReturn(0);
5845 }
5846 
5847 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5848 {
5849   DMLabel            depthLabel;
5850   PetscSection       clSection;
5851   IS                 clPoints;
5852   PetscScalar       *array;
5853   const PetscScalar *vArray;
5854   PetscInt          *points = NULL;
5855   const PetscInt    *clp, *perm = NULL;
5856   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5857 
5858   PetscFunctionBeginHot;
5859   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5860   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5861   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5862   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5863   PetscCall(DMPlexGetDepth(dm, &mdepth));
5864   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5865   PetscCall(PetscSectionGetNumFields(section, &numFields));
5866   if (mdepth == 1 && numFields < 2) {
5867     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5868     PetscFunctionReturn(0);
5869   }
5870   /* Get points */
5871   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5872   for (clsize=0,p=0; p<Np; p++) {
5873     PetscInt dof;
5874     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
5875     clsize += dof;
5876   }
5877   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm));
5878   /* Filter points */
5879   for (p = 0; p < numPoints*2; p += 2) {
5880     PetscInt dep;
5881 
5882     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5883     if (dep != depth) continue;
5884     points[Np*2+0] = points[p];
5885     points[Np*2+1] = points[p+1];
5886     ++Np;
5887   }
5888   /* Get array */
5889   if (!values || !*values) {
5890     PetscInt asize = 0, dof;
5891 
5892     for (p = 0; p < Np*2; p += 2) {
5893       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5894       asize += dof;
5895     }
5896     if (!values) {
5897       PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5898       if (csize) *csize = asize;
5899       PetscFunctionReturn(0);
5900     }
5901     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5902   } else {
5903     array = *values;
5904   }
5905   PetscCall(VecGetArrayRead(v, &vArray));
5906   /* Get values */
5907   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5908   else               PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5909   /* Cleanup points */
5910   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5911   /* Cleanup array */
5912   PetscCall(VecRestoreArrayRead(v, &vArray));
5913   if (!*values) {
5914     if (csize) *csize = size;
5915     *values = array;
5916   } else {
5917     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5918     *csize = size;
5919   }
5920   PetscFunctionReturn(0);
5921 }
5922 
5923 /*@C
5924   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5925 
5926   Not collective
5927 
5928   Input Parameters:
5929 + dm - The DM
5930 . section - The section describing the layout in v, or NULL to use the default section
5931 . v - The local vector
5932 . point - The point in the DM
5933 . csize - The number of values in the closure, or NULL
5934 - values - The array of values, which is a borrowed array and should not be freed
5935 
5936   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5937 
5938   Fortran Notes:
5939   Since it returns an array, this routine is only available in Fortran 90, and you must
5940   include petsc.h90 in your code.
5941 
5942   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5943 
5944   Level: intermediate
5945 
5946 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5947 @*/
5948 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5949 {
5950   PetscInt       size = 0;
5951 
5952   PetscFunctionBegin;
5953   /* Should work without recalculating size */
5954   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values));
5955   *values = NULL;
5956   PetscFunctionReturn(0);
5957 }
5958 
5959 static inline void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5960 static inline void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5961 
5962 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[])
5963 {
5964   PetscInt        cdof;   /* The number of constraints on this point */
5965   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5966   PetscScalar    *a;
5967   PetscInt        off, cind = 0, k;
5968 
5969   PetscFunctionBegin;
5970   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5971   PetscCall(PetscSectionGetOffset(section, point, &off));
5972   a    = &array[off];
5973   if (!cdof || setBC) {
5974     if (clperm) {
5975       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5976       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5977     } else {
5978       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5979       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5980     }
5981   } else {
5982     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5983     if (clperm) {
5984       if (perm) {for (k = 0; k < dof; ++k) {
5985           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5986           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5987         }
5988       } else {
5989         for (k = 0; k < dof; ++k) {
5990           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5991           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5992         }
5993       }
5994     } else {
5995       if (perm) {
5996         for (k = 0; k < dof; ++k) {
5997           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5998           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
5999         }
6000       } else {
6001         for (k = 0; k < dof; ++k) {
6002           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6003           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6004         }
6005       }
6006     }
6007   }
6008   PetscFunctionReturn(0);
6009 }
6010 
6011 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[])
6012 {
6013   PetscInt        cdof;   /* The number of constraints on this point */
6014   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6015   PetscScalar    *a;
6016   PetscInt        off, cind = 0, k;
6017 
6018   PetscFunctionBegin;
6019   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6020   PetscCall(PetscSectionGetOffset(section, point, &off));
6021   a    = &array[off];
6022   if (cdof) {
6023     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6024     if (clperm) {
6025       if (perm) {
6026         for (k = 0; k < dof; ++k) {
6027           if ((cind < cdof) && (k == cdofs[cind])) {
6028             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
6029             cind++;
6030           }
6031         }
6032       } else {
6033         for (k = 0; k < dof; ++k) {
6034           if ((cind < cdof) && (k == cdofs[cind])) {
6035             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
6036             cind++;
6037           }
6038         }
6039       }
6040     } else {
6041       if (perm) {
6042         for (k = 0; k < dof; ++k) {
6043           if ((cind < cdof) && (k == cdofs[cind])) {
6044             fuse(&a[k], values[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[offset+     k ] * (flip ? flip[     k ] : 1.));
6052             cind++;
6053           }
6054         }
6055       }
6056     }
6057   }
6058   PetscFunctionReturn(0);
6059 }
6060 
6061 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[])
6062 {
6063   PetscScalar    *a;
6064   PetscInt        fdof, foff, fcdof, foffset = *offset;
6065   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6066   PetscInt        cind = 0, b;
6067 
6068   PetscFunctionBegin;
6069   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6070   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6071   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6072   a    = &array[foff];
6073   if (!fcdof || setBC) {
6074     if (clperm) {
6075       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
6076       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
6077     } else {
6078       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
6079       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
6080     }
6081   } else {
6082     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6083     if (clperm) {
6084       if (perm) {
6085         for (b = 0; b < fdof; b++) {
6086           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6087           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6088         }
6089       } else {
6090         for (b = 0; b < fdof; b++) {
6091           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6092           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6093         }
6094       }
6095     } else {
6096       if (perm) {
6097         for (b = 0; b < fdof; b++) {
6098           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6099           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6100         }
6101       } else {
6102         for (b = 0; b < fdof; b++) {
6103           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6104           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6105         }
6106       }
6107     }
6108   }
6109   *offset += fdof;
6110   PetscFunctionReturn(0);
6111 }
6112 
6113 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[])
6114 {
6115   PetscScalar    *a;
6116   PetscInt        fdof, foff, fcdof, foffset = *offset;
6117   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6118   PetscInt        Nc, cind = 0, ncind = 0, b;
6119   PetscBool       ncSet, fcSet;
6120 
6121   PetscFunctionBegin;
6122   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6123   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6124   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6125   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6126   a    = &array[foff];
6127   if (fcdof) {
6128     /* We just override fcdof and fcdofs with Ncc and comps */
6129     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6130     if (clperm) {
6131       if (perm) {
6132         if (comps) {
6133           for (b = 0; b < fdof; b++) {
6134             ncSet = fcSet = PETSC_FALSE;
6135             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6136             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6137             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
6138           }
6139         } else {
6140           for (b = 0; b < fdof; b++) {
6141             if ((cind < fcdof) && (b == fcdofs[cind])) {
6142               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6143               ++cind;
6144             }
6145           }
6146         }
6147       } else {
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+     b ]] * (flip ? flip[     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+     b ]] * (flip ? flip[     b ] : 1.));
6159               ++cind;
6160             }
6161           }
6162         }
6163       }
6164     } else {
6165       if (perm) {
6166         if (comps) {
6167           for (b = 0; b < fdof; b++) {
6168             ncSet = fcSet = PETSC_FALSE;
6169             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6170             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6171             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
6172           }
6173         } else {
6174           for (b = 0; b < fdof; b++) {
6175             if ((cind < fcdof) && (b == fcdofs[cind])) {
6176               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6177               ++cind;
6178             }
6179           }
6180         }
6181       } else {
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+     b ] * (flip ? flip[     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+     b ] * (flip ? flip[     b ] : 1.));
6193               ++cind;
6194             }
6195           }
6196         }
6197       }
6198     }
6199   }
6200   *offset += fdof;
6201   PetscFunctionReturn(0);
6202 }
6203 
6204 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6205 {
6206   PetscScalar    *array;
6207   const PetscInt *cone, *coneO;
6208   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6209 
6210   PetscFunctionBeginHot;
6211   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6212   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6213   PetscCall(DMPlexGetCone(dm, point, &cone));
6214   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6215   PetscCall(VecGetArray(v, &array));
6216   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6217     const PetscInt cp = !p ? point : cone[p-1];
6218     const PetscInt o  = !p ? 0     : coneO[p-1];
6219 
6220     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
6221     PetscCall(PetscSectionGetDof(section, cp, &dof));
6222     /* ADD_VALUES */
6223     {
6224       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6225       PetscScalar    *a;
6226       PetscInt        cdof, coff, cind = 0, k;
6227 
6228       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6229       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6230       a    = &array[coff];
6231       if (!cdof) {
6232         if (o >= 0) {
6233           for (k = 0; k < dof; ++k) {
6234             a[k] += values[off+k];
6235           }
6236         } else {
6237           for (k = 0; k < dof; ++k) {
6238             a[k] += values[off+dof-k-1];
6239           }
6240         }
6241       } else {
6242         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6243         if (o >= 0) {
6244           for (k = 0; k < dof; ++k) {
6245             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6246             a[k] += values[off+k];
6247           }
6248         } else {
6249           for (k = 0; k < dof; ++k) {
6250             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6251             a[k] += values[off+dof-k-1];
6252           }
6253         }
6254       }
6255     }
6256   }
6257   PetscCall(VecRestoreArray(v, &array));
6258   PetscFunctionReturn(0);
6259 }
6260 
6261 /*@C
6262   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6263 
6264   Not collective
6265 
6266   Input Parameters:
6267 + dm - The DM
6268 . section - The section describing the layout in v, or NULL to use the default section
6269 . v - The local vector
6270 . point - The point in the DM
6271 . values - The array of values
6272 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6273          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6274 
6275   Fortran Notes:
6276   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6277 
6278   Level: intermediate
6279 
6280 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6281 @*/
6282 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6283 {
6284   PetscSection    clSection;
6285   IS              clPoints;
6286   PetscScalar    *array;
6287   PetscInt       *points = NULL;
6288   const PetscInt *clp, *clperm = NULL;
6289   PetscInt        depth, numFields, numPoints, p, clsize;
6290 
6291   PetscFunctionBeginHot;
6292   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6293   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6294   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6295   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6296   PetscCall(DMPlexGetDepth(dm, &depth));
6297   PetscCall(PetscSectionGetNumFields(section, &numFields));
6298   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6299     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6300     PetscFunctionReturn(0);
6301   }
6302   /* Get points */
6303   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6304   for (clsize=0,p=0; p<numPoints; p++) {
6305     PetscInt dof;
6306     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
6307     clsize += dof;
6308   }
6309   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
6310   /* Get array */
6311   PetscCall(VecGetArray(v, &array));
6312   /* Get values */
6313   if (numFields > 0) {
6314     PetscInt offset = 0, f;
6315     for (f = 0; f < numFields; ++f) {
6316       const PetscInt    **perms = NULL;
6317       const PetscScalar **flips = NULL;
6318 
6319       PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6320       switch (mode) {
6321       case INSERT_VALUES:
6322         for (p = 0; p < numPoints; p++) {
6323           const PetscInt    point = points[2*p];
6324           const PetscInt    *perm = perms ? perms[p] : NULL;
6325           const PetscScalar *flip = flips ? flips[p] : NULL;
6326           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6327         } break;
6328       case INSERT_ALL_VALUES:
6329         for (p = 0; p < numPoints; p++) {
6330           const PetscInt    point = points[2*p];
6331           const PetscInt    *perm = perms ? perms[p] : NULL;
6332           const PetscScalar *flip = flips ? flips[p] : NULL;
6333           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6334         } break;
6335       case INSERT_BC_VALUES:
6336         for (p = 0; p < numPoints; p++) {
6337           const PetscInt    point = points[2*p];
6338           const PetscInt    *perm = perms ? perms[p] : NULL;
6339           const PetscScalar *flip = flips ? flips[p] : NULL;
6340           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6341         } break;
6342       case ADD_VALUES:
6343         for (p = 0; p < numPoints; p++) {
6344           const PetscInt    point = points[2*p];
6345           const PetscInt    *perm = perms ? perms[p] : NULL;
6346           const PetscScalar *flip = flips ? flips[p] : NULL;
6347           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6348         } break;
6349       case ADD_ALL_VALUES:
6350         for (p = 0; p < numPoints; p++) {
6351           const PetscInt    point = points[2*p];
6352           const PetscInt    *perm = perms ? perms[p] : NULL;
6353           const PetscScalar *flip = flips ? flips[p] : NULL;
6354           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6355         } break;
6356       case ADD_BC_VALUES:
6357         for (p = 0; p < numPoints; p++) {
6358           const PetscInt    point = points[2*p];
6359           const PetscInt    *perm = perms ? perms[p] : NULL;
6360           const PetscScalar *flip = flips ? flips[p] : NULL;
6361           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6362         } break;
6363       default:
6364         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6365       }
6366       PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6367     }
6368   } else {
6369     PetscInt dof, off;
6370     const PetscInt    **perms = NULL;
6371     const PetscScalar **flips = NULL;
6372 
6373     PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
6374     switch (mode) {
6375     case INSERT_VALUES:
6376       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6377         const PetscInt    point = points[2*p];
6378         const PetscInt    *perm = perms ? perms[p] : NULL;
6379         const PetscScalar *flip = flips ? flips[p] : NULL;
6380         PetscCall(PetscSectionGetDof(section, point, &dof));
6381         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6382       } break;
6383     case INSERT_ALL_VALUES:
6384       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6385         const PetscInt    point = points[2*p];
6386         const PetscInt    *perm = perms ? perms[p] : NULL;
6387         const PetscScalar *flip = flips ? flips[p] : NULL;
6388         PetscCall(PetscSectionGetDof(section, point, &dof));
6389         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
6390       } break;
6391     case INSERT_BC_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         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
6398       } break;
6399     case ADD_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, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
6406       } break;
6407     case ADD_ALL_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         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
6414       } break;
6415     case ADD_BC_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         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
6422       } break;
6423     default:
6424       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6425     }
6426     PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
6427   }
6428   /* Cleanup points */
6429   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6430   /* Cleanup array */
6431   PetscCall(VecRestoreArray(v, &array));
6432   PetscFunctionReturn(0);
6433 }
6434 
6435 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6436 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
6437 {
6438   PetscFunctionBegin;
6439   if (label) {
6440     PetscBool contains;
6441     PetscInt  fdof;
6442 
6443     PetscCall(DMLabelStratumHasPoint(label, labelId, point, &contains));
6444     if (!contains) {
6445       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6446       *offset += fdof;
6447       PetscFunctionReturn(1);
6448     }
6449   }
6450   PetscFunctionReturn(0);
6451 }
6452 
6453 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6454 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)
6455 {
6456   PetscSection    clSection;
6457   IS              clPoints;
6458   PetscScalar    *array;
6459   PetscInt       *points = NULL;
6460   const PetscInt *clp;
6461   PetscInt        numFields, numPoints, p;
6462   PetscInt        offset = 0, f;
6463 
6464   PetscFunctionBeginHot;
6465   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6466   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6467   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6468   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6469   PetscCall(PetscSectionGetNumFields(section, &numFields));
6470   /* Get points */
6471   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6472   /* Get array */
6473   PetscCall(VecGetArray(v, &array));
6474   /* Get values */
6475   for (f = 0; f < numFields; ++f) {
6476     const PetscInt    **perms = NULL;
6477     const PetscScalar **flips = NULL;
6478 
6479     if (!fieldActive[f]) {
6480       for (p = 0; p < numPoints*2; p += 2) {
6481         PetscInt fdof;
6482         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6483         offset += fdof;
6484       }
6485       continue;
6486     }
6487     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6488     switch (mode) {
6489     case INSERT_VALUES:
6490       for (p = 0; p < numPoints; p++) {
6491         const PetscInt    point = points[2*p];
6492         const PetscInt    *perm = perms ? perms[p] : NULL;
6493         const PetscScalar *flip = flips ? flips[p] : NULL;
6494         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6495         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6496       } break;
6497     case INSERT_ALL_VALUES:
6498       for (p = 0; p < numPoints; p++) {
6499         const PetscInt    point = points[2*p];
6500         const PetscInt    *perm = perms ? perms[p] : NULL;
6501         const PetscScalar *flip = flips ? flips[p] : NULL;
6502         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6503         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6504       } break;
6505     case INSERT_BC_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(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6512       } break;
6513     case ADD_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, add, PETSC_FALSE, NULL, values, &offset, array));
6520       } break;
6521     case ADD_ALL_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(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6528       } break;
6529     default:
6530       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6531     }
6532     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6533   }
6534   /* Cleanup points */
6535   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6536   /* Cleanup array */
6537   PetscCall(VecRestoreArray(v, &array));
6538   PetscFunctionReturn(0);
6539 }
6540 
6541 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6542 {
6543   PetscMPIInt    rank;
6544   PetscInt       i, j;
6545 
6546   PetscFunctionBegin;
6547   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6548   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6549   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6550   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6551   numCIndices = numCIndices ? numCIndices : numRIndices;
6552   if (!values) PetscFunctionReturn(0);
6553   for (i = 0; i < numRIndices; i++) {
6554     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6555     for (j = 0; j < numCIndices; j++) {
6556 #if defined(PETSC_USE_COMPLEX)
6557       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j])));
6558 #else
6559       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]));
6560 #endif
6561     }
6562     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6563   }
6564   PetscFunctionReturn(0);
6565 }
6566 
6567 /*
6568   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6569 
6570   Input Parameters:
6571 + section - The section for this data layout
6572 . islocal - Is the section (and thus indices being requested) local or global?
6573 . point   - The point contributing dofs with these indices
6574 . off     - The global offset of this point
6575 . loff    - The local offset of each field
6576 . setBC   - The flag determining whether to include indices of boundary values
6577 . perm    - A permutation of the dofs on this point, or NULL
6578 - indperm - A permutation of the entire indices array, or NULL
6579 
6580   Output Parameter:
6581 . indices - Indices for dofs on this point
6582 
6583   Level: developer
6584 
6585   Note: The indices could be local or global, depending on the value of 'off'.
6586 */
6587 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6588 {
6589   PetscInt        dof;   /* The number of unknowns on this point */
6590   PetscInt        cdof;  /* The number of constraints on this point */
6591   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6592   PetscInt        cind = 0, k;
6593 
6594   PetscFunctionBegin;
6595   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6596   PetscCall(PetscSectionGetDof(section, point, &dof));
6597   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6598   if (!cdof || setBC) {
6599     for (k = 0; k < dof; ++k) {
6600       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6601       const PetscInt ind    = indperm ? indperm[preind] : preind;
6602 
6603       indices[ind] = off + k;
6604     }
6605   } else {
6606     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6607     for (k = 0; k < dof; ++k) {
6608       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6609       const PetscInt ind    = indperm ? indperm[preind] : preind;
6610 
6611       if ((cind < cdof) && (k == cdofs[cind])) {
6612         /* Insert check for returning constrained indices */
6613         indices[ind] = -(off+k+1);
6614         ++cind;
6615       } else {
6616         indices[ind] = off + k - (islocal ? 0 : cind);
6617       }
6618     }
6619   }
6620   *loff += dof;
6621   PetscFunctionReturn(0);
6622 }
6623 
6624 /*
6625  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6626 
6627  Input Parameters:
6628 + section - a section (global or local)
6629 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6630 . point - point within section
6631 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6632 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6633 . setBC - identify constrained (boundary condition) points via involution.
6634 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6635 . permsoff - offset
6636 - indperm - index permutation
6637 
6638  Output Parameter:
6639 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6640 . indices - array to hold indices (as defined by section) of each dof associated with point
6641 
6642  Notes:
6643  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6644  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6645  in the local vector.
6646 
6647  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6648  significant).  It is invalid to call with a global section and setBC=true.
6649 
6650  Developer Note:
6651  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6652  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6653  offset could be obtained from the section instead of passing it explicitly as we do now.
6654 
6655  Example:
6656  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6657  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6658  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6659  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.
6660 
6661  Level: developer
6662 */
6663 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[])
6664 {
6665   PetscInt       numFields, foff, f;
6666 
6667   PetscFunctionBegin;
6668   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6669   PetscCall(PetscSectionGetNumFields(section, &numFields));
6670   for (f = 0, foff = 0; f < numFields; ++f) {
6671     PetscInt        fdof, cfdof;
6672     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6673     PetscInt        cind = 0, b;
6674     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6675 
6676     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6677     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6678     if (!cfdof || setBC) {
6679       for (b = 0; b < fdof; ++b) {
6680         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6681         const PetscInt ind    = indperm ? indperm[preind] : preind;
6682 
6683         indices[ind] = off+foff+b;
6684       }
6685     } else {
6686       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6687       for (b = 0; b < fdof; ++b) {
6688         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6689         const PetscInt ind    = indperm ? indperm[preind] : preind;
6690 
6691         if ((cind < cfdof) && (b == fcdofs[cind])) {
6692           indices[ind] = -(off+foff+b+1);
6693           ++cind;
6694         } else {
6695           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6696         }
6697       }
6698     }
6699     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6700     foffs[f] += fdof;
6701   }
6702   PetscFunctionReturn(0);
6703 }
6704 
6705 /*
6706   This version believes the globalSection offsets for each field, rather than just the point offset
6707 
6708  . foffs - The offset into 'indices' for each field, since it is segregated by field
6709 
6710  Notes:
6711  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6712  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6713 */
6714 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6715 {
6716   PetscInt       numFields, foff, f;
6717 
6718   PetscFunctionBegin;
6719   PetscCall(PetscSectionGetNumFields(section, &numFields));
6720   for (f = 0; f < numFields; ++f) {
6721     PetscInt        fdof, cfdof;
6722     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6723     PetscInt        cind = 0, b;
6724     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6725 
6726     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6727     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6728     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6729     if (!cfdof) {
6730       for (b = 0; b < fdof; ++b) {
6731         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6732         const PetscInt ind    = indperm ? indperm[preind] : preind;
6733 
6734         indices[ind] = foff+b;
6735       }
6736     } else {
6737       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6738       for (b = 0; b < fdof; ++b) {
6739         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6740         const PetscInt ind    = indperm ? indperm[preind] : preind;
6741 
6742         if ((cind < cfdof) && (b == fcdofs[cind])) {
6743           indices[ind] = -(foff+b+1);
6744           ++cind;
6745         } else {
6746           indices[ind] = foff+b-cind;
6747         }
6748       }
6749     }
6750     foffs[f] += fdof;
6751   }
6752   PetscFunctionReturn(0);
6753 }
6754 
6755 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)
6756 {
6757   Mat             cMat;
6758   PetscSection    aSec, cSec;
6759   IS              aIS;
6760   PetscInt        aStart = -1, aEnd = -1;
6761   const PetscInt  *anchors;
6762   PetscInt        numFields, f, p, q, newP = 0;
6763   PetscInt        newNumPoints = 0, newNumIndices = 0;
6764   PetscInt        *newPoints, *indices, *newIndices;
6765   PetscInt        maxAnchor, maxDof;
6766   PetscInt        newOffsets[32];
6767   PetscInt        *pointMatOffsets[32];
6768   PetscInt        *newPointOffsets[32];
6769   PetscScalar     *pointMat[32];
6770   PetscScalar     *newValues=NULL,*tmpValues;
6771   PetscBool       anyConstrained = PETSC_FALSE;
6772 
6773   PetscFunctionBegin;
6774   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6775   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6776   PetscCall(PetscSectionGetNumFields(section, &numFields));
6777 
6778   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
6779   /* if there are point-to-point constraints */
6780   if (aSec) {
6781     PetscCall(PetscArrayzero(newOffsets, 32));
6782     PetscCall(ISGetIndices(aIS,&anchors));
6783     PetscCall(PetscSectionGetChart(aSec,&aStart,&aEnd));
6784     /* figure out how many points are going to be in the new element matrix
6785      * (we allow double counting, because it's all just going to be summed
6786      * into the global matrix anyway) */
6787     for (p = 0; p < 2*numPoints; p+=2) {
6788       PetscInt b    = points[p];
6789       PetscInt bDof = 0, bSecDof;
6790 
6791       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6792       if (!bSecDof) {
6793         continue;
6794       }
6795       if (b >= aStart && b < aEnd) {
6796         PetscCall(PetscSectionGetDof(aSec,b,&bDof));
6797       }
6798       if (bDof) {
6799         /* this point is constrained */
6800         /* it is going to be replaced by its anchors */
6801         PetscInt bOff, q;
6802 
6803         anyConstrained = PETSC_TRUE;
6804         newNumPoints  += bDof;
6805         PetscCall(PetscSectionGetOffset(aSec,b,&bOff));
6806         for (q = 0; q < bDof; q++) {
6807           PetscInt a = anchors[bOff + q];
6808           PetscInt aDof;
6809 
6810           PetscCall(PetscSectionGetDof(section,a,&aDof));
6811           newNumIndices += aDof;
6812           for (f = 0; f < numFields; ++f) {
6813             PetscInt fDof;
6814 
6815             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6816             newOffsets[f+1] += fDof;
6817           }
6818         }
6819       }
6820       else {
6821         /* this point is not constrained */
6822         newNumPoints++;
6823         newNumIndices += bSecDof;
6824         for (f = 0; f < numFields; ++f) {
6825           PetscInt fDof;
6826 
6827           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6828           newOffsets[f+1] += fDof;
6829         }
6830       }
6831     }
6832   }
6833   if (!anyConstrained) {
6834     if (outNumPoints)  *outNumPoints  = 0;
6835     if (outNumIndices) *outNumIndices = 0;
6836     if (outPoints)     *outPoints     = NULL;
6837     if (outValues)     *outValues     = NULL;
6838     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6839     PetscFunctionReturn(0);
6840   }
6841 
6842   if (outNumPoints)  *outNumPoints  = newNumPoints;
6843   if (outNumIndices) *outNumIndices = newNumIndices;
6844 
6845   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6846 
6847   if (!outPoints && !outValues) {
6848     if (offsets) {
6849       for (f = 0; f <= numFields; f++) {
6850         offsets[f] = newOffsets[f];
6851       }
6852     }
6853     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6854     PetscFunctionReturn(0);
6855   }
6856 
6857   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6858 
6859   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6860 
6861   /* workspaces */
6862   if (numFields) {
6863     for (f = 0; f < numFields; f++) {
6864       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
6865       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
6866     }
6867   }
6868   else {
6869     PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
6870     PetscCall(DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]));
6871   }
6872 
6873   /* get workspaces for the point-to-point matrices */
6874   if (numFields) {
6875     PetscInt totalOffset, totalMatOffset;
6876 
6877     for (p = 0; p < numPoints; p++) {
6878       PetscInt b    = points[2*p];
6879       PetscInt bDof = 0, bSecDof;
6880 
6881       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6882       if (!bSecDof) {
6883         for (f = 0; f < numFields; f++) {
6884           newPointOffsets[f][p + 1] = 0;
6885           pointMatOffsets[f][p + 1] = 0;
6886         }
6887         continue;
6888       }
6889       if (b >= aStart && b < aEnd) {
6890         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6891       }
6892       if (bDof) {
6893         for (f = 0; f < numFields; f++) {
6894           PetscInt fDof, q, bOff, allFDof = 0;
6895 
6896           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6897           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6898           for (q = 0; q < bDof; q++) {
6899             PetscInt a = anchors[bOff + q];
6900             PetscInt aFDof;
6901 
6902             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6903             allFDof += aFDof;
6904           }
6905           newPointOffsets[f][p+1] = allFDof;
6906           pointMatOffsets[f][p+1] = fDof * allFDof;
6907         }
6908       }
6909       else {
6910         for (f = 0; f < numFields; f++) {
6911           PetscInt fDof;
6912 
6913           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6914           newPointOffsets[f][p+1] = fDof;
6915           pointMatOffsets[f][p+1] = 0;
6916         }
6917       }
6918     }
6919     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6920       newPointOffsets[f][0] = totalOffset;
6921       pointMatOffsets[f][0] = totalMatOffset;
6922       for (p = 0; p < numPoints; p++) {
6923         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6924         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6925       }
6926       totalOffset    = newPointOffsets[f][numPoints];
6927       totalMatOffset = pointMatOffsets[f][numPoints];
6928       PetscCall(DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
6929     }
6930   }
6931   else {
6932     for (p = 0; p < numPoints; p++) {
6933       PetscInt b    = points[2*p];
6934       PetscInt bDof = 0, bSecDof;
6935 
6936       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6937       if (!bSecDof) {
6938         newPointOffsets[0][p + 1] = 0;
6939         pointMatOffsets[0][p + 1] = 0;
6940         continue;
6941       }
6942       if (b >= aStart && b < aEnd) {
6943         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6944       }
6945       if (bDof) {
6946         PetscInt bOff, q, allDof = 0;
6947 
6948         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6949         for (q = 0; q < bDof; q++) {
6950           PetscInt a = anchors[bOff + q], aDof;
6951 
6952           PetscCall(PetscSectionGetDof(section, a, &aDof));
6953           allDof += aDof;
6954         }
6955         newPointOffsets[0][p+1] = allDof;
6956         pointMatOffsets[0][p+1] = bSecDof * allDof;
6957       }
6958       else {
6959         newPointOffsets[0][p+1] = bSecDof;
6960         pointMatOffsets[0][p+1] = 0;
6961       }
6962     }
6963     newPointOffsets[0][0] = 0;
6964     pointMatOffsets[0][0] = 0;
6965     for (p = 0; p < numPoints; p++) {
6966       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6967       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6968     }
6969     PetscCall(DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
6970   }
6971 
6972   /* output arrays */
6973   PetscCall(DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
6974 
6975   /* get the point-to-point matrices; construct newPoints */
6976   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6977   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6978   PetscCall(DMGetWorkArray(dm,maxDof,MPIU_INT,&indices));
6979   PetscCall(DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
6980   if (numFields) {
6981     for (p = 0, newP = 0; p < numPoints; p++) {
6982       PetscInt b    = points[2*p];
6983       PetscInt o    = points[2*p+1];
6984       PetscInt bDof = 0, bSecDof;
6985 
6986       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6987       if (!bSecDof) {
6988         continue;
6989       }
6990       if (b >= aStart && b < aEnd) {
6991         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6992       }
6993       if (bDof) {
6994         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6995 
6996         fStart[0] = 0;
6997         fEnd[0]   = 0;
6998         for (f = 0; f < numFields; f++) {
6999           PetscInt fDof;
7000 
7001           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7002           fStart[f+1] = fStart[f] + fDof;
7003           fEnd[f+1]   = fStart[f+1];
7004         }
7005         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7006         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7007 
7008         fAnchorStart[0] = 0;
7009         fAnchorEnd[0]   = 0;
7010         for (f = 0; f < numFields; f++) {
7011           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7012 
7013           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
7014           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
7015         }
7016         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7017         for (q = 0; q < bDof; q++) {
7018           PetscInt a = anchors[bOff + q], aOff;
7019 
7020           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7021           newPoints[2*(newP + q)]     = a;
7022           newPoints[2*(newP + q) + 1] = 0;
7023           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7024           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7025         }
7026         newP += bDof;
7027 
7028         if (outValues) {
7029           /* get the point-to-point submatrix */
7030           for (f = 0; f < numFields; f++) {
7031             PetscCall(MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]));
7032           }
7033         }
7034       }
7035       else {
7036         newPoints[2 * newP]     = b;
7037         newPoints[2 * newP + 1] = o;
7038         newP++;
7039       }
7040     }
7041   } else {
7042     for (p = 0; p < numPoints; p++) {
7043       PetscInt b    = points[2*p];
7044       PetscInt o    = points[2*p+1];
7045       PetscInt bDof = 0, bSecDof;
7046 
7047       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7048       if (!bSecDof) {
7049         continue;
7050       }
7051       if (b >= aStart && b < aEnd) {
7052         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7053       }
7054       if (bDof) {
7055         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7056 
7057         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7058         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7059 
7060         PetscCall(PetscSectionGetOffset (aSec, b, &bOff));
7061         for (q = 0; q < bDof; q++) {
7062           PetscInt a = anchors[bOff + q], aOff;
7063 
7064           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7065 
7066           newPoints[2*(newP + q)]     = a;
7067           newPoints[2*(newP + q) + 1] = 0;
7068           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7069           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7070         }
7071         newP += bDof;
7072 
7073         /* get the point-to-point submatrix */
7074         if (outValues) {
7075           PetscCall(MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]));
7076         }
7077       }
7078       else {
7079         newPoints[2 * newP]     = b;
7080         newPoints[2 * newP + 1] = o;
7081         newP++;
7082       }
7083     }
7084   }
7085 
7086   if (outValues) {
7087     PetscCall(DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7088     PetscCall(PetscArrayzero(tmpValues,newNumIndices*numIndices));
7089     /* multiply constraints on the right */
7090     if (numFields) {
7091       for (f = 0; f < numFields; f++) {
7092         PetscInt oldOff = offsets[f];
7093 
7094         for (p = 0; p < numPoints; p++) {
7095           PetscInt cStart = newPointOffsets[f][p];
7096           PetscInt b      = points[2 * p];
7097           PetscInt c, r, k;
7098           PetscInt dof;
7099 
7100           PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7101           if (!dof) {
7102             continue;
7103           }
7104           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7105             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
7106             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
7107 
7108             for (r = 0; r < numIndices; r++) {
7109               for (c = 0; c < nCols; c++) {
7110                 for (k = 0; k < dof; k++) {
7111                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7112                 }
7113               }
7114             }
7115           }
7116           else {
7117             /* copy this column as is */
7118             for (r = 0; r < numIndices; r++) {
7119               for (c = 0; c < dof; c++) {
7120                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7121               }
7122             }
7123           }
7124           oldOff += dof;
7125         }
7126       }
7127     }
7128     else {
7129       PetscInt oldOff = 0;
7130       for (p = 0; p < numPoints; p++) {
7131         PetscInt cStart = newPointOffsets[0][p];
7132         PetscInt b      = points[2 * p];
7133         PetscInt c, r, k;
7134         PetscInt dof;
7135 
7136         PetscCall(PetscSectionGetDof(section,b,&dof));
7137         if (!dof) {
7138           continue;
7139         }
7140         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7141           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
7142           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
7143 
7144           for (r = 0; r < numIndices; r++) {
7145             for (c = 0; c < nCols; c++) {
7146               for (k = 0; k < dof; k++) {
7147                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7148               }
7149             }
7150           }
7151         }
7152         else {
7153           /* copy this column as is */
7154           for (r = 0; r < numIndices; r++) {
7155             for (c = 0; c < dof; c++) {
7156               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7157             }
7158           }
7159         }
7160         oldOff += dof;
7161       }
7162     }
7163 
7164     if (multiplyLeft) {
7165       PetscCall(DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues));
7166       PetscCall(PetscArrayzero(newValues,newNumIndices*newNumIndices));
7167       /* multiply constraints transpose on the left */
7168       if (numFields) {
7169         for (f = 0; f < numFields; f++) {
7170           PetscInt oldOff = offsets[f];
7171 
7172           for (p = 0; p < numPoints; p++) {
7173             PetscInt rStart = newPointOffsets[f][p];
7174             PetscInt b      = points[2 * p];
7175             PetscInt c, r, k;
7176             PetscInt dof;
7177 
7178             PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7179             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7180               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
7181               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
7182 
7183               for (r = 0; r < nRows; r++) {
7184                 for (c = 0; c < newNumIndices; c++) {
7185                   for (k = 0; k < dof; k++) {
7186                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7187                   }
7188                 }
7189               }
7190             }
7191             else {
7192               /* copy this row as is */
7193               for (r = 0; r < dof; r++) {
7194                 for (c = 0; c < newNumIndices; c++) {
7195                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7196                 }
7197               }
7198             }
7199             oldOff += dof;
7200           }
7201         }
7202       }
7203       else {
7204         PetscInt oldOff = 0;
7205 
7206         for (p = 0; p < numPoints; p++) {
7207           PetscInt rStart = newPointOffsets[0][p];
7208           PetscInt b      = points[2 * p];
7209           PetscInt c, r, k;
7210           PetscInt dof;
7211 
7212           PetscCall(PetscSectionGetDof(section,b,&dof));
7213           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7214             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
7215             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
7216 
7217             for (r = 0; r < nRows; r++) {
7218               for (c = 0; c < newNumIndices; c++) {
7219                 for (k = 0; k < dof; k++) {
7220                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7221                 }
7222               }
7223             }
7224           }
7225           else {
7226             /* copy this row as is */
7227             for (r = 0; r < dof; r++) {
7228               for (c = 0; c < newNumIndices; c++) {
7229                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7230               }
7231             }
7232           }
7233           oldOff += dof;
7234         }
7235       }
7236 
7237       PetscCall(DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7238     }
7239     else {
7240       newValues = tmpValues;
7241     }
7242   }
7243 
7244   /* clean up */
7245   PetscCall(DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices));
7246   PetscCall(DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
7247 
7248   if (numFields) {
7249     for (f = 0; f < numFields; f++) {
7250       PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
7251       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
7252       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
7253     }
7254   }
7255   else {
7256     PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
7257     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
7258     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]));
7259   }
7260   PetscCall(ISRestoreIndices(aIS,&anchors));
7261 
7262   /* output */
7263   if (outPoints) {
7264     *outPoints = newPoints;
7265   }
7266   else {
7267     PetscCall(DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
7268   }
7269   if (outValues) {
7270     *outValues = newValues;
7271   }
7272   for (f = 0; f <= numFields; f++) {
7273     offsets[f] = newOffsets[f];
7274   }
7275   PetscFunctionReturn(0);
7276 }
7277 
7278 /*@C
7279   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7280 
7281   Not collective
7282 
7283   Input Parameters:
7284 + dm         - The DM
7285 . section    - The PetscSection describing the points (a local section)
7286 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7287 . point      - The point defining the closure
7288 - useClPerm  - Use the closure point permutation if available
7289 
7290   Output Parameters:
7291 + numIndices - The number of dof indices in the closure of point with the input sections
7292 . indices    - The dof indices
7293 . outOffsets - Array to write the field offsets into, or NULL
7294 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7295 
7296   Notes:
7297   Must call DMPlexRestoreClosureIndices() to free allocated memory
7298 
7299   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7300   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7301   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7302   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7303   indices (with the above semantics) are implied.
7304 
7305   Level: advanced
7306 
7307 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7308 @*/
7309 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7310                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7311 {
7312   /* Closure ordering */
7313   PetscSection        clSection;
7314   IS                  clPoints;
7315   const PetscInt     *clp;
7316   PetscInt           *points;
7317   const PetscInt     *clperm = NULL;
7318   /* Dof permutation and sign flips */
7319   const PetscInt    **perms[32] = {NULL};
7320   const PetscScalar **flips[32] = {NULL};
7321   PetscScalar        *valCopy   = NULL;
7322   /* Hanging node constraints */
7323   PetscInt           *pointsC = NULL;
7324   PetscScalar        *valuesC = NULL;
7325   PetscInt            NclC, NiC;
7326 
7327   PetscInt           *idx;
7328   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
7329   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7330 
7331   PetscFunctionBeginHot;
7332   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7333   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7334   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7335   if (numIndices) PetscValidIntPointer(numIndices, 6);
7336   if (indices)    PetscValidPointer(indices, 7);
7337   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7338   if (values)     PetscValidPointer(values, 9);
7339   PetscCall(PetscSectionGetNumFields(section, &Nf));
7340   PetscCheck(Nf <= 31,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7341   PetscCall(PetscArrayzero(offsets, 32));
7342   /* 1) Get points in closure */
7343   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7344   if (useClPerm) {
7345     PetscInt depth, clsize;
7346     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7347     for (clsize=0,p=0; p<Ncl; p++) {
7348       PetscInt dof;
7349       PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
7350       clsize += dof;
7351     }
7352     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
7353   }
7354   /* 2) Get number of indices on these points and field offsets from section */
7355   for (p = 0; p < Ncl*2; p += 2) {
7356     PetscInt dof, fdof;
7357 
7358     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7359     for (f = 0; f < Nf; ++f) {
7360       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7361       offsets[f+1] += fdof;
7362     }
7363     Ni += dof;
7364   }
7365   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
7366   PetscCheck(!Nf || offsets[Nf] == Ni,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7367   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7368   for (f = 0; f < PetscMax(1, Nf); ++f) {
7369     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7370     else    PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7371     /* may need to apply sign changes to the element matrix */
7372     if (values && flips[f]) {
7373       PetscInt foffset = offsets[f];
7374 
7375       for (p = 0; p < Ncl; ++p) {
7376         PetscInt           pnt  = points[2*p], fdof;
7377         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7378 
7379         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7380         else     PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7381         if (flip) {
7382           PetscInt i, j, k;
7383 
7384           if (!valCopy) {
7385             PetscCall(DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7386             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7387             *values = valCopy;
7388           }
7389           for (i = 0; i < fdof; ++i) {
7390             PetscScalar fval = flip[i];
7391 
7392             for (k = 0; k < Ni; ++k) {
7393               valCopy[Ni * (foffset + i) + k] *= fval;
7394               valCopy[Ni * k + (foffset + i)] *= fval;
7395             }
7396           }
7397         }
7398         foffset += fdof;
7399       }
7400     }
7401   }
7402   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7403   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7404   if (NclC) {
7405     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7406     for (f = 0; f < PetscMax(1, Nf); ++f) {
7407       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7408       else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7409     }
7410     for (f = 0; f < PetscMax(1, Nf); ++f) {
7411       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7412       else    PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7413     }
7414     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7415     Ncl     = NclC;
7416     Ni      = NiC;
7417     points  = pointsC;
7418     if (values) *values = valuesC;
7419   }
7420   /* 5) Calculate indices */
7421   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7422   if (Nf) {
7423     PetscInt  idxOff;
7424     PetscBool useFieldOffsets;
7425 
7426     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
7427     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7428     if (useFieldOffsets) {
7429       for (p = 0; p < Ncl; ++p) {
7430         const PetscInt pnt = points[p*2];
7431 
7432         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7433       }
7434     } else {
7435       for (p = 0; p < Ncl; ++p) {
7436         const PetscInt pnt = points[p*2];
7437 
7438         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7439         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7440          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7441          * global section. */
7442         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7443       }
7444     }
7445   } else {
7446     PetscInt off = 0, idxOff;
7447 
7448     for (p = 0; p < Ncl; ++p) {
7449       const PetscInt  pnt  = points[p*2];
7450       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7451 
7452       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7453       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7454        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7455       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7456     }
7457   }
7458   /* 6) Cleanup */
7459   for (f = 0; f < PetscMax(1, Nf); ++f) {
7460     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7461     else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7462   }
7463   if (NclC) {
7464     PetscCall(DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC));
7465   } else {
7466     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7467   }
7468 
7469   if (numIndices) *numIndices = Ni;
7470   if (indices)    *indices    = idx;
7471   PetscFunctionReturn(0);
7472 }
7473 
7474 /*@C
7475   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7476 
7477   Not collective
7478 
7479   Input Parameters:
7480 + dm         - The DM
7481 . section    - The PetscSection describing the points (a local section)
7482 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7483 . point      - The point defining the closure
7484 - useClPerm  - Use the closure point permutation if available
7485 
7486   Output Parameters:
7487 + numIndices - The number of dof indices in the closure of point with the input sections
7488 . indices    - The dof indices
7489 . outOffsets - Array to write the field offsets into, or NULL
7490 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7491 
7492   Notes:
7493   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7494 
7495   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7496   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7497   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7498   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7499   indices (with the above semantics) are implied.
7500 
7501   Level: advanced
7502 
7503 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7504 @*/
7505 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7506                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7507 {
7508   PetscFunctionBegin;
7509   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7510   PetscValidPointer(indices, 7);
7511   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7512   PetscFunctionReturn(0);
7513 }
7514 
7515 /*@C
7516   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7517 
7518   Not collective
7519 
7520   Input Parameters:
7521 + dm - The DM
7522 . section - The section describing the layout in v, or NULL to use the default section
7523 . globalSection - The section describing the layout in v, or NULL to use the default global section
7524 . A - The matrix
7525 . point - The point in the DM
7526 . values - The array of values
7527 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7528 
7529   Fortran Notes:
7530   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7531 
7532   Level: intermediate
7533 
7534 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7535 @*/
7536 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7537 {
7538   DM_Plex           *mesh = (DM_Plex*) dm->data;
7539   PetscInt          *indices;
7540   PetscInt           numIndices;
7541   const PetscScalar *valuesOrig = values;
7542   PetscErrorCode     ierr;
7543 
7544   PetscFunctionBegin;
7545   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7546   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7547   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7548   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7549   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7550   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7551 
7552   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7553 
7554   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7555   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7556   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7557   if (ierr) {
7558     PetscMPIInt    rank;
7559 
7560     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7561     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7562     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7563     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7564     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7565     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7566   }
7567   if (mesh->printFEM > 1) {
7568     PetscInt i;
7569     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7570     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7571     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7572   }
7573 
7574   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7575   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7576   PetscFunctionReturn(0);
7577 }
7578 
7579 /*@C
7580   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7581 
7582   Not collective
7583 
7584   Input Parameters:
7585 + dmRow - The DM for the row fields
7586 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7587 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7588 . dmCol - The DM for the column fields
7589 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7590 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7591 . A - The matrix
7592 . point - The point in the DMs
7593 . values - The array of values
7594 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7595 
7596   Level: intermediate
7597 
7598 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7599 @*/
7600 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7601 {
7602   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7603   PetscInt          *indicesRow, *indicesCol;
7604   PetscInt           numIndicesRow, numIndicesCol;
7605   const PetscScalar *valuesOrig = values;
7606   PetscErrorCode     ierr;
7607 
7608   PetscFunctionBegin;
7609   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7610   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7611   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7612   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7613   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7614   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7615   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7616   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7617   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7618   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7619   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7620 
7621   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7622   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7623 
7624   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7625   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7626   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7627   if (ierr) {
7628     PetscMPIInt    rank;
7629 
7630     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7631     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7632     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7633     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7634     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7635     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7636   }
7637 
7638   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7639   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7640   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7641   PetscFunctionReturn(0);
7642 }
7643 
7644 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7645 {
7646   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7647   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7648   PetscInt       *cpoints = NULL;
7649   PetscInt       *findices, *cindices;
7650   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7651   PetscInt        foffsets[32], coffsets[32];
7652   DMPolytopeType  ct;
7653   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7654   PetscErrorCode  ierr;
7655 
7656   PetscFunctionBegin;
7657   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7658   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7659   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7660   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7661   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7662   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7663   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7664   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7665   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7666   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7667   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7668   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7669   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7670   PetscCall(PetscArrayzero(foffsets, 32));
7671   PetscCall(PetscArrayzero(coffsets, 32));
7672   /* Column indices */
7673   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7674   maxFPoints = numCPoints;
7675   /* Compress out points not in the section */
7676   /*   TODO: Squeeze out points with 0 dof as well */
7677   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7678   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7679     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7680       cpoints[q*2]   = cpoints[p];
7681       cpoints[q*2+1] = cpoints[p+1];
7682       ++q;
7683     }
7684   }
7685   numCPoints = q;
7686   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7687     PetscInt fdof;
7688 
7689     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7690     if (!dof) continue;
7691     for (f = 0; f < numFields; ++f) {
7692       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7693       coffsets[f+1] += fdof;
7694     }
7695     numCIndices += dof;
7696   }
7697   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7698   /* Row indices */
7699   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7700   {
7701     DMPlexTransform tr;
7702     DMPolytopeType *rct;
7703     PetscInt       *rsize, *rcone, *rornt, Nt;
7704 
7705     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7706     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7707     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7708     numSubcells = rsize[Nt-1];
7709     PetscCall(DMPlexTransformDestroy(&tr));
7710   }
7711   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7712   for (r = 0, q = 0; r < numSubcells; ++r) {
7713     /* TODO Map from coarse to fine cells */
7714     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7715     /* Compress out points not in the section */
7716     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7717     for (p = 0; p < numFPoints*2; p += 2) {
7718       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7719         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7720         if (!dof) continue;
7721         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7722         if (s < q) continue;
7723         ftotpoints[q*2]   = fpoints[p];
7724         ftotpoints[q*2+1] = fpoints[p+1];
7725         ++q;
7726       }
7727     }
7728     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7729   }
7730   numFPoints = q;
7731   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7732     PetscInt fdof;
7733 
7734     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7735     if (!dof) continue;
7736     for (f = 0; f < numFields; ++f) {
7737       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7738       foffsets[f+1] += fdof;
7739     }
7740     numFIndices += dof;
7741   }
7742   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7743 
7744   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7745   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7746   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7747   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7748   if (numFields) {
7749     const PetscInt **permsF[32] = {NULL};
7750     const PetscInt **permsC[32] = {NULL};
7751 
7752     for (f = 0; f < numFields; f++) {
7753       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7754       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7755     }
7756     for (p = 0; p < numFPoints; p++) {
7757       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7758       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7759     }
7760     for (p = 0; p < numCPoints; p++) {
7761       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7762       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7763     }
7764     for (f = 0; f < numFields; f++) {
7765       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7766       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7767     }
7768   } else {
7769     const PetscInt **permsF = NULL;
7770     const PetscInt **permsC = NULL;
7771 
7772     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7773     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7774     for (p = 0, off = 0; p < numFPoints; p++) {
7775       const PetscInt *perm = permsF ? permsF[p] : NULL;
7776 
7777       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7778       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7779     }
7780     for (p = 0, off = 0; p < numCPoints; p++) {
7781       const PetscInt *perm = permsC ? permsC[p] : NULL;
7782 
7783       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7784       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7785     }
7786     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7787     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7788   }
7789   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7790   /* TODO: flips */
7791   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7792   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7793   if (ierr) {
7794     PetscMPIInt    rank;
7795 
7796     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7797     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7798     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7799     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7800     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7801   }
7802   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7803   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7804   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7805   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7806   PetscFunctionReturn(0);
7807 }
7808 
7809 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7810 {
7811   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7812   PetscInt      *cpoints = NULL;
7813   PetscInt       foffsets[32], coffsets[32];
7814   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7815   DMPolytopeType ct;
7816   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7817 
7818   PetscFunctionBegin;
7819   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7820   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7821   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7822   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7823   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7824   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7825   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7826   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7827   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7828   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7829   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7830   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7831   PetscCall(PetscArrayzero(foffsets, 32));
7832   PetscCall(PetscArrayzero(coffsets, 32));
7833   /* Column indices */
7834   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7835   maxFPoints = numCPoints;
7836   /* Compress out points not in the section */
7837   /*   TODO: Squeeze out points with 0 dof as well */
7838   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7839   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7840     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7841       cpoints[q*2]   = cpoints[p];
7842       cpoints[q*2+1] = cpoints[p+1];
7843       ++q;
7844     }
7845   }
7846   numCPoints = q;
7847   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7848     PetscInt fdof;
7849 
7850     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7851     if (!dof) continue;
7852     for (f = 0; f < numFields; ++f) {
7853       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7854       coffsets[f+1] += fdof;
7855     }
7856     numCIndices += dof;
7857   }
7858   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7859   /* Row indices */
7860   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7861   {
7862     DMPlexTransform tr;
7863     DMPolytopeType *rct;
7864     PetscInt       *rsize, *rcone, *rornt, Nt;
7865 
7866     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7867     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7868     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7869     numSubcells = rsize[Nt-1];
7870     PetscCall(DMPlexTransformDestroy(&tr));
7871   }
7872   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7873   for (r = 0, q = 0; r < numSubcells; ++r) {
7874     /* TODO Map from coarse to fine cells */
7875     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7876     /* Compress out points not in the section */
7877     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7878     for (p = 0; p < numFPoints*2; p += 2) {
7879       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7880         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7881         if (!dof) continue;
7882         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7883         if (s < q) continue;
7884         ftotpoints[q*2]   = fpoints[p];
7885         ftotpoints[q*2+1] = fpoints[p+1];
7886         ++q;
7887       }
7888     }
7889     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7890   }
7891   numFPoints = q;
7892   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7893     PetscInt fdof;
7894 
7895     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7896     if (!dof) continue;
7897     for (f = 0; f < numFields; ++f) {
7898       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7899       foffsets[f+1] += fdof;
7900     }
7901     numFIndices += dof;
7902   }
7903   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7904 
7905   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7906   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7907   if (numFields) {
7908     const PetscInt **permsF[32] = {NULL};
7909     const PetscInt **permsC[32] = {NULL};
7910 
7911     for (f = 0; f < numFields; f++) {
7912       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7913       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7914     }
7915     for (p = 0; p < numFPoints; p++) {
7916       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7917       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7918     }
7919     for (p = 0; p < numCPoints; p++) {
7920       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7921       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7922     }
7923     for (f = 0; f < numFields; f++) {
7924       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7925       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7926     }
7927   } else {
7928     const PetscInt **permsF = NULL;
7929     const PetscInt **permsC = NULL;
7930 
7931     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7932     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7933     for (p = 0, off = 0; p < numFPoints; p++) {
7934       const PetscInt *perm = permsF ? permsF[p] : NULL;
7935 
7936       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7937       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7938     }
7939     for (p = 0, off = 0; p < numCPoints; p++) {
7940       const PetscInt *perm = permsC ? permsC[p] : NULL;
7941 
7942       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7943       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7944     }
7945     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7946     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7947   }
7948   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7949   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7950   PetscFunctionReturn(0);
7951 }
7952 
7953 /*@C
7954   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7955 
7956   Input Parameter:
7957 . dm   - The DMPlex object
7958 
7959   Output Parameter:
7960 . cellHeight - The height of a cell
7961 
7962   Level: developer
7963 
7964 .seealso `DMPlexSetVTKCellHeight()`
7965 @*/
7966 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7967 {
7968   DM_Plex *mesh = (DM_Plex*) dm->data;
7969 
7970   PetscFunctionBegin;
7971   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7972   PetscValidIntPointer(cellHeight, 2);
7973   *cellHeight = mesh->vtkCellHeight;
7974   PetscFunctionReturn(0);
7975 }
7976 
7977 /*@C
7978   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7979 
7980   Input Parameters:
7981 + dm   - The DMPlex object
7982 - cellHeight - The height of a cell
7983 
7984   Level: developer
7985 
7986 .seealso `DMPlexGetVTKCellHeight()`
7987 @*/
7988 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7989 {
7990   DM_Plex *mesh = (DM_Plex*) dm->data;
7991 
7992   PetscFunctionBegin;
7993   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7994   mesh->vtkCellHeight = cellHeight;
7995   PetscFunctionReturn(0);
7996 }
7997 
7998 /*@
7999   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8000 
8001   Input Parameter:
8002 . dm - The DMPlex object
8003 
8004   Output Parameters:
8005 + gcStart - The first ghost cell, or NULL
8006 - gcEnd   - The upper bound on ghost cells, or NULL
8007 
8008   Level: advanced
8009 
8010 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8011 @*/
8012 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8013 {
8014   DMLabel        ctLabel;
8015 
8016   PetscFunctionBegin;
8017   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8018   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8019   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8020   PetscFunctionReturn(0);
8021 }
8022 
8023 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8024 {
8025   PetscSection   section, globalSection;
8026   PetscInt      *numbers, p;
8027 
8028   PetscFunctionBegin;
8029   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
8030   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8031   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8032   for (p = pStart; p < pEnd; ++p) {
8033     PetscCall(PetscSectionSetDof(section, p, 1));
8034   }
8035   PetscCall(PetscSectionSetUp(section));
8036   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8037   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8038   for (p = pStart; p < pEnd; ++p) {
8039     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8040     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8041     else                       numbers[p-pStart] += shift;
8042   }
8043   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8044   if (globalSize) {
8045     PetscLayout layout;
8046     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8047     PetscCall(PetscLayoutGetSize(layout, globalSize));
8048     PetscCall(PetscLayoutDestroy(&layout));
8049   }
8050   PetscCall(PetscSectionDestroy(&section));
8051   PetscCall(PetscSectionDestroy(&globalSection));
8052   PetscFunctionReturn(0);
8053 }
8054 
8055 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8056 {
8057   PetscInt       cellHeight, cStart, cEnd;
8058 
8059   PetscFunctionBegin;
8060   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8061   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8062   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8063   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8064   PetscFunctionReturn(0);
8065 }
8066 
8067 /*@
8068   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8069 
8070   Input Parameter:
8071 . dm   - The DMPlex object
8072 
8073   Output Parameter:
8074 . globalCellNumbers - Global cell numbers for all cells on this process
8075 
8076   Level: developer
8077 
8078 .seealso `DMPlexGetVertexNumbering()`
8079 @*/
8080 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8081 {
8082   DM_Plex       *mesh = (DM_Plex*) dm->data;
8083 
8084   PetscFunctionBegin;
8085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8086   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8087   *globalCellNumbers = mesh->globalCellNumbers;
8088   PetscFunctionReturn(0);
8089 }
8090 
8091 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8092 {
8093   PetscInt       vStart, vEnd;
8094 
8095   PetscFunctionBegin;
8096   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8097   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8098   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8099   PetscFunctionReturn(0);
8100 }
8101 
8102 /*@
8103   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8104 
8105   Input Parameter:
8106 . dm   - The DMPlex object
8107 
8108   Output Parameter:
8109 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8110 
8111   Level: developer
8112 
8113 .seealso `DMPlexGetCellNumbering()`
8114 @*/
8115 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8116 {
8117   DM_Plex       *mesh = (DM_Plex*) dm->data;
8118 
8119   PetscFunctionBegin;
8120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8121   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8122   *globalVertexNumbers = mesh->globalVertexNumbers;
8123   PetscFunctionReturn(0);
8124 }
8125 
8126 /*@
8127   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
8128 
8129   Input Parameter:
8130 . dm   - The DMPlex object
8131 
8132   Output Parameter:
8133 . globalPointNumbers - Global numbers for all points on this process
8134 
8135   Level: developer
8136 
8137 .seealso `DMPlexGetCellNumbering()`
8138 @*/
8139 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8140 {
8141   IS             nums[4];
8142   PetscInt       depths[4], gdepths[4], starts[4];
8143   PetscInt       depth, d, shift = 0;
8144 
8145   PetscFunctionBegin;
8146   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8147   PetscCall(DMPlexGetDepth(dm, &depth));
8148   /* For unstratified meshes use dim instead of depth */
8149   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8150   for (d = 0; d <= depth; ++d) {
8151     PetscInt end;
8152 
8153     depths[d] = depth-d;
8154     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8155     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8156   }
8157   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8158   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8159   for (d = 0; d <= depth; ++d) {
8160     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]);
8161   }
8162   for (d = 0; d <= depth; ++d) {
8163     PetscInt pStart, pEnd, gsize;
8164 
8165     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8166     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8167     shift += gsize;
8168   }
8169   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8170   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8171   PetscFunctionReturn(0);
8172 }
8173 
8174 /*@
8175   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8176 
8177   Input Parameter:
8178 . dm - The DMPlex object
8179 
8180   Output Parameter:
8181 . ranks - The rank field
8182 
8183   Options Database Keys:
8184 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8185 
8186   Level: intermediate
8187 
8188 .seealso: `DMView()`
8189 @*/
8190 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8191 {
8192   DM             rdm;
8193   PetscFE        fe;
8194   PetscScalar   *r;
8195   PetscMPIInt    rank;
8196   DMPolytopeType ct;
8197   PetscInt       dim, cStart, cEnd, c;
8198   PetscBool      simplex;
8199 
8200   PetscFunctionBeginUser;
8201   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8202   PetscValidPointer(ranks, 2);
8203   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8204   PetscCall(DMClone(dm, &rdm));
8205   PetscCall(DMGetDimension(rdm, &dim));
8206   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8207   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8208   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8209   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8210   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8211   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8212   PetscCall(PetscFEDestroy(&fe));
8213   PetscCall(DMCreateDS(rdm));
8214   PetscCall(DMCreateGlobalVector(rdm, ranks));
8215   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8216   PetscCall(VecGetArray(*ranks, &r));
8217   for (c = cStart; c < cEnd; ++c) {
8218     PetscScalar *lr;
8219 
8220     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8221     if (lr) *lr = rank;
8222   }
8223   PetscCall(VecRestoreArray(*ranks, &r));
8224   PetscCall(DMDestroy(&rdm));
8225   PetscFunctionReturn(0);
8226 }
8227 
8228 /*@
8229   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8230 
8231   Input Parameters:
8232 + dm    - The DMPlex
8233 - label - The DMLabel
8234 
8235   Output Parameter:
8236 . val - The label value field
8237 
8238   Options Database Keys:
8239 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8240 
8241   Level: intermediate
8242 
8243 .seealso: `DMView()`
8244 @*/
8245 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8246 {
8247   DM             rdm;
8248   PetscFE        fe;
8249   PetscScalar   *v;
8250   PetscInt       dim, cStart, cEnd, c;
8251 
8252   PetscFunctionBeginUser;
8253   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8254   PetscValidPointer(label, 2);
8255   PetscValidPointer(val, 3);
8256   PetscCall(DMClone(dm, &rdm));
8257   PetscCall(DMGetDimension(rdm, &dim));
8258   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8259   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8260   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8261   PetscCall(PetscFEDestroy(&fe));
8262   PetscCall(DMCreateDS(rdm));
8263   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8264   PetscCall(DMCreateGlobalVector(rdm, val));
8265   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8266   PetscCall(VecGetArray(*val, &v));
8267   for (c = cStart; c < cEnd; ++c) {
8268     PetscScalar *lv;
8269     PetscInt     cval;
8270 
8271     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8272     PetscCall(DMLabelGetValue(label, c, &cval));
8273     *lv = cval;
8274   }
8275   PetscCall(VecRestoreArray(*val, &v));
8276   PetscCall(DMDestroy(&rdm));
8277   PetscFunctionReturn(0);
8278 }
8279 
8280 /*@
8281   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8282 
8283   Input Parameter:
8284 . dm - The DMPlex object
8285 
8286   Notes:
8287   This is a useful diagnostic when creating meshes programmatically.
8288 
8289   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8290 
8291   Level: developer
8292 
8293 .seealso: `DMCreate()`, `DMSetFromOptions()`
8294 @*/
8295 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8296 {
8297   PetscSection    coneSection, supportSection;
8298   const PetscInt *cone, *support;
8299   PetscInt        coneSize, c, supportSize, s;
8300   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8301   PetscBool       storagecheck = PETSC_TRUE;
8302 
8303   PetscFunctionBegin;
8304   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8305   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8306   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8307   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8308   /* Check that point p is found in the support of its cone points, and vice versa */
8309   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8310   for (p = pStart; p < pEnd; ++p) {
8311     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8312     PetscCall(DMPlexGetCone(dm, p, &cone));
8313     for (c = 0; c < coneSize; ++c) {
8314       PetscBool dup = PETSC_FALSE;
8315       PetscInt  d;
8316       for (d = c-1; d >= 0; --d) {
8317         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8318       }
8319       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8320       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8321       for (s = 0; s < supportSize; ++s) {
8322         if (support[s] == p) break;
8323       }
8324       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8325         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8326         for (s = 0; s < coneSize; ++s) {
8327           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8328         }
8329         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8330         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8331         for (s = 0; s < supportSize; ++s) {
8332           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8333         }
8334         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8335         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]);
8336         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8337       }
8338     }
8339     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8340     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8341     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8342     PetscCall(DMPlexGetSupport(dm, p, &support));
8343     for (s = 0; s < supportSize; ++s) {
8344       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8345       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8346       for (c = 0; c < coneSize; ++c) {
8347         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8348         if (cone[c] != pp) { c = 0; break; }
8349         if (cone[c] == p) break;
8350       }
8351       if (c >= coneSize) {
8352         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8353         for (c = 0; c < supportSize; ++c) {
8354           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8355         }
8356         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8357         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8358         for (c = 0; c < coneSize; ++c) {
8359           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8360         }
8361         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8362         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8363       }
8364     }
8365   }
8366   if (storagecheck) {
8367     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8368     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8369     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8370   }
8371   PetscFunctionReturn(0);
8372 }
8373 
8374 /*
8375   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.
8376 */
8377 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8378 {
8379   DMPolytopeType  cct;
8380   PetscInt        ptpoints[4];
8381   const PetscInt *cone, *ccone, *ptcone;
8382   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8383 
8384   PetscFunctionBegin;
8385   *unsplit = 0;
8386   switch (ct) {
8387     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8388       ptpoints[npt++] = c;
8389       break;
8390     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8391       PetscCall(DMPlexGetCone(dm, c, &cone));
8392       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8393       for (cp = 0; cp < coneSize; ++cp) {
8394         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8395         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8396       }
8397       break;
8398     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8399     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8400       PetscCall(DMPlexGetCone(dm, c, &cone));
8401       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8402       for (cp = 0; cp < coneSize; ++cp) {
8403         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8404         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8405         for (ccp = 0; ccp < cconeSize; ++ccp) {
8406           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8407           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8408             PetscInt p;
8409             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8410             if (p == npt) ptpoints[npt++] = ccone[ccp];
8411           }
8412         }
8413       }
8414       break;
8415     default: break;
8416   }
8417   for (pt = 0; pt < npt; ++pt) {
8418     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8419     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8420   }
8421   PetscFunctionReturn(0);
8422 }
8423 
8424 /*@
8425   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8426 
8427   Input Parameters:
8428 + dm - The DMPlex object
8429 - cellHeight - Normally 0
8430 
8431   Notes:
8432   This is a useful diagnostic when creating meshes programmatically.
8433   Currently applicable only to homogeneous simplex or tensor meshes.
8434 
8435   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8436 
8437   Level: developer
8438 
8439 .seealso: `DMCreate()`, `DMSetFromOptions()`
8440 @*/
8441 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8442 {
8443   DMPlexInterpolatedFlag interp;
8444   DMPolytopeType         ct;
8445   PetscInt               vStart, vEnd, cStart, cEnd, c;
8446 
8447   PetscFunctionBegin;
8448   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8449   PetscCall(DMPlexIsInterpolated(dm, &interp));
8450   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8451   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8452   for (c = cStart; c < cEnd; ++c) {
8453     PetscInt *closure = NULL;
8454     PetscInt  coneSize, closureSize, cl, Nv = 0;
8455 
8456     PetscCall(DMPlexGetCellType(dm, c, &ct));
8457     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8458     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8459     if (interp == DMPLEX_INTERPOLATED_FULL) {
8460       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8461       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));
8462     }
8463     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8464     for (cl = 0; cl < closureSize*2; cl += 2) {
8465       const PetscInt p = closure[cl];
8466       if ((p >= vStart) && (p < vEnd)) ++Nv;
8467     }
8468     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8469     /* Special Case: Tensor faces with identified vertices */
8470     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8471       PetscInt unsplit;
8472 
8473       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8474       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8475     }
8476     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));
8477   }
8478   PetscFunctionReturn(0);
8479 }
8480 
8481 /*@
8482   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8483 
8484   Collective
8485 
8486   Input Parameters:
8487 + dm - The DMPlex object
8488 - cellHeight - Normally 0
8489 
8490   Notes:
8491   This is a useful diagnostic when creating meshes programmatically.
8492   This routine is only relevant for meshes that are fully interpolated across all ranks.
8493   It will error out if a partially interpolated mesh is given on some rank.
8494   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8495 
8496   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8497 
8498   Level: developer
8499 
8500 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8501 @*/
8502 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8503 {
8504   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8505   DMPlexInterpolatedFlag interpEnum;
8506 
8507   PetscFunctionBegin;
8508   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8509   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8510   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8511   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8512     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8513     PetscFunctionReturn(0);
8514   }
8515 
8516   PetscCall(DMGetDimension(dm, &dim));
8517   PetscCall(DMPlexGetDepth(dm, &depth));
8518   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8519   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8520     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8521     for (c = cStart; c < cEnd; ++c) {
8522       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8523       const DMPolytopeType *faceTypes;
8524       DMPolytopeType        ct;
8525       PetscInt              numFaces, coneSize, f;
8526       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8527 
8528       PetscCall(DMPlexGetCellType(dm, c, &ct));
8529       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8530       if (unsplit) continue;
8531       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8532       PetscCall(DMPlexGetCone(dm, c, &cone));
8533       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8534       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8535       for (cl = 0; cl < closureSize*2; cl += 2) {
8536         const PetscInt p = closure[cl];
8537         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8538       }
8539       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8540       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);
8541       for (f = 0; f < numFaces; ++f) {
8542         DMPolytopeType fct;
8543         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8544 
8545         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8546         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8547         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8548           const PetscInt p = fclosure[cl];
8549           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8550         }
8551         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]);
8552         for (v = 0; v < fnumCorners; ++v) {
8553           if (fclosure[v] != faces[fOff+v]) {
8554             PetscInt v1;
8555 
8556             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8557             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8558             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8559             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff+v1]));
8560             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8561             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]);
8562           }
8563         }
8564         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8565         fOff += faceSizes[f];
8566       }
8567       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8568       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8569     }
8570   }
8571   PetscFunctionReturn(0);
8572 }
8573 
8574 /*@
8575   DMPlexCheckGeometry - Check the geometry of mesh cells
8576 
8577   Input Parameter:
8578 . dm - The DMPlex object
8579 
8580   Notes:
8581   This is a useful diagnostic when creating meshes programmatically.
8582 
8583   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8584 
8585   Level: developer
8586 
8587 .seealso: `DMCreate()`, `DMSetFromOptions()`
8588 @*/
8589 PetscErrorCode DMPlexCheckGeometry(DM dm)
8590 {
8591   Vec       coordinates;
8592   PetscReal detJ, J[9], refVol = 1.0;
8593   PetscReal vol;
8594   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8595 
8596   PetscFunctionBegin;
8597   PetscCall(DMGetDimension(dm, &dim));
8598   PetscCall(DMGetCoordinateDim(dm, &dE));
8599   if (dim != dE) PetscFunctionReturn(0);
8600   PetscCall(DMPlexGetDepth(dm, &depth));
8601   for (d = 0; d < dim; ++d) refVol *= 2.0;
8602   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8603   /* Make sure local coordinates are created, because that step is collective */
8604   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8605   for (c = cStart; c < cEnd; ++c) {
8606     DMPolytopeType ct;
8607     PetscInt       unsplit;
8608     PetscBool      ignoreZeroVol = PETSC_FALSE;
8609 
8610     PetscCall(DMPlexGetCellType(dm, c, &ct));
8611     switch (ct) {
8612       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8613       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8614       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8615         ignoreZeroVol = PETSC_TRUE; break;
8616       default: break;
8617     }
8618     switch (ct) {
8619       case DM_POLYTOPE_TRI_PRISM:
8620       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8621       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8622       case DM_POLYTOPE_PYRAMID:
8623         continue;
8624       default: break;
8625     }
8626     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8627     if (unsplit) continue;
8628     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8629     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);
8630     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ*refVol)));
8631     /* This should work with periodicity since DG coordinates should be used */
8632     if (depth > 1) {
8633       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8634       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);
8635       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double) vol));
8636     }
8637   }
8638   PetscFunctionReturn(0);
8639 }
8640 
8641 /*@
8642   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8643 
8644   Collective
8645 
8646   Input Parameters:
8647 + dm - The DMPlex object
8648 - pointSF - The Point SF, or NULL for Point SF attached to DM
8649 
8650   Notes:
8651   This is mainly intended for debugging/testing purposes.
8652 
8653   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8654 
8655   Level: developer
8656 
8657 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8658 @*/
8659 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF)
8660 {
8661   PetscInt        l, nleaves, nroots, overlap;
8662   const PetscInt *locals;
8663   const PetscSFNode *remotes;
8664   PetscBool       distributed;
8665   MPI_Comm        comm;
8666   PetscMPIInt     rank;
8667 
8668   PetscFunctionBegin;
8669   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8670   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8671   else         pointSF = dm->sf;
8672   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8673   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8674   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8675   {
8676     PetscMPIInt    mpiFlag;
8677 
8678     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF),&mpiFlag));
8679     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)",mpiFlag);
8680   }
8681   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8682   PetscCall(DMPlexIsDistributed(dm, &distributed));
8683   if (!distributed) {
8684     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);
8685     PetscFunctionReturn(0);
8686   }
8687   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);
8688   PetscCall(DMPlexGetOverlap(dm, &overlap));
8689 
8690   /* Check SF graph is compatible with DMPlex chart */
8691   {
8692     PetscInt pStart, pEnd, maxLeaf;
8693 
8694     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8695     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8696     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd-pStart, nroots);
8697     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8698   }
8699 
8700   /* Check Point SF has no local points referenced */
8701   for (l = 0; l < nleaves; l++) {
8702     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);
8703   }
8704 
8705   /* Check there are no cells in interface */
8706   if (!overlap) {
8707     PetscInt cellHeight, cStart, cEnd;
8708 
8709     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8710     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8711     for (l = 0; l < nleaves; ++l) {
8712       const PetscInt point = locals ? locals[l] : l;
8713 
8714       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8715     }
8716   }
8717 
8718   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8719   {
8720     const PetscInt *rootdegree;
8721 
8722     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8723     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8724     for (l = 0; l < nleaves; ++l) {
8725       const PetscInt  point = locals ? locals[l] : l;
8726       const PetscInt *cone;
8727       PetscInt        coneSize, c, idx;
8728 
8729       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8730       PetscCall(DMPlexGetCone(dm, point, &cone));
8731       for (c = 0; c < coneSize; ++c) {
8732         if (!rootdegree[cone[c]]) {
8733           if (locals) {
8734             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8735           } else {
8736             idx = (cone[c] < nleaves) ? cone[c] : -1;
8737           }
8738           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8739         }
8740       }
8741     }
8742   }
8743   PetscFunctionReturn(0);
8744 }
8745 
8746 /*@
8747   DMPlexCheck - Perform various checks of Plex sanity
8748 
8749   Input Parameter:
8750 . dm - The DMPlex object
8751 
8752   Notes:
8753   This is a useful diagnostic when creating meshes programmatically.
8754 
8755   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8756 
8757   Currently does not include DMPlexCheckCellShape().
8758 
8759   Level: developer
8760 
8761 .seealso: DMCreate(), DMSetFromOptions()
8762 @*/
8763 PetscErrorCode DMPlexCheck(DM dm)
8764 {
8765   PetscInt cellHeight;
8766 
8767   PetscFunctionBegin;
8768   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8769   PetscCall(DMPlexCheckSymmetry(dm));
8770   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8771   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8772   PetscCall(DMPlexCheckGeometry(dm));
8773   PetscCall(DMPlexCheckPointSF(dm, NULL));
8774   PetscCall(DMPlexCheckInterfaceCones(dm));
8775   PetscFunctionReturn(0);
8776 }
8777 
8778 typedef struct cell_stats
8779 {
8780   PetscReal min, max, sum, squaresum;
8781   PetscInt  count;
8782 } cell_stats_t;
8783 
8784 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8785 {
8786   PetscInt i, N = *len;
8787 
8788   for (i = 0; i < N; i++) {
8789     cell_stats_t *A = (cell_stats_t *) a;
8790     cell_stats_t *B = (cell_stats_t *) b;
8791 
8792     B->min = PetscMin(A->min,B->min);
8793     B->max = PetscMax(A->max,B->max);
8794     B->sum += A->sum;
8795     B->squaresum += A->squaresum;
8796     B->count += A->count;
8797   }
8798 }
8799 
8800 /*@
8801   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8802 
8803   Collective on dm
8804 
8805   Input Parameters:
8806 + dm        - The DMPlex object
8807 . output    - If true, statistics will be displayed on stdout
8808 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8809 
8810   Notes:
8811   This is mainly intended for debugging/testing purposes.
8812 
8813   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8814 
8815   Level: developer
8816 
8817 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8818 @*/
8819 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8820 {
8821   DM             dmCoarse;
8822   cell_stats_t   stats, globalStats;
8823   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8824   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8825   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8826   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8827   PetscMPIInt    rank,size;
8828 
8829   PetscFunctionBegin;
8830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8831   stats.min   = PETSC_MAX_REAL;
8832   stats.max   = PETSC_MIN_REAL;
8833   stats.sum   = stats.squaresum = 0.;
8834   stats.count = 0;
8835 
8836   PetscCallMPI(MPI_Comm_size(comm, &size));
8837   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8838   PetscCall(DMGetCoordinateDim(dm,&cdim));
8839   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8840   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8841   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8842   for (c = cStart; c < cEnd; c++) {
8843     PetscInt  i;
8844     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8845 
8846     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8847     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8848     for (i = 0; i < PetscSqr(cdim); ++i) {
8849       frobJ    += J[i] * J[i];
8850       frobInvJ += invJ[i] * invJ[i];
8851     }
8852     cond2 = frobJ * frobInvJ;
8853     cond  = PetscSqrtReal(cond2);
8854 
8855     stats.min        = PetscMin(stats.min,cond);
8856     stats.max        = PetscMax(stats.max,cond);
8857     stats.sum       += cond;
8858     stats.squaresum += cond2;
8859     stats.count++;
8860     if (output && cond > limit) {
8861       PetscSection coordSection;
8862       Vec          coordsLocal;
8863       PetscScalar *coords = NULL;
8864       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8865 
8866       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8867       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8868       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8869       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double) cond));
8870       for (i = 0; i < Nv/cdim; ++i) {
8871         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8872         for (d = 0; d < cdim; ++d) {
8873           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8874           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8875         }
8876         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8877       }
8878       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8879       for (cl = 0; cl < clSize*2; cl += 2) {
8880         const PetscInt edge = closure[cl];
8881 
8882         if ((edge >= eStart) && (edge < eEnd)) {
8883           PetscReal len;
8884 
8885           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8886           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double) len));
8887         }
8888       }
8889       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8890       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8891     }
8892   }
8893   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8894 
8895   if (size > 1) {
8896     PetscMPIInt   blockLengths[2] = {4,1};
8897     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8898     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8899     MPI_Op        statReduce;
8900 
8901     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8902     PetscCallMPI(MPI_Type_commit(&statType));
8903     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8904     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8905     PetscCallMPI(MPI_Op_free(&statReduce));
8906     PetscCallMPI(MPI_Type_free(&statType));
8907   } else {
8908     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8909   }
8910   if (rank == 0) {
8911     count = globalStats.count;
8912     min   = globalStats.min;
8913     max   = globalStats.max;
8914     mean  = globalStats.sum / globalStats.count;
8915     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8916   }
8917 
8918   if (output) {
8919     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));
8920   }
8921   PetscCall(PetscFree2(J,invJ));
8922 
8923   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8924   if (dmCoarse) {
8925     PetscBool isplex;
8926 
8927     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8928     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8929   }
8930   PetscFunctionReturn(0);
8931 }
8932 
8933 /*@
8934   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8935   orthogonal quality below given tolerance.
8936 
8937   Collective on dm
8938 
8939   Input Parameters:
8940 + dm   - The DMPlex object
8941 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8942 - atol - [0, 1] Absolute tolerance for tagging cells.
8943 
8944   Output Parameters:
8945 + OrthQual      - Vec containing orthogonal quality per cell
8946 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8947 
8948   Options Database Keys:
8949 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8950 supported.
8951 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8952 
8953   Notes:
8954   Orthogonal quality is given by the following formula:
8955 
8956   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8957 
8958   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
8959   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8960   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8961   calculating the cosine of the angle between these vectors.
8962 
8963   Orthogonal quality ranges from 1 (best) to 0 (worst).
8964 
8965   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8966   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8967 
8968   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8969 
8970   Level: intermediate
8971 
8972 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8973 @*/
8974 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8975 {
8976   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8977   PetscInt                *idx;
8978   PetscScalar             *oqVals;
8979   const PetscScalar       *cellGeomArr, *faceGeomArr;
8980   PetscReal               *ci, *fi, *Ai;
8981   MPI_Comm                comm;
8982   Vec                     cellgeom, facegeom;
8983   DM                      dmFace, dmCell;
8984   IS                      glob;
8985   ISLocalToGlobalMapping  ltog;
8986   PetscViewer             vwr;
8987 
8988   PetscFunctionBegin;
8989   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8990   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
8991   PetscValidPointer(OrthQual, 4);
8992   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
8993   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
8994   PetscCall(DMGetDimension(dm, &nc));
8995   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
8996   {
8997     DMPlexInterpolatedFlag interpFlag;
8998 
8999     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9000     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9001       PetscMPIInt rank;
9002 
9003       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9004       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9005     }
9006   }
9007   if (OrthQualLabel) {
9008     PetscValidPointer(OrthQualLabel, 5);
9009     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9010     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9011   } else {*OrthQualLabel = NULL;}
9012   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9013   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9014   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9015   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9016   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9017   PetscCall(VecCreate(comm, OrthQual));
9018   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9019   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
9020   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9021   PetscCall(VecSetUp(*OrthQual));
9022   PetscCall(ISDestroy(&glob));
9023   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9024   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9025   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9026   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9027   PetscCall(VecGetDM(cellgeom, &dmCell));
9028   PetscCall(VecGetDM(facegeom, &dmFace));
9029   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9030   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
9031     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9032     PetscInt           cellarr[2], *adj = NULL;
9033     PetscScalar        *cArr, *fArr;
9034     PetscReal          minvalc = 1.0, minvalf = 1.0;
9035     PetscFVCellGeom    *cg;
9036 
9037     idx[cellIter] = cell-cStart;
9038     cellarr[0] = cell;
9039     /* Make indexing into cellGeom easier */
9040     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9041     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9042     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9043     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9044     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
9045       PetscInt         i;
9046       const PetscInt   neigh = adj[cellneigh];
9047       PetscReal        normci = 0, normfi = 0, normai = 0;
9048       PetscFVCellGeom  *cgneigh;
9049       PetscFVFaceGeom  *fg;
9050 
9051       /* Don't count ourselves in the neighbor list */
9052       if (neigh == cell) continue;
9053       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9054       cellarr[1] = neigh;
9055       {
9056         PetscInt       numcovpts;
9057         const PetscInt *covpts;
9058 
9059         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9060         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9061         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9062       }
9063 
9064       /* Compute c_i, f_i and their norms */
9065       for (i = 0; i < nc; i++) {
9066         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9067         fi[i] = fg->centroid[i] - cg->centroid[i];
9068         Ai[i] = fg->normal[i];
9069         normci += PetscPowReal(ci[i], 2);
9070         normfi += PetscPowReal(fi[i], 2);
9071         normai += PetscPowReal(Ai[i], 2);
9072       }
9073       normci = PetscSqrtReal(normci);
9074       normfi = PetscSqrtReal(normfi);
9075       normai = PetscSqrtReal(normai);
9076 
9077       /* Normalize and compute for each face-cell-normal pair */
9078       for (i = 0; i < nc; i++) {
9079         ci[i] = ci[i]/normci;
9080         fi[i] = fi[i]/normfi;
9081         Ai[i] = Ai[i]/normai;
9082         /* PetscAbs because I don't know if normals are guaranteed to point out */
9083         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9084         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9085       }
9086       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9087         minvalc = PetscRealPart(cArr[cellneighiter]);
9088       }
9089       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9090         minvalf = PetscRealPart(fArr[cellneighiter]);
9091       }
9092     }
9093     PetscCall(PetscFree(adj));
9094     PetscCall(PetscFree2(cArr, fArr));
9095     /* Defer to cell if they're equal */
9096     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9097     if (OrthQualLabel) {
9098       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9099     }
9100   }
9101   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9102   PetscCall(VecAssemblyBegin(*OrthQual));
9103   PetscCall(VecAssemblyEnd(*OrthQual));
9104   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9105   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9106   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9107   if (OrthQualLabel) {
9108     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9109   }
9110   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9111   PetscCall(PetscViewerDestroy(&vwr));
9112   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9113   PetscFunctionReturn(0);
9114 }
9115 
9116 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9117  * interpolator construction */
9118 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9119 {
9120   PetscSection   section, newSection, gsection;
9121   PetscSF        sf;
9122   PetscBool      hasConstraints, ghasConstraints;
9123 
9124   PetscFunctionBegin;
9125   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9126   PetscValidPointer(odm,2);
9127   PetscCall(DMGetLocalSection(dm, &section));
9128   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9129   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9130   if (!ghasConstraints) {
9131     PetscCall(PetscObjectReference((PetscObject)dm));
9132     *odm = dm;
9133     PetscFunctionReturn(0);
9134   }
9135   PetscCall(DMClone(dm, odm));
9136   PetscCall(DMCopyFields(dm, *odm));
9137   PetscCall(DMGetLocalSection(*odm, &newSection));
9138   PetscCall(DMGetPointSF(*odm, &sf));
9139   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9140   PetscCall(DMSetGlobalSection(*odm, gsection));
9141   PetscCall(PetscSectionDestroy(&gsection));
9142   PetscFunctionReturn(0);
9143 }
9144 
9145 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9146 {
9147   DM             dmco, dmfo;
9148   Mat            interpo;
9149   Vec            rscale;
9150   Vec            cglobalo, clocal;
9151   Vec            fglobal, fglobalo, flocal;
9152   PetscBool      regular;
9153 
9154   PetscFunctionBegin;
9155   PetscCall(DMGetFullDM(dmc, &dmco));
9156   PetscCall(DMGetFullDM(dmf, &dmfo));
9157   PetscCall(DMSetCoarseDM(dmfo, dmco));
9158   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9159   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9160   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9161   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9162   PetscCall(DMCreateLocalVector(dmc, &clocal));
9163   PetscCall(VecSet(cglobalo, 0.));
9164   PetscCall(VecSet(clocal, 0.));
9165   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9166   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9167   PetscCall(DMCreateLocalVector(dmf, &flocal));
9168   PetscCall(VecSet(fglobal, 0.));
9169   PetscCall(VecSet(fglobalo, 0.));
9170   PetscCall(VecSet(flocal, 0.));
9171   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9172   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9173   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9174   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9175   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9176   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9177   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9178   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9179   *shift = fglobal;
9180   PetscCall(VecDestroy(&flocal));
9181   PetscCall(VecDestroy(&fglobalo));
9182   PetscCall(VecDestroy(&clocal));
9183   PetscCall(VecDestroy(&cglobalo));
9184   PetscCall(VecDestroy(&rscale));
9185   PetscCall(MatDestroy(&interpo));
9186   PetscCall(DMDestroy(&dmfo));
9187   PetscCall(DMDestroy(&dmco));
9188   PetscFunctionReturn(0);
9189 }
9190 
9191 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9192 {
9193   PetscObject    shifto;
9194   Vec            shift;
9195 
9196   PetscFunctionBegin;
9197   if (!interp) {
9198     Vec rscale;
9199 
9200     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9201     PetscCall(VecDestroy(&rscale));
9202   } else {
9203     PetscCall(PetscObjectReference((PetscObject)interp));
9204   }
9205   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9206   if (!shifto) {
9207     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9208     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9209     shifto = (PetscObject) shift;
9210     PetscCall(VecDestroy(&shift));
9211   }
9212   shift = (Vec) shifto;
9213   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9214   PetscCall(VecAXPY(fineSol, 1.0, shift));
9215   PetscCall(MatDestroy(&interp));
9216   PetscFunctionReturn(0);
9217 }
9218 
9219 /* Pointwise interpolation
9220      Just code FEM for now
9221      u^f = I u^c
9222      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9223      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9224      I_{ij} = psi^f_i phi^c_j
9225 */
9226 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9227 {
9228   PetscSection   gsc, gsf;
9229   PetscInt       m, n;
9230   void          *ctx;
9231   DM             cdm;
9232   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9233 
9234   PetscFunctionBegin;
9235   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9236   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9237   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9238   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9239 
9240   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9241   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9242   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9243   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9244   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9245 
9246   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9247   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9248   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9249   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9250   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9251   if (scaling) {
9252     /* Use naive scaling */
9253     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9254   }
9255   PetscFunctionReturn(0);
9256 }
9257 
9258 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9259 {
9260   VecScatter     ctx;
9261 
9262   PetscFunctionBegin;
9263   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9264   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9265   PetscCall(VecScatterDestroy(&ctx));
9266   PetscFunctionReturn(0);
9267 }
9268 
9269 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9270                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9271                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9272                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9273 {
9274   const PetscInt Nc = uOff[1] - uOff[0];
9275   PetscInt       c;
9276   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9277 }
9278 
9279 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9280 {
9281   DM             dmc;
9282   PetscDS        ds;
9283   Vec            ones, locmass;
9284   IS             cellIS;
9285   PetscFormKey   key;
9286   PetscInt       depth;
9287 
9288   PetscFunctionBegin;
9289   PetscCall(DMClone(dm, &dmc));
9290   PetscCall(DMCopyDisc(dm, dmc));
9291   PetscCall(DMGetDS(dmc, &ds));
9292   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9293   PetscCall(DMCreateGlobalVector(dmc, mass));
9294   PetscCall(DMGetLocalVector(dmc, &ones));
9295   PetscCall(DMGetLocalVector(dmc, &locmass));
9296   PetscCall(DMPlexGetDepth(dmc, &depth));
9297   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9298   PetscCall(VecSet(locmass, 0.0));
9299   PetscCall(VecSet(ones, 1.0));
9300   key.label = NULL;
9301   key.value = 0;
9302   key.field = 0;
9303   key.part  = 0;
9304   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9305   PetscCall(ISDestroy(&cellIS));
9306   PetscCall(VecSet(*mass, 0.0));
9307   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9308   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9309   PetscCall(DMRestoreLocalVector(dmc, &ones));
9310   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9311   PetscCall(DMDestroy(&dmc));
9312   PetscFunctionReturn(0);
9313 }
9314 
9315 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9316 {
9317   PetscSection   gsc, gsf;
9318   PetscInt       m, n;
9319   void          *ctx;
9320   DM             cdm;
9321   PetscBool      regular;
9322 
9323   PetscFunctionBegin;
9324   if (dmFine == dmCoarse) {
9325     DM            dmc;
9326     PetscDS       ds;
9327     PetscWeakForm wf;
9328     Vec           u;
9329     IS            cellIS;
9330     PetscFormKey  key;
9331     PetscInt      depth;
9332 
9333     PetscCall(DMClone(dmFine, &dmc));
9334     PetscCall(DMCopyDisc(dmFine, dmc));
9335     PetscCall(DMGetDS(dmc, &ds));
9336     PetscCall(PetscDSGetWeakForm(ds, &wf));
9337     PetscCall(PetscWeakFormClear(wf));
9338     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9339     PetscCall(DMCreateMatrix(dmc, mass));
9340     PetscCall(DMGetGlobalVector(dmc, &u));
9341     PetscCall(DMPlexGetDepth(dmc, &depth));
9342     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9343     PetscCall(MatZeroEntries(*mass));
9344     key.label = NULL;
9345     key.value = 0;
9346     key.field = 0;
9347     key.part  = 0;
9348     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9349     PetscCall(ISDestroy(&cellIS));
9350     PetscCall(DMRestoreGlobalVector(dmc, &u));
9351     PetscCall(DMDestroy(&dmc));
9352   } else {
9353     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9354     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9355     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9356     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9357 
9358     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9359     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9360     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9361     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9362 
9363     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9364     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9365     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9366     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9367   }
9368   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9369   PetscFunctionReturn(0);
9370 }
9371 
9372 /*@
9373   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9374 
9375   Input Parameter:
9376 . dm - The DMPlex object
9377 
9378   Output Parameter:
9379 . regular - The flag
9380 
9381   Level: intermediate
9382 
9383 .seealso: `DMPlexSetRegularRefinement()`
9384 @*/
9385 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9386 {
9387   PetscFunctionBegin;
9388   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9389   PetscValidBoolPointer(regular, 2);
9390   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9391   PetscFunctionReturn(0);
9392 }
9393 
9394 /*@
9395   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9396 
9397   Input Parameters:
9398 + dm - The DMPlex object
9399 - regular - The flag
9400 
9401   Level: intermediate
9402 
9403 .seealso: `DMPlexGetRegularRefinement()`
9404 @*/
9405 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9406 {
9407   PetscFunctionBegin;
9408   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9409   ((DM_Plex *) dm->data)->regularRefinement = regular;
9410   PetscFunctionReturn(0);
9411 }
9412 
9413 /* anchors */
9414 /*@
9415   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9416   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9417 
9418   not collective
9419 
9420   Input Parameter:
9421 . dm - The DMPlex object
9422 
9423   Output Parameters:
9424 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9425 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9426 
9427   Level: intermediate
9428 
9429 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9430 @*/
9431 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9432 {
9433   DM_Plex *plex = (DM_Plex *)dm->data;
9434 
9435   PetscFunctionBegin;
9436   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9437   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9438   if (anchorSection) *anchorSection = plex->anchorSection;
9439   if (anchorIS) *anchorIS = plex->anchorIS;
9440   PetscFunctionReturn(0);
9441 }
9442 
9443 /*@
9444   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9445   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9446   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9447 
9448   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9449   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9450 
9451   collective on dm
9452 
9453   Input Parameters:
9454 + dm - The DMPlex object
9455 . 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).
9456 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9457 
9458   The reference counts of anchorSection and anchorIS are incremented.
9459 
9460   Level: intermediate
9461 
9462 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9463 @*/
9464 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9465 {
9466   DM_Plex        *plex = (DM_Plex *)dm->data;
9467   PetscMPIInt    result;
9468 
9469   PetscFunctionBegin;
9470   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9471   if (anchorSection) {
9472     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9473     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9474     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9475   }
9476   if (anchorIS) {
9477     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9478     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9479     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9480   }
9481 
9482   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9483   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9484   plex->anchorSection = anchorSection;
9485 
9486   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9487   PetscCall(ISDestroy(&plex->anchorIS));
9488   plex->anchorIS = anchorIS;
9489 
9490   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9491     PetscInt size, a, pStart, pEnd;
9492     const PetscInt *anchors;
9493 
9494     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9495     PetscCall(ISGetLocalSize(anchorIS,&size));
9496     PetscCall(ISGetIndices(anchorIS,&anchors));
9497     for (a = 0; a < size; a++) {
9498       PetscInt p;
9499 
9500       p = anchors[a];
9501       if (p >= pStart && p < pEnd) {
9502         PetscInt dof;
9503 
9504         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9505         if (dof) {
9506 
9507           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9508           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %" PetscInt_FMT " cannot be constrained and an anchor",p);
9509         }
9510       }
9511     }
9512     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9513   }
9514   /* reset the generic constraints */
9515   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9516   PetscFunctionReturn(0);
9517 }
9518 
9519 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9520 {
9521   PetscSection anchorSection;
9522   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9523 
9524   PetscFunctionBegin;
9525   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9526   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9527   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9528   PetscCall(PetscSectionGetNumFields(section,&numFields));
9529   if (numFields) {
9530     PetscInt f;
9531     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9532 
9533     for (f = 0; f < numFields; f++) {
9534       PetscInt numComp;
9535 
9536       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9537       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9538     }
9539   }
9540   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9541   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9542   pStart = PetscMax(pStart,sStart);
9543   pEnd   = PetscMin(pEnd,sEnd);
9544   pEnd   = PetscMax(pStart,pEnd);
9545   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9546   for (p = pStart; p < pEnd; p++) {
9547     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9548     if (dof) {
9549       PetscCall(PetscSectionGetDof(section,p,&dof));
9550       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9551       for (f = 0; f < numFields; f++) {
9552         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9553         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9554       }
9555     }
9556   }
9557   PetscCall(PetscSectionSetUp(*cSec));
9558   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9559   PetscFunctionReturn(0);
9560 }
9561 
9562 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9563 {
9564   PetscSection   aSec;
9565   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9566   const PetscInt *anchors;
9567   PetscInt       numFields, f;
9568   IS             aIS;
9569   MatType        mtype;
9570   PetscBool      iscuda,iskokkos;
9571 
9572   PetscFunctionBegin;
9573   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9574   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9575   PetscCall(PetscSectionGetStorageSize(section, &n));
9576   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9577   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9578   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9579   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9580   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9581   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9582   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9583   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9584   else mtype = MATSEQAIJ;
9585   PetscCall(MatSetType(*cMat,mtype));
9586   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9587   PetscCall(ISGetIndices(aIS,&anchors));
9588   /* cSec will be a subset of aSec and section */
9589   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9590   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9591   PetscCall(PetscMalloc1(m+1,&i));
9592   i[0] = 0;
9593   PetscCall(PetscSectionGetNumFields(section,&numFields));
9594   for (p = pStart; p < pEnd; p++) {
9595     PetscInt rDof, rOff, r;
9596 
9597     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9598     if (!rDof) continue;
9599     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9600     if (numFields) {
9601       for (f = 0; f < numFields; f++) {
9602         annz = 0;
9603         for (r = 0; r < rDof; r++) {
9604           a = anchors[rOff + r];
9605           if (a < sStart || a >= sEnd) continue;
9606           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9607           annz += aDof;
9608         }
9609         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9610         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9611         for (q = 0; q < dof; q++) {
9612           i[off + q + 1] = i[off + q] + annz;
9613         }
9614       }
9615     } else {
9616       annz = 0;
9617       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9618       for (q = 0; q < dof; q++) {
9619         a = anchors[rOff + q];
9620         if (a < sStart || a >= sEnd) continue;
9621         PetscCall(PetscSectionGetDof(section,a,&aDof));
9622         annz += aDof;
9623       }
9624       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9625       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9626       for (q = 0; q < dof; q++) {
9627         i[off + q + 1] = i[off + q] + annz;
9628       }
9629     }
9630   }
9631   nnz = i[m];
9632   PetscCall(PetscMalloc1(nnz,&j));
9633   offset = 0;
9634   for (p = pStart; p < pEnd; p++) {
9635     if (numFields) {
9636       for (f = 0; f < numFields; f++) {
9637         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9638         for (q = 0; q < dof; q++) {
9639           PetscInt rDof, rOff, r;
9640           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9641           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9642           for (r = 0; r < rDof; r++) {
9643             PetscInt s;
9644 
9645             a = anchors[rOff + r];
9646             if (a < sStart || a >= sEnd) continue;
9647             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9648             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9649             for (s = 0; s < aDof; s++) {
9650               j[offset++] = aOff + s;
9651             }
9652           }
9653         }
9654       }
9655     } else {
9656       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9657       for (q = 0; q < dof; q++) {
9658         PetscInt rDof, rOff, r;
9659         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9660         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9661         for (r = 0; r < rDof; r++) {
9662           PetscInt s;
9663 
9664           a = anchors[rOff + r];
9665           if (a < sStart || a >= sEnd) continue;
9666           PetscCall(PetscSectionGetDof(section,a,&aDof));
9667           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9668           for (s = 0; s < aDof; s++) {
9669             j[offset++] = aOff + s;
9670           }
9671         }
9672       }
9673     }
9674   }
9675   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9676   PetscCall(PetscFree(i));
9677   PetscCall(PetscFree(j));
9678   PetscCall(ISRestoreIndices(aIS,&anchors));
9679   PetscFunctionReturn(0);
9680 }
9681 
9682 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9683 {
9684   DM_Plex        *plex = (DM_Plex *)dm->data;
9685   PetscSection   anchorSection, section, cSec;
9686   Mat            cMat;
9687 
9688   PetscFunctionBegin;
9689   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9690   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9691   if (anchorSection) {
9692     PetscInt Nf;
9693 
9694     PetscCall(DMGetLocalSection(dm,&section));
9695     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9696     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9697     PetscCall(DMGetNumFields(dm,&Nf));
9698     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9699     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9700     PetscCall(PetscSectionDestroy(&cSec));
9701     PetscCall(MatDestroy(&cMat));
9702   }
9703   PetscFunctionReturn(0);
9704 }
9705 
9706 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9707 {
9708   IS             subis;
9709   PetscSection   section, subsection;
9710 
9711   PetscFunctionBegin;
9712   PetscCall(DMGetLocalSection(dm, &section));
9713   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9714   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9715   /* Create subdomain */
9716   PetscCall(DMPlexFilter(dm, label, value, subdm));
9717   /* Create submodel */
9718   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9719   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9720   PetscCall(DMSetLocalSection(*subdm, subsection));
9721   PetscCall(PetscSectionDestroy(&subsection));
9722   PetscCall(DMCopyDisc(dm, *subdm));
9723   /* Create map from submodel to global model */
9724   if (is) {
9725     PetscSection    sectionGlobal, subsectionGlobal;
9726     IS              spIS;
9727     const PetscInt *spmap;
9728     PetscInt       *subIndices;
9729     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9730     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9731 
9732     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9733     PetscCall(ISGetIndices(spIS, &spmap));
9734     PetscCall(PetscSectionGetNumFields(section, &Nf));
9735     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9736     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9737     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9738     for (p = pStart; p < pEnd; ++p) {
9739       PetscInt gdof, pSubSize  = 0;
9740 
9741       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9742       if (gdof > 0) {
9743         for (f = 0; f < Nf; ++f) {
9744           PetscInt fdof, fcdof;
9745 
9746           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9747           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9748           pSubSize += fdof-fcdof;
9749         }
9750         subSize += pSubSize;
9751         if (pSubSize) {
9752           if (bs < 0) {
9753             bs = pSubSize;
9754           } else if (bs != pSubSize) {
9755             /* Layout does not admit a pointwise block size */
9756             bs = 1;
9757           }
9758         }
9759       }
9760     }
9761     /* Must have same blocksize on all procs (some might have no points) */
9762     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9763     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9764     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9765     else                            {bs = bsMinMax[0];}
9766     PetscCall(PetscMalloc1(subSize, &subIndices));
9767     for (p = pStart; p < pEnd; ++p) {
9768       PetscInt gdof, goff;
9769 
9770       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9771       if (gdof > 0) {
9772         const PetscInt point = spmap[p];
9773 
9774         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9775         for (f = 0; f < Nf; ++f) {
9776           PetscInt fdof, fcdof, fc, f2, poff = 0;
9777 
9778           /* Can get rid of this loop by storing field information in the global section */
9779           for (f2 = 0; f2 < f; ++f2) {
9780             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9781             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9782             poff += fdof-fcdof;
9783           }
9784           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9785           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9786           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9787             subIndices[subOff] = goff+poff+fc;
9788           }
9789         }
9790       }
9791     }
9792     PetscCall(ISRestoreIndices(spIS, &spmap));
9793     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9794     if (bs > 1) {
9795       /* We need to check that the block size does not come from non-contiguous fields */
9796       PetscInt i, j, set = 1;
9797       for (i = 0; i < subSize; i += bs) {
9798         for (j = 0; j < bs; ++j) {
9799           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9800         }
9801       }
9802       if (set) PetscCall(ISSetBlockSize(*is, bs));
9803     }
9804     /* Attach nullspace */
9805     for (f = 0; f < Nf; ++f) {
9806       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9807       if ((*subdm)->nullspaceConstructors[f]) break;
9808     }
9809     if (f < Nf) {
9810       MatNullSpace nullSpace;
9811       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9812 
9813       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9814       PetscCall(MatNullSpaceDestroy(&nullSpace));
9815     }
9816   }
9817   PetscFunctionReturn(0);
9818 }
9819 
9820 /*@
9821   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9822 
9823   Input Parameter:
9824 - dm - The DM
9825 
9826   Level: developer
9827 
9828   Options Database Keys:
9829 . -dm_plex_monitor_throughput - Activate the monitor
9830 
9831 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9832 @*/
9833 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9834 {
9835 #if defined(PETSC_USE_LOG)
9836   PetscStageLog      stageLog;
9837   PetscLogEvent      event;
9838   PetscLogStage      stage;
9839   PetscEventPerfInfo eventInfo;
9840   PetscReal          cellRate, flopRate;
9841   PetscInt           cStart, cEnd, Nf, N;
9842   const char        *name;
9843 #endif
9844 
9845   PetscFunctionBegin;
9846   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9847 #if defined(PETSC_USE_LOG)
9848   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9849   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9850   PetscCall(DMGetNumFields(dm, &Nf));
9851   PetscCall(PetscLogGetStageLog(&stageLog));
9852   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9853   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9854   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9855   N        = (cEnd - cStart)*Nf*eventInfo.count;
9856   flopRate = eventInfo.flops/eventInfo.time;
9857   cellRate = N/eventInfo.time;
9858   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)));
9859 #else
9860   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9861 #endif
9862   PetscFunctionReturn(0);
9863 }
9864