xref: /petsc/src/dm/impls/plex/plex.c (revision 65c78980d86b286952aba5ac84149d487245284f)
1 #include <petsc/private/dmpleximpl.h>   /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF,DMPLEX_LocatePoints,DMPLEX_TopologyView,DMPLEX_LabelsView,DMPLEX_CoordinatesView,DMPLEX_SectionView,DMPLEX_GlobalVectorView,DMPLEX_LocalVectorView,DMPLEX_TopologyLoad,DMPLEX_LabelsLoad,DMPLEX_CoordinatesLoad,DMPLEX_SectionLoad,DMPLEX_GlobalVectorLoad,DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph,DMPLEX_RebalRewriteSF,DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm      - The DMPlex object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
28   If the mesh has no cells, this returns PETSC_FALSE.
29 
30   Level: intermediate
31 
32 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
33 @*/
34 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
35 {
36   DMPolytopeType ct;
37   PetscInt       cStart, cEnd;
38 
39   PetscFunctionBegin;
40   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
41   if (cEnd <= cStart) {*simplex = PETSC_FALSE; PetscFunctionReturn(0);}
42   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
43   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
44   PetscFunctionReturn(0);
45 }
46 
47 /*@
48   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
49 
50   Input Parameters:
51 + dm     - The DMPlex object
52 - height - The cell height in the Plex, 0 is the default
53 
54   Output Parameters:
55 + cStart - The first "normal" cell
56 - cEnd   - The upper bound on "normal"" cells
57 
58   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
59 
60   Level: developer
61 
62 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
63 @*/
64 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
65 {
66   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
67   PetscInt       cS, cE, c;
68 
69   PetscFunctionBegin;
70   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
71   for (c = cS; c < cE; ++c) {
72     DMPolytopeType cct;
73 
74     PetscCall(DMPlexGetCellType(dm, c, &cct));
75     if ((PetscInt) cct < 0) break;
76     switch (cct) {
77       case DM_POLYTOPE_POINT:
78       case DM_POLYTOPE_SEGMENT:
79       case DM_POLYTOPE_TRIANGLE:
80       case DM_POLYTOPE_QUADRILATERAL:
81       case DM_POLYTOPE_TETRAHEDRON:
82       case DM_POLYTOPE_HEXAHEDRON:
83         ct = cct;
84         break;
85       default: break;
86     }
87     if (ct != DM_POLYTOPE_UNKNOWN) break;
88   }
89   if (ct != DM_POLYTOPE_UNKNOWN) {
90     DMLabel ctLabel;
91 
92     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
93     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
94     // Reset label for fast lookup
95     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
96   }
97   if (cStart) *cStart = cS;
98   if (cEnd)   *cEnd   = cE;
99   PetscFunctionReturn(0);
100 }
101 
102 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
103 {
104   PetscInt       cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
105   PetscInt       vcdof[2] = {0,0}, globalvcdof[2];
106 
107   PetscFunctionBegin;
108   *ft  = PETSC_VTK_INVALID;
109   PetscCall(DMGetCoordinateDim(dm, &cdim));
110   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
111   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
112   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
113   if (field >= 0) {
114     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
115     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
116   } else {
117     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
118     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
119   }
120   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
121   if (globalvcdof[0]) {
122     *sStart = vStart;
123     *sEnd   = vEnd;
124     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
125     else                        *ft = PETSC_VTK_POINT_FIELD;
126   } else if (globalvcdof[1]) {
127     *sStart = cStart;
128     *sEnd   = cEnd;
129     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
130     else                        *ft = PETSC_VTK_CELL_FIELD;
131   } else {
132     if (field >= 0) {
133       const char *fieldname;
134 
135       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
136       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
137     } else {
138       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section\n"));
139     }
140   }
141   PetscFunctionReturn(0);
142 }
143 
144 /*@
145   DMPlexVecView1D - Plot many 1D solutions on the same line graph
146 
147   Collective on dm
148 
149   Input Parameters:
150 + dm - The DMPlex
151 . n  - The number of vectors
152 . u  - The array of local vectors
153 - viewer - The Draw viewer
154 
155   Level: advanced
156 
157 .seealso: `VecViewFromOptions()`, `VecView()`
158 @*/
159 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
160 {
161   PetscDS            ds;
162   PetscDraw          draw = NULL;
163   PetscDrawLG        lg;
164   Vec                coordinates;
165   const PetscScalar *coords, **sol;
166   PetscReal         *vals;
167   PetscInt          *Nc;
168   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
169   char             **names;
170 
171   PetscFunctionBegin;
172   PetscCall(DMGetDS(dm, &ds));
173   PetscCall(PetscDSGetNumFields(ds, &Nf));
174   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
175   PetscCall(PetscDSGetComponents(ds, &Nc));
176 
177   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
178   if (!draw) PetscFunctionReturn(0);
179   PetscCall(PetscDrawLGCreate(draw, n*Nl, &lg));
180 
181   PetscCall(PetscMalloc3(n, &sol, n*Nl, &names, n*Nl, &vals));
182   for (i = 0, l = 0; i < n; ++i) {
183     const char *vname;
184 
185     PetscCall(PetscObjectGetName((PetscObject) u[i], &vname));
186     for (f = 0; f < Nf; ++f) {
187       PetscObject disc;
188       const char *fname;
189       char        tmpname[PETSC_MAX_PATH_LEN];
190 
191       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
192       /* TODO Create names for components */
193       for (c = 0; c < Nc[f]; ++c, ++l) {
194         PetscCall(PetscObjectGetName(disc, &fname));
195         PetscCall(PetscStrcpy(tmpname, vname));
196         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
197         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
198         PetscCall(PetscStrallocpy(tmpname, &names[l]));
199       }
200     }
201   }
202   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *) names));
203   /* Just add P_1 support for now */
204   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
205   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
206   PetscCall(VecGetArrayRead(coordinates, &coords));
207   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
208   for (v = vStart; v < vEnd; ++v) {
209     PetscScalar *x, *svals;
210 
211     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
212     for (i = 0; i < n; ++i) {
213       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
214       for (l = 0; l < Nl; ++l) vals[i*Nl + l] = PetscRealPart(svals[l]);
215     }
216     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
217   }
218   PetscCall(VecRestoreArrayRead(coordinates, &coords));
219   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
220   for (l = 0; l < n*Nl; ++l) PetscCall(PetscFree(names[l]));
221   PetscCall(PetscFree3(sol, names, vals));
222 
223   PetscCall(PetscDrawLGDraw(lg));
224   PetscCall(PetscDrawLGDestroy(&lg));
225   PetscFunctionReturn(0);
226 }
227 
228 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
229 {
230   DM             dm;
231 
232   PetscFunctionBegin;
233   PetscCall(VecGetDM(u, &dm));
234   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
235   PetscFunctionReturn(0);
236 }
237 
238 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
239 {
240   DM                 dm;
241   PetscSection       s;
242   PetscDraw          draw, popup;
243   DM                 cdm;
244   PetscSection       coordSection;
245   Vec                coordinates;
246   const PetscScalar *coords, *array;
247   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
248   PetscReal          vbound[2], time;
249   PetscBool          flg;
250   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
251   const char        *name;
252   char               title[PETSC_MAX_PATH_LEN];
253 
254   PetscFunctionBegin;
255   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
256   PetscCall(VecGetDM(v, &dm));
257   PetscCall(DMGetCoordinateDim(dm, &dim));
258   PetscCall(DMGetLocalSection(dm, &s));
259   PetscCall(PetscSectionGetNumFields(s, &Nf));
260   PetscCall(DMGetCoarsenLevel(dm, &level));
261   PetscCall(DMGetCoordinateDM(dm, &cdm));
262   PetscCall(DMGetLocalSection(cdm, &coordSection));
263   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
264   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
265   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
266 
267   PetscCall(PetscObjectGetName((PetscObject) v, &name));
268   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
269 
270   PetscCall(VecGetLocalSize(coordinates, &N));
271   PetscCall(VecGetArrayRead(coordinates, &coords));
272   for (c = 0; c < N; c += dim) {
273     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
274     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
275   }
276   PetscCall(VecRestoreArrayRead(coordinates, &coords));
277   PetscCall(PetscDrawClear(draw));
278 
279   /* Could implement something like DMDASelectFields() */
280   for (f = 0; f < Nf; ++f) {
281     DM   fdm = dm;
282     Vec  fv  = v;
283     IS   fis;
284     char prefix[PETSC_MAX_PATH_LEN];
285     const char *fname;
286 
287     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
288     PetscCall(PetscSectionGetFieldName(s, f, &fname));
289 
290     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix,sizeof(prefix)));
291     else               {prefix[0] = '\0';}
292     if (Nf > 1) {
293       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
294       PetscCall(VecGetSubVector(v, fis, &fv));
295       PetscCall(PetscStrlcat(prefix, fname,sizeof(prefix)));
296       PetscCall(PetscStrlcat(prefix, "_",sizeof(prefix)));
297     }
298     for (comp = 0; comp < Nc; ++comp, ++w) {
299       PetscInt nmax = 2;
300 
301       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
302       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
303       else        PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
304       PetscCall(PetscDrawSetTitle(draw, title));
305 
306       /* TODO Get max and min only for this component */
307       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
308       if (!flg) {
309         PetscCall(VecMin(fv, NULL, &vbound[0]));
310         PetscCall(VecMax(fv, NULL, &vbound[1]));
311         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
312       }
313       PetscCall(PetscDrawGetPopup(draw, &popup));
314       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
315       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
316 
317       PetscCall(VecGetArrayRead(fv, &array));
318       for (c = cStart; c < cEnd; ++c) {
319         PetscScalar *coords = NULL, *a = NULL;
320         PetscInt     numCoords, color[4] = {-1,-1,-1,-1};
321 
322         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
323         if (a) {
324           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
325           color[1] = color[2] = color[3] = color[0];
326         } else {
327           PetscScalar *vals = NULL;
328           PetscInt     numVals, va;
329 
330           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
331           PetscCheck(numVals % Nc == 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
332           switch (numVals/Nc) {
333           case 3: /* P1 Triangle */
334           case 4: /* P1 Quadrangle */
335             for (va = 0; va < numVals/Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp]), vbound[0], vbound[1]);
336             break;
337           case 6: /* P2 Triangle */
338           case 8: /* P2 Quadrangle */
339             for (va = 0; va < numVals/(Nc*2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp + numVals/(Nc*2)]), vbound[0], vbound[1]);
340             break;
341           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals/Nc);
342           }
343           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
344         }
345         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
346         switch (numCoords) {
347         case 6:
348         case 12: /* Localized triangle */
349           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
350           break;
351         case 8:
352         case 16: /* Localized quadrilateral */
353           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
354           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
355           break;
356         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
357         }
358         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
359       }
360       PetscCall(VecRestoreArrayRead(fv, &array));
361       PetscCall(PetscDrawFlush(draw));
362       PetscCall(PetscDrawPause(draw));
363       PetscCall(PetscDrawSave(draw));
364     }
365     if (Nf > 1) {
366       PetscCall(VecRestoreSubVector(v, fis, &fv));
367       PetscCall(ISDestroy(&fis));
368       PetscCall(DMDestroy(&fdm));
369     }
370   }
371   PetscFunctionReturn(0);
372 }
373 
374 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
375 {
376   DM        dm;
377   PetscDraw draw;
378   PetscInt  dim;
379   PetscBool isnull;
380 
381   PetscFunctionBegin;
382   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
383   PetscCall(PetscDrawIsNull(draw, &isnull));
384   if (isnull) PetscFunctionReturn(0);
385 
386   PetscCall(VecGetDM(v, &dm));
387   PetscCall(DMGetCoordinateDim(dm, &dim));
388   switch (dim) {
389   case 1: PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));break;
390   case 2: PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));break;
391   default: SETERRQ(PetscObjectComm((PetscObject) v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
392   }
393   PetscFunctionReturn(0);
394 }
395 
396 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
397 {
398   DM                      dm;
399   Vec                     locv;
400   const char              *name;
401   PetscSection            section;
402   PetscInt                pStart, pEnd;
403   PetscInt                numFields;
404   PetscViewerVTKFieldType ft;
405 
406   PetscFunctionBegin;
407   PetscCall(VecGetDM(v, &dm));
408   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
409   PetscCall(PetscObjectGetName((PetscObject) v, &name));
410   PetscCall(PetscObjectSetName((PetscObject) locv, name));
411   PetscCall(VecCopy(v, locv));
412   PetscCall(DMGetLocalSection(dm, &section));
413   PetscCall(PetscSectionGetNumFields(section, &numFields));
414   if (!numFields) {
415     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
416     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE,(PetscObject) locv));
417   } else {
418     PetscInt f;
419 
420     for (f = 0; f < numFields; f++) {
421       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
422       if (ft == PETSC_VTK_INVALID) continue;
423       PetscCall(PetscObjectReference((PetscObject)locv));
424       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE,(PetscObject) locv));
425     }
426     PetscCall(VecDestroy(&locv));
427   }
428   PetscFunctionReturn(0);
429 }
430 
431 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
432 {
433   DM             dm;
434   PetscBool      isvtk, ishdf5, isdraw, isglvis, iscgns;
435 
436   PetscFunctionBegin;
437   PetscCall(VecGetDM(v, &dm));
438   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
439   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk));
440   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5));
441   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw));
442   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis));
443   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERCGNS,  &iscgns));
444   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
445     PetscInt    i,numFields;
446     PetscObject fe;
447     PetscBool   fem = PETSC_FALSE;
448     Vec         locv = v;
449     const char  *name;
450     PetscInt    step;
451     PetscReal   time;
452 
453     PetscCall(DMGetNumFields(dm, &numFields));
454     for (i=0; i<numFields; i++) {
455       PetscCall(DMGetField(dm, i, NULL, &fe));
456       if (fe->classid == PETSCFE_CLASSID) { fem = PETSC_TRUE; break; }
457     }
458     if (fem) {
459       PetscObject isZero;
460 
461       PetscCall(DMGetLocalVector(dm, &locv));
462       PetscCall(PetscObjectGetName((PetscObject) v, &name));
463       PetscCall(PetscObjectSetName((PetscObject) locv, name));
464       PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
465       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
466       PetscCall(VecCopy(v, locv));
467       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
468       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
469     }
470     if (isvtk) {
471       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
472     } else if (ishdf5) {
473 #if defined(PETSC_HAVE_HDF5)
474       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
475 #else
476       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
477 #endif
478     } else if (isdraw) {
479       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
480     } else if (isglvis) {
481       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
482       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
483       PetscCall(VecView_GLVis(locv, viewer));
484     } else if (iscgns) {
485 #if defined(PETSC_HAVE_CGNS)
486       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
487 #else
488       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
489 #endif
490     }
491     if (fem) {
492       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
493       PetscCall(DMRestoreLocalVector(dm, &locv));
494     }
495   } else {
496     PetscBool isseq;
497 
498     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
499     if (isseq) PetscCall(VecView_Seq(v, viewer));
500     else       PetscCall(VecView_MPI(v, viewer));
501   }
502   PetscFunctionReturn(0);
503 }
504 
505 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
506 {
507   DM        dm;
508   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
509 
510   PetscFunctionBegin;
511   PetscCall(VecGetDM(v, &dm));
512   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
513   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
514   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
515   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
516   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
517   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERCGNS,     &iscgns));
518   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
519   if (isvtk || isdraw || isglvis || iscgns) {
520     Vec         locv;
521     PetscObject isZero;
522     const char *name;
523 
524     PetscCall(DMGetLocalVector(dm, &locv));
525     PetscCall(PetscObjectGetName((PetscObject) v, &name));
526     PetscCall(PetscObjectSetName((PetscObject) locv, name));
527     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
528     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
529     PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
530     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
531     PetscCall(VecView_Plex_Local(locv, viewer));
532     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
533     PetscCall(DMRestoreLocalVector(dm, &locv));
534   } else if (ishdf5) {
535 #if defined(PETSC_HAVE_HDF5)
536     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
537 #else
538     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
539 #endif
540   } else if (isexodusii) {
541 #if defined(PETSC_HAVE_EXODUSII)
542     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
543 #else
544     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
545 #endif
546   } else {
547     PetscBool isseq;
548 
549     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
550     if (isseq) PetscCall(VecView_Seq(v, viewer));
551     else       PetscCall(VecView_MPI(v, viewer));
552   }
553   PetscFunctionReturn(0);
554 }
555 
556 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
557 {
558   DM                dm;
559   MPI_Comm          comm;
560   PetscViewerFormat format;
561   Vec               v;
562   PetscBool         isvtk, ishdf5;
563 
564   PetscFunctionBegin;
565   PetscCall(VecGetDM(originalv, &dm));
566   PetscCall(PetscObjectGetComm((PetscObject) originalv, &comm));
567   PetscCheck(dm,comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
568   PetscCall(PetscViewerGetFormat(viewer, &format));
569   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
570   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,  &isvtk));
571   if (format == PETSC_VIEWER_NATIVE) {
572     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
573     /* this need a better fix */
574     if (dm->useNatural) {
575       if (dm->sfNatural) {
576         const char *vecname;
577         PetscInt    n, nroots;
578 
579         PetscCall(VecGetLocalSize(originalv, &n));
580         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
581         if (n == nroots) {
582           PetscCall(DMGetGlobalVector(dm, &v));
583           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
584           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
585           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
586           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
587         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
588       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
589     } else v = originalv;
590   } else v = originalv;
591 
592   if (ishdf5) {
593 #if defined(PETSC_HAVE_HDF5)
594     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
595 #else
596     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
597 #endif
598   } else if (isvtk) {
599     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
600   } else {
601     PetscBool isseq;
602 
603     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
604     if (isseq) PetscCall(VecView_Seq(v, viewer));
605     else       PetscCall(VecView_MPI(v, viewer));
606   }
607   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
608   PetscFunctionReturn(0);
609 }
610 
611 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
612 {
613   DM             dm;
614   PetscBool      ishdf5;
615 
616   PetscFunctionBegin;
617   PetscCall(VecGetDM(v, &dm));
618   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
619   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
620   if (ishdf5) {
621     DM          dmBC;
622     Vec         gv;
623     const char *name;
624 
625     PetscCall(DMGetOutputDM(dm, &dmBC));
626     PetscCall(DMGetGlobalVector(dmBC, &gv));
627     PetscCall(PetscObjectGetName((PetscObject) v, &name));
628     PetscCall(PetscObjectSetName((PetscObject) gv, name));
629     PetscCall(VecLoad_Default(gv, viewer));
630     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
631     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
632     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
633   } else PetscCall(VecLoad_Default(v, viewer));
634   PetscFunctionReturn(0);
635 }
636 
637 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
638 {
639   DM             dm;
640   PetscBool      ishdf5,isexodusii;
641 
642   PetscFunctionBegin;
643   PetscCall(VecGetDM(v, &dm));
644   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
645   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
646   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
647   if (ishdf5) {
648 #if defined(PETSC_HAVE_HDF5)
649     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
650 #else
651     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
652 #endif
653   } else if (isexodusii) {
654 #if defined(PETSC_HAVE_EXODUSII)
655     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
656 #else
657     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
658 #endif
659   } else PetscCall(VecLoad_Default(v, viewer));
660   PetscFunctionReturn(0);
661 }
662 
663 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
664 {
665   DM                dm;
666   PetscViewerFormat format;
667   PetscBool         ishdf5;
668 
669   PetscFunctionBegin;
670   PetscCall(VecGetDM(originalv, &dm));
671   PetscCheck(dm,PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
672   PetscCall(PetscViewerGetFormat(viewer, &format));
673   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
674   if (format == PETSC_VIEWER_NATIVE) {
675     if (dm->useNatural) {
676       if (dm->sfNatural) {
677         if (ishdf5) {
678 #if defined(PETSC_HAVE_HDF5)
679           Vec         v;
680           const char *vecname;
681 
682           PetscCall(DMGetGlobalVector(dm, &v));
683           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
684           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
685           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
686           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
687           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
688           PetscCall(DMRestoreGlobalVector(dm, &v));
689 #else
690           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
691 #endif
692         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
693       }
694     } else PetscCall(VecLoad_Default(originalv, viewer));
695   }
696   PetscFunctionReturn(0);
697 }
698 
699 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
700 {
701   PetscSection       coordSection;
702   Vec                coordinates;
703   DMLabel            depthLabel, celltypeLabel;
704   const char        *name[4];
705   const PetscScalar *a;
706   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
707 
708   PetscFunctionBegin;
709   PetscCall(DMGetDimension(dm, &dim));
710   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
711   PetscCall(DMGetCoordinateSection(dm, &coordSection));
712   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
713   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
714   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
715   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
716   PetscCall(VecGetArrayRead(coordinates, &a));
717   name[0]     = "vertex";
718   name[1]     = "edge";
719   name[dim-1] = "face";
720   name[dim]   = "cell";
721   for (c = cStart; c < cEnd; ++c) {
722     PetscInt *closure = NULL;
723     PetscInt  closureSize, cl, ct;
724 
725     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
726     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
727     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
728     PetscCall(PetscViewerASCIIPushTab(viewer));
729     for (cl = 0; cl < closureSize*2; cl += 2) {
730       PetscInt point = closure[cl], depth, dof, off, d, p;
731 
732       if ((point < pStart) || (point >= pEnd)) continue;
733       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
734       if (!dof) continue;
735       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
736       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
737       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
738       for (p = 0; p < dof/dim; ++p) {
739         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
740         for (d = 0; d < dim; ++d) {
741           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
742           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d])));
743         }
744         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
745       }
746       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
747     }
748     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
749     PetscCall(PetscViewerASCIIPopTab(viewer));
750   }
751   PetscCall(VecRestoreArrayRead(coordinates, &a));
752   PetscFunctionReturn(0);
753 }
754 
755 typedef enum {CS_CARTESIAN, CS_POLAR, CS_CYLINDRICAL, CS_SPHERICAL} CoordSystem;
756 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
757 
758 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
759 {
760   PetscInt       i;
761 
762   PetscFunctionBegin;
763   if (dim > 3) {
764     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) PetscRealPart(x[i])));
765   } else {
766     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
767 
768     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
769     switch (cs) {
770       case CS_CARTESIAN: for (i = 0; i < dim; ++i) trcoords[i] = coords[i];break;
771       case CS_POLAR:
772         PetscCheck(dim == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
773         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
774         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
775         break;
776       case CS_CYLINDRICAL:
777         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
778         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
779         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
780         trcoords[2] = coords[2];
781         break;
782       case CS_SPHERICAL:
783         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
784         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
785         trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
786         trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
787         break;
788     }
789     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) trcoords[i]));
790   }
791   PetscFunctionReturn(0);
792 }
793 
794 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
795 {
796   DM_Plex          *mesh = (DM_Plex*) dm->data;
797   DM                cdm, cdmCell;
798   PetscSection      coordSection, coordSectionCell;
799   Vec               coordinates, coordinatesCell;
800   PetscViewerFormat format;
801 
802   PetscFunctionBegin;
803   PetscCall(DMGetCoordinateDM(dm, &cdm));
804   PetscCall(DMGetCoordinateSection(dm, &coordSection));
805   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
806   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
807   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
808   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
809   PetscCall(PetscViewerGetFormat(viewer, &format));
810   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
811     const char *name;
812     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
813     PetscInt    pStart, pEnd, p, numLabels, l;
814     PetscMPIInt rank, size;
815 
816     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
817     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
818     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
819     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
820     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
821     PetscCall(DMGetDimension(dm, &dim));
822     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
823     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
824     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
825     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
826     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
827     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
828     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
829     for (p = pStart; p < pEnd; ++p) {
830       PetscInt dof, off, s;
831 
832       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
833       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
834       for (s = off; s < off+dof; ++s) {
835         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
836       }
837     }
838     PetscCall(PetscViewerFlush(viewer));
839     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
840     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
841     for (p = pStart; p < pEnd; ++p) {
842       PetscInt dof, off, c;
843 
844       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
845       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
846       for (c = off; c < off+dof; ++c) {
847         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
848       }
849     }
850     PetscCall(PetscViewerFlush(viewer));
851     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
852     if (coordSection && coordinates) {
853       CoordSystem        cs = CS_CARTESIAN;
854       const PetscScalar *array, *arrayCell = NULL;
855       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
856       PetscMPIInt        rank;
857       const char        *name;
858 
859       PetscCall(PetscOptionsGetEnum(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *) &cs, NULL));
860       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
861       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
862       PetscCheck(Nf == 1,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
863       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
864       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
865       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
866       pStart =  PetscMin(pvStart, pcStart);
867       pEnd   =  PetscMax(pvEnd,   pcEnd);
868       PetscCall(PetscObjectGetName((PetscObject) coordinates, &name));
869       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
870       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
871       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
872 
873       PetscCall(VecGetArrayRead(coordinates, &array));
874       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
875       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
876       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
877       for (p = pStart; p < pEnd; ++p) {
878         PetscInt dof, off;
879 
880         if (p >= pvStart && p < pvEnd) {
881           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
882           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
883           if (dof) {
884             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
885             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
886             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
887           }
888         }
889         if (cdmCell && p >= pcStart && p < pcEnd) {
890           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
891           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
892           if (dof) {
893             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
894             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
895             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
896           }
897         }
898       }
899       PetscCall(PetscViewerFlush(viewer));
900       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
901       PetscCall(VecRestoreArrayRead(coordinates, &array));
902       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
903     }
904     PetscCall(DMGetNumLabels(dm, &numLabels));
905     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
906     for (l = 0; l < numLabels; ++l) {
907       DMLabel     label;
908       PetscBool   isdepth;
909       const char *name;
910 
911       PetscCall(DMGetLabelName(dm, l, &name));
912       PetscCall(PetscStrcmp(name, "depth", &isdepth));
913       if (isdepth) continue;
914       PetscCall(DMGetLabel(dm, name, &label));
915       PetscCall(DMLabelView(label, viewer));
916     }
917     if (size > 1) {
918       PetscSF sf;
919 
920       PetscCall(DMGetPointSF(dm, &sf));
921       PetscCall(PetscSFView(sf, viewer));
922     }
923     PetscCall(PetscViewerFlush(viewer));
924   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
925     const char  *name, *color;
926     const char  *defcolors[3]  = {"gray", "orange", "green"};
927     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
928     char         lname[PETSC_MAX_PATH_LEN];
929     PetscReal    scale         = 2.0;
930     PetscReal    tikzscale     = 1.0;
931     PetscBool    useNumbers    = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
932     double       tcoords[3];
933     PetscScalar *coords;
934     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
935     PetscMPIInt  rank, size;
936     char         **names, **colors, **lcolors;
937     PetscBool    flg, lflg;
938     PetscBT      wp = NULL;
939     PetscInt     pEnd, pStart;
940 
941     PetscCall(DMGetDimension(dm, &dim));
942     PetscCall(DMPlexGetDepth(dm, &depth));
943     PetscCall(DMGetNumLabels(dm, &numLabels));
944     numLabels  = PetscMax(numLabels, 10);
945     numColors  = 10;
946     numLColors = 10;
947     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
948     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
949     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
950     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
951     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
952     for (d = 0; d < 4; ++d) drawColors[d]  = PETSC_TRUE;
953     n = 4;
954     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
955     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
956     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
957     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
958     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
959     if (!useLabels) numLabels = 0;
960     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
961     if (!useColors) {
962       numColors = 3;
963       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
964     }
965     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
966     if (!useColors) {
967       numLColors = 4;
968       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
969     }
970     PetscCall(PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
971     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
972     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
973     PetscCheck(!flg || !plotEdges || depth >= dim,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
974     if (depth < dim) plotEdges = PETSC_FALSE;
975     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
976 
977     /* filter points with labelvalue != labeldefaultvalue */
978     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
979     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
980     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
981     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
982     if (lflg) {
983       DMLabel lbl;
984 
985       PetscCall(DMGetLabel(dm, lname, &lbl));
986       if (lbl) {
987         PetscInt val, defval;
988 
989         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
990         PetscCall(PetscBTCreate(pEnd-pStart, &wp));
991         for (c = pStart;  c < pEnd; c++) {
992           PetscInt *closure = NULL;
993           PetscInt  closureSize;
994 
995           PetscCall(DMLabelGetValue(lbl, c, &val));
996           if (val == defval) continue;
997 
998           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
999           for (p = 0; p < closureSize*2; p += 2) {
1000             PetscCall(PetscBTSet(wp, closure[p] - pStart));
1001           }
1002           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1003         }
1004       }
1005     }
1006 
1007     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1008     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1009     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1010     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1011 \\documentclass[tikz]{standalone}\n\n\
1012 \\usepackage{pgflibraryshapes}\n\
1013 \\usetikzlibrary{backgrounds}\n\
1014 \\usetikzlibrary{arrows}\n\
1015 \\begin{document}\n"));
1016     if (size > 1) {
1017       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1018       for (p = 0; p < size; ++p) {
1019         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size-1) ? ", and " :  ", "));
1020         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p%numColors], p));
1021       }
1022       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1023     }
1024     if (drawHasse) {
1025       PetscInt maxStratum = PetscMax(vEnd-vStart, PetscMax(eEnd-eStart, cEnd-cStart));
1026 
1027       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1028       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd-1));
1029       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd-vStart));
1030       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum-(vEnd-vStart))/2.));
1031       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1032       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd-1));
1033       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum-(eEnd-eStart))/2.));
1034       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd-eStart));
1035       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1036       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd-1));
1037       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd-cStart));
1038       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum-(cEnd-cStart))/2.));
1039     }
1040     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale));
1041 
1042     /* Plot vertices */
1043     PetscCall(VecGetArray(coordinates, &coords));
1044     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1045     for (v = vStart; v < vEnd; ++v) {
1046       PetscInt  off, dof, d;
1047       PetscBool isLabeled = PETSC_FALSE;
1048 
1049       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
1050       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1051       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1052       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1053       PetscCheck(dof <= 3,PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3",v,dof);
1054       for (d = 0; d < dof; ++d) {
1055         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1056         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1057       }
1058       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1059       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1060       for (d = 0; d < dof; ++d) {
1061         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1062         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]));
1063       }
1064       if (drawHasse) color = colors[0%numColors];
1065       else           color = colors[rank%numColors];
1066       for (l = 0; l < numLabels; ++l) {
1067         PetscInt val;
1068         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1069         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1070       }
1071       if (drawNumbers[0]) {
1072         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1073       } else if (drawColors[0]) {
1074         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1075       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1076     }
1077     PetscCall(VecRestoreArray(coordinates, &coords));
1078     PetscCall(PetscViewerFlush(viewer));
1079     /* Plot edges */
1080     if (plotEdges) {
1081       PetscCall(VecGetArray(coordinates, &coords));
1082       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1083       for (e = eStart; e < eEnd; ++e) {
1084         const PetscInt *cone;
1085         PetscInt        coneSize, offA, offB, dof, d;
1086 
1087         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1088         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1089         PetscCheck(coneSize == 2,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1090         PetscCall(DMPlexGetCone(dm, e, &cone));
1091         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1092         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1093         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1094         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1095         for (d = 0; d < dof; ++d) {
1096           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
1097           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1098         }
1099         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1100         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1101         for (d = 0; d < dof; ++d) {
1102           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1103           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1104         }
1105         if (drawHasse) color = colors[1%numColors];
1106         else           color = colors[rank%numColors];
1107         for (l = 0; l < numLabels; ++l) {
1108           PetscInt val;
1109           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1110           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1111         }
1112         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1113       }
1114       PetscCall(VecRestoreArray(coordinates, &coords));
1115       PetscCall(PetscViewerFlush(viewer));
1116       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1117     }
1118     /* Plot cells */
1119     if (dim == 3 || !drawNumbers[1]) {
1120       for (e = eStart; e < eEnd; ++e) {
1121         const PetscInt *cone;
1122 
1123         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1124         color = colors[rank%numColors];
1125         for (l = 0; l < numLabels; ++l) {
1126           PetscInt val;
1127           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1128           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1129         }
1130         PetscCall(DMPlexGetCone(dm, e, &cone));
1131         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1132       }
1133     } else {
1134        DMPolytopeType ct;
1135 
1136       /* Drawing a 2D polygon */
1137       for (c = cStart; c < cEnd; ++c) {
1138         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1139         PetscCall(DMPlexGetCellType(dm, c, &ct));
1140         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR ||
1141             ct == DM_POLYTOPE_TRI_PRISM_TENSOR ||
1142             ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1143           const PetscInt *cone;
1144           PetscInt        coneSize, e;
1145 
1146           PetscCall(DMPlexGetCone(dm, c, &cone));
1147           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1148           for (e = 0; e < coneSize; ++e) {
1149             const PetscInt *econe;
1150 
1151             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1152             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank%numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1153           }
1154         } else {
1155           PetscInt *closure = NULL;
1156           PetscInt  closureSize, Nv = 0, v;
1157 
1158           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1159           for (p = 0; p < closureSize*2; p += 2) {
1160             const PetscInt point = closure[p];
1161 
1162             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1163           }
1164           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]));
1165           for (v = 0; v <= Nv; ++v) {
1166             const PetscInt vertex = closure[v%Nv];
1167 
1168             if (v > 0) {
1169               if (plotEdges) {
1170                 const PetscInt *edge;
1171                 PetscInt        endpoints[2], ne;
1172 
1173                 endpoints[0] = closure[v-1]; endpoints[1] = vertex;
1174                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1175                 PetscCheck(ne == 1,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1176                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1177                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1178               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1179             }
1180             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1181           }
1182           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1183           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1184         }
1185       }
1186     }
1187     for (c = cStart; c < cEnd; ++c) {
1188       double             ccoords[3] = {0.0, 0.0, 0.0};
1189       PetscBool          isLabeled  = PETSC_FALSE;
1190       PetscScalar       *cellCoords = NULL;
1191       const PetscScalar *array;
1192       PetscInt           numCoords, cdim, d;
1193       PetscBool          isDG;
1194 
1195       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
1196       PetscCall(DMGetCoordinateDim(dm, &cdim));
1197       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1198       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1199       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1200       for (p = 0; p < numCoords/cdim; ++p) {
1201         for (d = 0; d < cdim; ++d) {
1202           tcoords[d] = (double) (scale*PetscRealPart(cellCoords[p*cdim+d]));
1203           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1204         }
1205         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1206         if (cdim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1207         for (d = 0; d < dim; ++d) {ccoords[d] += tcoords[d];}
1208       }
1209       for (d = 0; d < cdim; ++d) {ccoords[d] /= (numCoords/cdim);}
1210       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1211       for (d = 0; d < cdim; ++d) {
1212         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1213         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]));
1214       }
1215       if (drawHasse) color = colors[depth%numColors];
1216       else           color = colors[rank%numColors];
1217       for (l = 0; l < numLabels; ++l) {
1218         PetscInt val;
1219         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1220         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1221       }
1222       if (drawNumbers[dim]) {
1223         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1224       } else if (drawColors[dim]) {
1225         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1226       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1227     }
1228     if (drawHasse) {
1229       color = colors[depth%numColors];
1230       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1231       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1232       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1233       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1234       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1235 
1236       color = colors[1%numColors];
1237       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1238       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1239       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1241       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1242 
1243       color = colors[0%numColors];
1244       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1245       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1246       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1247       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1248       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1249 
1250       for (p = pStart; p < pEnd; ++p) {
1251         const PetscInt *cone;
1252         PetscInt        coneSize, cp;
1253 
1254         PetscCall(DMPlexGetCone(dm, p, &cone));
1255         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1256         for (cp = 0; cp < coneSize; ++cp) {
1257           PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1258         }
1259       }
1260     }
1261     PetscCall(PetscViewerFlush(viewer));
1262     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1263     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1264     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1265     for (l = 0; l < numLabels;  ++l) PetscCall(PetscFree(names[l]));
1266     for (c = 0; c < numColors;  ++c) PetscCall(PetscFree(colors[c]));
1267     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1268     PetscCall(PetscFree3(names, colors, lcolors));
1269     PetscCall(PetscBTDestroy(&wp));
1270   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1271     Vec                    cown,acown;
1272     VecScatter             sct;
1273     ISLocalToGlobalMapping g2l;
1274     IS                     gid,acis;
1275     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
1276     MPI_Group              ggroup,ngroup;
1277     PetscScalar            *array,nid;
1278     const PetscInt         *idxs;
1279     PetscInt               *idxs2,*start,*adjacency,*work;
1280     PetscInt64             lm[3],gm[3];
1281     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
1282     PetscMPIInt            d1,d2,rank;
1283 
1284     PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
1285     PetscCallMPI(MPI_Comm_rank(comm,&rank));
1286 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1287     PetscCallMPI(MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm));
1288 #endif
1289     if (ncomm != MPI_COMM_NULL) {
1290       PetscCallMPI(MPI_Comm_group(comm,&ggroup));
1291       PetscCallMPI(MPI_Comm_group(ncomm,&ngroup));
1292       d1   = 0;
1293       PetscCallMPI(MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2));
1294       nid  = d2;
1295       PetscCallMPI(MPI_Group_free(&ggroup));
1296       PetscCallMPI(MPI_Group_free(&ngroup));
1297       PetscCallMPI(MPI_Comm_free(&ncomm));
1298     } else nid = 0.0;
1299 
1300     /* Get connectivity */
1301     PetscCall(DMPlexGetVTKCellHeight(dm,&cellHeight));
1302     PetscCall(DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid));
1303 
1304     /* filter overlapped local cells */
1305     PetscCall(DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd));
1306     PetscCall(ISGetIndices(gid,&idxs));
1307     PetscCall(ISGetLocalSize(gid,&cum));
1308     PetscCall(PetscMalloc1(cum,&idxs2));
1309     for (c = cStart, cum = 0; c < cEnd; c++) {
1310       if (idxs[c-cStart] < 0) continue;
1311       idxs2[cum++] = idxs[c-cStart];
1312     }
1313     PetscCall(ISRestoreIndices(gid,&idxs));
1314     PetscCheck(numVertices == cum,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %" PetscInt_FMT " != %" PetscInt_FMT,numVertices,cum);
1315     PetscCall(ISDestroy(&gid));
1316     PetscCall(ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid));
1317 
1318     /* support for node-aware cell locality */
1319     PetscCall(ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis));
1320     PetscCall(VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown));
1321     PetscCall(VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown));
1322     PetscCall(VecGetArray(cown,&array));
1323     for (c = 0; c < numVertices; c++) array[c] = nid;
1324     PetscCall(VecRestoreArray(cown,&array));
1325     PetscCall(VecScatterCreate(cown,acis,acown,NULL,&sct));
1326     PetscCall(VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1327     PetscCall(VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1328     PetscCall(ISDestroy(&acis));
1329     PetscCall(VecScatterDestroy(&sct));
1330     PetscCall(VecDestroy(&cown));
1331 
1332     /* compute edgeCut */
1333     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
1334     PetscCall(PetscMalloc1(cum,&work));
1335     PetscCall(ISLocalToGlobalMappingCreateIS(gid,&g2l));
1336     PetscCall(ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH));
1337     PetscCall(ISDestroy(&gid));
1338     PetscCall(VecGetArray(acown,&array));
1339     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1340       PetscInt totl;
1341 
1342       totl = start[c+1]-start[c];
1343       PetscCall(ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work));
1344       for (i = 0; i < totl; i++) {
1345         if (work[i] < 0) {
1346           ect  += 1;
1347           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1348         }
1349       }
1350     }
1351     PetscCall(PetscFree(work));
1352     PetscCall(VecRestoreArray(acown,&array));
1353     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1354     lm[1] = -numVertices;
1355     PetscCall(MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm));
1356     PetscCall(PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT,-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]));
1357     lm[0] = ect; /* edgeCut */
1358     lm[1] = ectn; /* node-aware edgeCut */
1359     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1360     PetscCall(MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm));
1361     PetscCall(PetscViewerASCIIPrintf(viewer,", empty %" PetscInt_FMT ")\n",(PetscInt)gm[2]));
1362 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1363     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),gm[0] ? ((double)(gm[1]))/((double)gm[0]) : 1.));
1364 #else
1365     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0));
1366 #endif
1367     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1368     PetscCall(PetscFree(start));
1369     PetscCall(PetscFree(adjacency));
1370     PetscCall(VecDestroy(&acown));
1371   } else {
1372     const char    *name;
1373     PetscInt      *sizes, *hybsizes, *ghostsizes;
1374     PetscInt       locDepth, depth, cellHeight, dim, d;
1375     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1376     PetscInt       numLabels, l, maxSize = 17;
1377     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1378     MPI_Comm       comm;
1379     PetscMPIInt    size, rank;
1380 
1381     PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
1382     PetscCallMPI(MPI_Comm_size(comm, &size));
1383     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1384     PetscCall(DMGetDimension(dm, &dim));
1385     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1386     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1387     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1388     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1389     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1390     PetscCall(DMPlexGetDepth(dm, &locDepth));
1391     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1392     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1393     gcNum = gcEnd - gcStart;
1394     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1395     else                PetscCall(PetscCalloc3(3,    &sizes, 3,    &hybsizes, 3,    &ghostsizes));
1396     for (d = 0; d <= depth; d++) {
1397       PetscInt Nc[2] = {0, 0}, ict;
1398 
1399       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1400       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1401       ict  = ct0;
1402       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1403       ct0  = (DMPolytopeType) ict;
1404       for (p = pStart; p < pEnd; ++p) {
1405         DMPolytopeType ct;
1406 
1407         PetscCall(DMPlexGetCellType(dm, p, &ct));
1408         if (ct == ct0) ++Nc[0];
1409         else           ++Nc[1];
1410       }
1411       if (size < maxSize) {
1412         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm));
1413         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1414         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1415         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1416         for (p = 0; p < size; ++p) {
1417           if (rank == 0) {
1418             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p]+hybsizes[p]));
1419             if (hybsizes[p]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1420             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1421           }
1422         }
1423       } else {
1424         PetscInt locMinMax[2];
1425 
1426         locMinMax[0] = Nc[0]+Nc[1]; locMinMax[1] = Nc[0]+Nc[1];
1427         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1428         locMinMax[0] = Nc[1]; locMinMax[1] = Nc[1];
1429         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1430         if (d == depth) {
1431           locMinMax[0] = gcNum; locMinMax[1] = gcNum;
1432           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1433         }
1434         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1435         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1436         if (hybsizes[0]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1437         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1438       }
1439       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1440     }
1441     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1442     {
1443       const PetscReal *maxCell;
1444       const PetscReal *L;
1445       PetscBool        localized;
1446 
1447       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1448       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1449       if (L || localized) {
1450         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1451         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1452         if (L) {
1453           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1454           for (d = 0; d < dim; ++d) {
1455             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1456             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1457           }
1458           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1459         }
1460         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1461         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1462       }
1463     }
1464     PetscCall(DMGetNumLabels(dm, &numLabels));
1465     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1466     for (l = 0; l < numLabels; ++l) {
1467       DMLabel         label;
1468       const char     *name;
1469       IS              valueIS;
1470       const PetscInt *values;
1471       PetscInt        numValues, v;
1472 
1473       PetscCall(DMGetLabelName(dm, l, &name));
1474       PetscCall(DMGetLabel(dm, name, &label));
1475       PetscCall(DMLabelGetNumValues(label, &numValues));
1476       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1477       PetscCall(DMLabelGetValueIS(label, &valueIS));
1478       PetscCall(ISGetIndices(valueIS, &values));
1479       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1480       for (v = 0; v < numValues; ++v) {
1481         PetscInt size;
1482 
1483         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1484         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1485         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1486       }
1487       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1488       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1489       PetscCall(ISRestoreIndices(valueIS, &values));
1490       PetscCall(ISDestroy(&valueIS));
1491     }
1492     {
1493       char    **labelNames;
1494       PetscInt  Nl = numLabels;
1495       PetscBool flg;
1496 
1497       PetscCall(PetscMalloc1(Nl, &labelNames));
1498       PetscCall(PetscOptionsGetStringArray(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1499       for (l = 0; l < Nl; ++l) {
1500         DMLabel label;
1501 
1502         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1503         if (flg) {
1504           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1505           PetscCall(DMLabelView(label, viewer));
1506         }
1507         PetscCall(PetscFree(labelNames[l]));
1508       }
1509       PetscCall(PetscFree(labelNames));
1510     }
1511     /* If no fields are specified, people do not want to see adjacency */
1512     if (dm->Nf) {
1513       PetscInt f;
1514 
1515       for (f = 0; f < dm->Nf; ++f) {
1516         const char *name;
1517 
1518         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1519         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1520         PetscCall(PetscViewerASCIIPushTab(viewer));
1521         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1522         if (dm->fields[f].adjacency[0]) {
1523           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1524           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1525         } else {
1526           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1527           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1528         }
1529         PetscCall(PetscViewerASCIIPopTab(viewer));
1530       }
1531     }
1532     PetscCall(DMGetCoarseDM(dm, &cdm));
1533     if (cdm) {
1534       PetscCall(PetscViewerASCIIPushTab(viewer));
1535       PetscCall(DMPlexView_Ascii(cdm, viewer));
1536       PetscCall(PetscViewerASCIIPopTab(viewer));
1537     }
1538   }
1539   PetscFunctionReturn(0);
1540 }
1541 
1542 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1543 {
1544   DMPolytopeType ct;
1545   PetscMPIInt    rank;
1546   PetscInt       cdim;
1547 
1548   PetscFunctionBegin;
1549   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1550   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1551   PetscCall(DMGetCoordinateDim(dm, &cdim));
1552   switch (ct) {
1553   case DM_POLYTOPE_SEGMENT:
1554   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1555     switch (cdim) {
1556     case 1:
1557     {
1558       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1559       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1560 
1561       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y,    PetscRealPart(coords[1]), y,    PETSC_DRAW_BLACK));
1562       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y+dy, PetscRealPart(coords[0]), y-dy, PETSC_DRAW_BLACK));
1563       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y+dy, PetscRealPart(coords[1]), y-dy, PETSC_DRAW_BLACK));
1564     }
1565     break;
1566     case 2:
1567     {
1568       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1569       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1570       const PetscReal l  = 0.1/PetscSqrtReal(dx*dx + dy*dy);
1571 
1572       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1573       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0])+l*dx, PetscRealPart(coords[1])+l*dy, PetscRealPart(coords[0])-l*dx, PetscRealPart(coords[1])-l*dy, PETSC_DRAW_BLACK));
1574       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2])+l*dx, PetscRealPart(coords[3])+l*dy, PetscRealPart(coords[2])-l*dx, PetscRealPart(coords[3])-l*dy, PETSC_DRAW_BLACK));
1575     }
1576     break;
1577     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1578     }
1579     break;
1580   case DM_POLYTOPE_TRIANGLE:
1581     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1582                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1583                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1584                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1585     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1586     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1587     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1588     break;
1589   case DM_POLYTOPE_QUADRILATERAL:
1590     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1591                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1592                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1593                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1594     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1595                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1596                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1597                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1598     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1599     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1600     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1601     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1602     break;
1603   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1604   }
1605   PetscFunctionReturn(0);
1606 }
1607 
1608 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1609 {
1610   DMPolytopeType ct;
1611   PetscReal      centroid[2] = {0., 0.};
1612   PetscMPIInt    rank;
1613   PetscInt       fillColor, v, e, d;
1614 
1615   PetscFunctionBegin;
1616   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1617   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1618   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2;
1619   switch (ct) {
1620   case DM_POLYTOPE_TRIANGLE:
1621     {
1622       PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1623 
1624       for (v = 0; v < 3; ++v) {centroid[0] += PetscRealPart(coords[v*2+0])/3.;centroid[1] += PetscRealPart(coords[v*2+1])/3.;}
1625       for (e = 0; e < 3; ++e) {
1626         refCoords[0] = refVertices[e*2+0];
1627         refCoords[1] = refVertices[e*2+1];
1628         for (d = 1; d <= edgeDiv; ++d) {
1629           refCoords[d*2+0] = refCoords[0] + (refVertices[(e+1)%3 * 2 + 0] - refCoords[0])*d/edgeDiv;
1630           refCoords[d*2+1] = refCoords[1] + (refVertices[(e+1)%3 * 2 + 1] - refCoords[1])*d/edgeDiv;
1631         }
1632         PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv+1, refCoords, edgeCoords));
1633         for (d = 0; d < edgeDiv; ++d) {
1634           PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], fillColor, fillColor, fillColor));
1635           PetscCall(PetscDrawLine(draw, edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], PETSC_DRAW_BLACK));
1636         }
1637       }
1638     }
1639     break;
1640   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1641   }
1642   PetscFunctionReturn(0);
1643 }
1644 
1645 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1646 {
1647   PetscDraw          draw;
1648   DM                 cdm;
1649   PetscSection       coordSection;
1650   Vec                coordinates;
1651   const PetscScalar *coords;
1652   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1653   PetscReal         *refCoords, *edgeCoords;
1654   PetscBool          isnull, drawAffine = PETSC_TRUE;
1655   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1656 
1657   PetscFunctionBegin;
1658   PetscCall(DMGetCoordinateDim(dm, &dim));
1659   PetscCheck(dim <= 2,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1660   PetscCall(PetscOptionsGetBool(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1661   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv+1)*dim, &refCoords, (edgeDiv+1)*dim, &edgeCoords));
1662   PetscCall(DMGetCoordinateDM(dm, &cdm));
1663   PetscCall(DMGetLocalSection(cdm, &coordSection));
1664   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1665   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1666   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1667 
1668   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1669   PetscCall(PetscDrawIsNull(draw, &isnull));
1670   if (isnull) PetscFunctionReturn(0);
1671   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1672 
1673   PetscCall(VecGetLocalSize(coordinates, &N));
1674   PetscCall(VecGetArrayRead(coordinates, &coords));
1675   for (c = 0; c < N; c += dim) {
1676     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1677     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1678   }
1679   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1680   PetscCall(MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm)));
1681   PetscCall(MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm)));
1682   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1683   PetscCall(PetscDrawClear(draw));
1684 
1685   for (c = cStart; c < cEnd; ++c) {
1686     PetscScalar *coords = NULL;
1687     PetscInt     numCoords;
1688 
1689     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1690     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1691     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1692     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1693   }
1694   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1695   PetscCall(PetscDrawFlush(draw));
1696   PetscCall(PetscDrawPause(draw));
1697   PetscCall(PetscDrawSave(draw));
1698   PetscFunctionReturn(0);
1699 }
1700 
1701 #if defined(PETSC_HAVE_EXODUSII)
1702 #include <exodusII.h>
1703 #include <petscviewerexodusii.h>
1704 #endif
1705 
1706 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1707 {
1708   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1709   char           name[PETSC_MAX_PATH_LEN];
1710 
1711   PetscFunctionBegin;
1712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1713   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1714   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii));
1715   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
1716   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
1717   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
1718   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
1719   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus));
1720   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERCGNS,     &iscgns));
1721   if (iascii) {
1722     PetscViewerFormat format;
1723     PetscCall(PetscViewerGetFormat(viewer, &format));
1724     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1725     else PetscCall(DMPlexView_Ascii(dm, viewer));
1726   } else if (ishdf5) {
1727 #if defined(PETSC_HAVE_HDF5)
1728     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1729 #else
1730     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1731 #endif
1732   } else if (isvtk) {
1733     PetscCall(DMPlexVTKWriteAll((PetscObject) dm,viewer));
1734   } else if (isdraw) {
1735     PetscCall(DMPlexView_Draw(dm, viewer));
1736   } else if (isglvis) {
1737     PetscCall(DMPlexView_GLVis(dm, viewer));
1738 #if defined(PETSC_HAVE_EXODUSII)
1739   } else if (isexodus) {
1740 /*
1741       exodusII requires that all sets be part of exactly one cell set.
1742       If the dm does not have a "Cell Sets" label defined, we create one
1743       with ID 1, containig all cells.
1744       Note that if the Cell Sets label is defined but does not cover all cells,
1745       we may still have a problem. This should probably be checked here or in the viewer;
1746     */
1747     PetscInt numCS;
1748     PetscCall(DMGetLabelSize(dm,"Cell Sets",&numCS));
1749     if (!numCS) {
1750       PetscInt cStart, cEnd, c;
1751       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1752       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1753       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1754     }
1755     PetscCall(DMView_PlexExodusII(dm, viewer));
1756 #endif
1757 #if defined(PETSC_HAVE_CGNS)
1758   } else if (iscgns) {
1759     PetscCall(DMView_PlexCGNS(dm, viewer));
1760 #endif
1761   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1762   /* Optionally view the partition */
1763   PetscCall(PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg));
1764   if (flg) {
1765     Vec ranks;
1766     PetscCall(DMPlexCreateRankField(dm, &ranks));
1767     PetscCall(VecView(ranks, viewer));
1768     PetscCall(VecDestroy(&ranks));
1769   }
1770   /* Optionally view a label */
1771   PetscCall(PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1772   if (flg) {
1773     DMLabel label;
1774     Vec     val;
1775 
1776     PetscCall(DMGetLabel(dm, name, &label));
1777     PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1778     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1779     PetscCall(VecView(val, viewer));
1780     PetscCall(VecDestroy(&val));
1781   }
1782   PetscFunctionReturn(0);
1783 }
1784 
1785 /*@
1786   DMPlexTopologyView - Saves a DMPlex topology into a file
1787 
1788   Collective on DM
1789 
1790   Input Parameters:
1791 + dm     - The DM whose topology is to be saved
1792 - viewer - The PetscViewer for saving
1793 
1794   Level: advanced
1795 
1796 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1797 @*/
1798 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1799 {
1800   PetscBool      ishdf5;
1801 
1802   PetscFunctionBegin;
1803   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1804   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1805   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1806   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView,viewer,0,0,0));
1807   if (ishdf5) {
1808 #if defined(PETSC_HAVE_HDF5)
1809     PetscViewerFormat format;
1810     PetscCall(PetscViewerGetFormat(viewer, &format));
1811     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1812       IS globalPointNumbering;
1813 
1814       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1815       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1816       PetscCall(ISDestroy(&globalPointNumbering));
1817     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1818 #else
1819     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1820 #endif
1821   }
1822   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView,viewer,0,0,0));
1823   PetscFunctionReturn(0);
1824 }
1825 
1826 /*@
1827   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1828 
1829   Collective on DM
1830 
1831   Input Parameters:
1832 + dm     - The DM whose coordinates are to be saved
1833 - viewer - The PetscViewer for saving
1834 
1835   Level: advanced
1836 
1837 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1838 @*/
1839 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1840 {
1841   PetscBool      ishdf5;
1842 
1843   PetscFunctionBegin;
1844   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1845   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1846   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1847   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView,viewer,0,0,0));
1848   if (ishdf5) {
1849 #if defined(PETSC_HAVE_HDF5)
1850     PetscViewerFormat format;
1851     PetscCall(PetscViewerGetFormat(viewer, &format));
1852     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1853       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1854     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1855 #else
1856     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1857 #endif
1858   }
1859   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView,viewer,0,0,0));
1860   PetscFunctionReturn(0);
1861 }
1862 
1863 /*@
1864   DMPlexLabelsView - Saves DMPlex labels into a file
1865 
1866   Collective on DM
1867 
1868   Input Parameters:
1869 + dm     - The DM whose labels are to be saved
1870 - viewer - The PetscViewer for saving
1871 
1872   Level: advanced
1873 
1874 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1875 @*/
1876 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1877 {
1878   PetscBool      ishdf5;
1879 
1880   PetscFunctionBegin;
1881   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1882   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1883   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1884   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView,viewer,0,0,0));
1885   if (ishdf5) {
1886 #if defined(PETSC_HAVE_HDF5)
1887     IS                globalPointNumbering;
1888     PetscViewerFormat format;
1889 
1890     PetscCall(PetscViewerGetFormat(viewer, &format));
1891     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1892       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1893       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1894       PetscCall(ISDestroy(&globalPointNumbering));
1895     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1896 #else
1897     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1898 #endif
1899   }
1900   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView,viewer,0,0,0));
1901   PetscFunctionReturn(0);
1902 }
1903 
1904 /*@
1905   DMPlexSectionView - Saves a section associated with a DMPlex
1906 
1907   Collective on DM
1908 
1909   Input Parameters:
1910 + dm         - The DM that contains the topology on which the section to be saved is defined
1911 . viewer     - The PetscViewer for saving
1912 - sectiondm  - The DM that contains the section to be saved
1913 
1914   Level: advanced
1915 
1916   Notes:
1917   This function is a wrapper around PetscSectionView(); in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with DMPlexTopologyLoad() and DMPlexSectionLoad(), respectively, this information is used to match section points with topology points.
1918 
1919   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1920 
1921 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1922 @*/
1923 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1924 {
1925   PetscBool      ishdf5;
1926 
1927   PetscFunctionBegin;
1928   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1929   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1930   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1931   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
1932   PetscCall(PetscLogEventBegin(DMPLEX_SectionView,viewer,0,0,0));
1933   if (ishdf5) {
1934 #if defined(PETSC_HAVE_HDF5)
1935     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1936 #else
1937     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1938 #endif
1939   }
1940   PetscCall(PetscLogEventEnd(DMPLEX_SectionView,viewer,0,0,0));
1941   PetscFunctionReturn(0);
1942 }
1943 
1944 /*@
1945   DMPlexGlobalVectorView - Saves a global vector
1946 
1947   Collective on DM
1948 
1949   Input Parameters:
1950 + dm        - The DM that represents the topology
1951 . viewer    - The PetscViewer to save data with
1952 . sectiondm - The DM that contains the global section on which vec is defined
1953 - vec       - The global vector to be saved
1954 
1955   Level: advanced
1956 
1957   Notes:
1958   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1959 
1960   Typical calling sequence
1961 $       DMCreate(PETSC_COMM_WORLD, &dm);
1962 $       DMSetType(dm, DMPLEX);
1963 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1964 $       DMClone(dm, &sectiondm);
1965 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1966 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1967 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1968 $       PetscSectionSetChart(section, pStart, pEnd);
1969 $       PetscSectionSetUp(section);
1970 $       DMSetLocalSection(sectiondm, section);
1971 $       PetscSectionDestroy(&section);
1972 $       DMGetGlobalVector(sectiondm, &vec);
1973 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1974 $       DMPlexTopologyView(dm, viewer);
1975 $       DMPlexSectionView(dm, viewer, sectiondm);
1976 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1977 $       DMRestoreGlobalVector(sectiondm, &vec);
1978 $       DMDestroy(&sectiondm);
1979 $       DMDestroy(&dm);
1980 
1981 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1982 @*/
1983 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
1984 {
1985   PetscBool       ishdf5;
1986 
1987   PetscFunctionBegin;
1988   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1989   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1990   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1991   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1992   /* Check consistency */
1993   {
1994     PetscSection  section;
1995     PetscBool     includesConstraints;
1996     PetscInt      m, m1;
1997 
1998     PetscCall(VecGetLocalSize(vec, &m1));
1999     PetscCall(DMGetGlobalSection(sectiondm, &section));
2000     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2001     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2002     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2003     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2004   }
2005   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2006   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView,viewer,0,0,0));
2007   if (ishdf5) {
2008 #if defined(PETSC_HAVE_HDF5)
2009     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2010 #else
2011     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2012 #endif
2013   }
2014   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView,viewer,0,0,0));
2015   PetscFunctionReturn(0);
2016 }
2017 
2018 /*@
2019   DMPlexLocalVectorView - Saves a local vector
2020 
2021   Collective on DM
2022 
2023   Input Parameters:
2024 + dm        - The DM that represents the topology
2025 . viewer    - The PetscViewer to save data with
2026 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2027 - vec       - The local vector to be saved
2028 
2029   Level: advanced
2030 
2031   Notes:
2032   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2033 
2034   Typical calling sequence
2035 $       DMCreate(PETSC_COMM_WORLD, &dm);
2036 $       DMSetType(dm, DMPLEX);
2037 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2038 $       DMClone(dm, &sectiondm);
2039 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2040 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2041 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2042 $       PetscSectionSetChart(section, pStart, pEnd);
2043 $       PetscSectionSetUp(section);
2044 $       DMSetLocalSection(sectiondm, section);
2045 $       DMGetLocalVector(sectiondm, &vec);
2046 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2047 $       DMPlexTopologyView(dm, viewer);
2048 $       DMPlexSectionView(dm, viewer, sectiondm);
2049 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2050 $       DMRestoreLocalVector(sectiondm, &vec);
2051 $       DMDestroy(&sectiondm);
2052 $       DMDestroy(&dm);
2053 
2054 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2055 @*/
2056 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2057 {
2058   PetscBool       ishdf5;
2059 
2060   PetscFunctionBegin;
2061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2062   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2063   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2064   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2065   /* Check consistency */
2066   {
2067     PetscSection  section;
2068     PetscBool     includesConstraints;
2069     PetscInt      m, m1;
2070 
2071     PetscCall(VecGetLocalSize(vec, &m1));
2072     PetscCall(DMGetLocalSection(sectiondm, &section));
2073     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2074     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2075     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2076     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2077   }
2078   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2079   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView,viewer,0,0,0));
2080   if (ishdf5) {
2081 #if defined(PETSC_HAVE_HDF5)
2082     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2083 #else
2084     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2085 #endif
2086   }
2087   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView,viewer,0,0,0));
2088   PetscFunctionReturn(0);
2089 }
2090 
2091 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2092 {
2093   PetscBool      ishdf5;
2094 
2095   PetscFunctionBegin;
2096   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2097   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2098   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5));
2099   if (ishdf5) {
2100 #if defined(PETSC_HAVE_HDF5)
2101     PetscViewerFormat format;
2102     PetscCall(PetscViewerGetFormat(viewer, &format));
2103     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2104       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2105     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2106       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2107     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2108     PetscFunctionReturn(0);
2109 #else
2110     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2111 #endif
2112   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2113 }
2114 
2115 /*@
2116   DMPlexTopologyLoad - Loads a topology into a DMPlex
2117 
2118   Collective on DM
2119 
2120   Input Parameters:
2121 + dm     - The DM into which the topology is loaded
2122 - viewer - The PetscViewer for the saved topology
2123 
2124   Output Parameters:
2125 . globalToLocalPointSF - The PetscSF that pushes points in [0, N) to the associated points in the loaded plex, where N is the global number of points; NULL if unneeded
2126 
2127   Level: advanced
2128 
2129 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2130 @*/
2131 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2132 {
2133   PetscBool      ishdf5;
2134 
2135   PetscFunctionBegin;
2136   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2137   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2138   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2139   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2140   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad,viewer,0,0,0));
2141   if (ishdf5) {
2142 #if defined(PETSC_HAVE_HDF5)
2143     PetscViewerFormat format;
2144     PetscCall(PetscViewerGetFormat(viewer, &format));
2145     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2146       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2147     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2148 #else
2149     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2150 #endif
2151   }
2152   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad,viewer,0,0,0));
2153   PetscFunctionReturn(0);
2154 }
2155 
2156 /*@
2157   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2158 
2159   Collective on DM
2160 
2161   Input Parameters:
2162 + dm     - The DM into which the coordinates are loaded
2163 . viewer - The PetscViewer for the saved coordinates
2164 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2165 
2166   Level: advanced
2167 
2168 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2169 @*/
2170 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2171 {
2172   PetscBool      ishdf5;
2173 
2174   PetscFunctionBegin;
2175   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2176   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2177   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2178   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2179   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2180   if (ishdf5) {
2181 #if defined(PETSC_HAVE_HDF5)
2182     PetscViewerFormat format;
2183     PetscCall(PetscViewerGetFormat(viewer, &format));
2184     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2185       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2186     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2187 #else
2188     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2189 #endif
2190   }
2191   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2192   PetscFunctionReturn(0);
2193 }
2194 
2195 /*@
2196   DMPlexLabelsLoad - Loads labels into a DMPlex
2197 
2198   Collective on DM
2199 
2200   Input Parameters:
2201 + dm     - The DM into which the labels are loaded
2202 . viewer - The PetscViewer for the saved labels
2203 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2204 
2205   Level: advanced
2206 
2207   Notes:
2208   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2209 
2210 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2211 @*/
2212 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2213 {
2214   PetscBool      ishdf5;
2215 
2216   PetscFunctionBegin;
2217   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2218   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2219   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2220   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2221   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad,viewer,0,0,0));
2222   if (ishdf5) {
2223 #if defined(PETSC_HAVE_HDF5)
2224     PetscViewerFormat format;
2225 
2226     PetscCall(PetscViewerGetFormat(viewer, &format));
2227     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2228       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2229     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2230 #else
2231     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2232 #endif
2233   }
2234   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad,viewer,0,0,0));
2235   PetscFunctionReturn(0);
2236 }
2237 
2238 /*@
2239   DMPlexSectionLoad - Loads section into a DMPlex
2240 
2241   Collective on DM
2242 
2243   Input Parameters:
2244 + dm          - The DM that represents the topology
2245 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2246 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2247 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2248 
2249   Output Parameters
2250 + globalDofSF - The SF that migrates any on-disk Vec data associated with sectionA into a global Vec associated with the sectiondm's global section (NULL if not needed)
2251 - localDofSF  - The SF that migrates any on-disk Vec data associated with sectionA into a local Vec associated with the sectiondm's local section (NULL if not needed)
2252 
2253   Level: advanced
2254 
2255   Notes:
2256   This function is a wrapper around PetscSectionLoad(); it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in dm. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2257 
2258   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2259 
2260   The output parameter, globalDofSF (localDofSF), can later be used with DMPlexGlobalVectorLoad() (DMPlexLocalVectorLoad()) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2261 
2262   Example using 2 processes:
2263 $  NX (number of points on dm): 4
2264 $  sectionA                   : the on-disk section
2265 $  vecA                       : a vector associated with sectionA
2266 $  sectionB                   : sectiondm's local section constructed in this function
2267 $  vecB (local)               : a vector associated with sectiondm's local section
2268 $  vecB (global)              : a vector associated with sectiondm's global section
2269 $
2270 $                                     rank 0    rank 1
2271 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2272 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2273 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2274 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2275 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2276 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2277 $  sectionB->atlasDof             :     1 0 1 | 1 3
2278 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2279 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2280 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2281 $
2282 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2283 
2284 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2285 @*/
2286 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2287 {
2288   PetscBool      ishdf5;
2289 
2290   PetscFunctionBegin;
2291   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2292   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2293   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2294   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2295   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2296   if (localDofSF) PetscValidPointer(localDofSF, 6);
2297   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2298   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad,viewer,0,0,0));
2299   if (ishdf5) {
2300 #if defined(PETSC_HAVE_HDF5)
2301     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2302 #else
2303     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2304 #endif
2305   }
2306   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad,viewer,0,0,0));
2307   PetscFunctionReturn(0);
2308 }
2309 
2310 /*@
2311   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2312 
2313   Collective on DM
2314 
2315   Input Parameters:
2316 + dm        - The DM that represents the topology
2317 . viewer    - The PetscViewer that represents the on-disk vector data
2318 . sectiondm - The DM that contains the global section on which vec is defined
2319 . sf        - The SF that migrates the on-disk vector data into vec
2320 - vec       - The global vector to set values of
2321 
2322   Level: advanced
2323 
2324   Notes:
2325   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2326 
2327   Typical calling sequence
2328 $       DMCreate(PETSC_COMM_WORLD, &dm);
2329 $       DMSetType(dm, DMPLEX);
2330 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2331 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2332 $       DMClone(dm, &sectiondm);
2333 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2334 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2335 $       DMGetGlobalVector(sectiondm, &vec);
2336 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2337 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2338 $       DMRestoreGlobalVector(sectiondm, &vec);
2339 $       PetscSFDestroy(&gsf);
2340 $       PetscSFDestroy(&sfX);
2341 $       DMDestroy(&sectiondm);
2342 $       DMDestroy(&dm);
2343 
2344 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2345 @*/
2346 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2347 {
2348   PetscBool       ishdf5;
2349 
2350   PetscFunctionBegin;
2351   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2352   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2353   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2354   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2355   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2356   /* Check consistency */
2357   {
2358     PetscSection  section;
2359     PetscBool     includesConstraints;
2360     PetscInt      m, m1;
2361 
2362     PetscCall(VecGetLocalSize(vec, &m1));
2363     PetscCall(DMGetGlobalSection(sectiondm, &section));
2364     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2365     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2366     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2367     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2368   }
2369   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2370   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2371   if (ishdf5) {
2372 #if defined(PETSC_HAVE_HDF5)
2373     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2374 #else
2375     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2376 #endif
2377   }
2378   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2379   PetscFunctionReturn(0);
2380 }
2381 
2382 /*@
2383   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2384 
2385   Collective on DM
2386 
2387   Input Parameters:
2388 + dm        - The DM that represents the topology
2389 . viewer    - The PetscViewer that represents the on-disk vector data
2390 . sectiondm - The DM that contains the local section on which vec is defined
2391 . sf        - The SF that migrates the on-disk vector data into vec
2392 - vec       - The local vector to set values of
2393 
2394   Level: advanced
2395 
2396   Notes:
2397   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2398 
2399   Typical calling sequence
2400 $       DMCreate(PETSC_COMM_WORLD, &dm);
2401 $       DMSetType(dm, DMPLEX);
2402 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2403 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2404 $       DMClone(dm, &sectiondm);
2405 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2406 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2407 $       DMGetLocalVector(sectiondm, &vec);
2408 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2409 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2410 $       DMRestoreLocalVector(sectiondm, &vec);
2411 $       PetscSFDestroy(&lsf);
2412 $       PetscSFDestroy(&sfX);
2413 $       DMDestroy(&sectiondm);
2414 $       DMDestroy(&dm);
2415 
2416 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2417 @*/
2418 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2419 {
2420   PetscBool       ishdf5;
2421 
2422   PetscFunctionBegin;
2423   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2424   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2425   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2426   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2427   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2428   /* Check consistency */
2429   {
2430     PetscSection  section;
2431     PetscBool     includesConstraints;
2432     PetscInt      m, m1;
2433 
2434     PetscCall(VecGetLocalSize(vec, &m1));
2435     PetscCall(DMGetLocalSection(sectiondm, &section));
2436     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2437     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2438     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2439     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2440   }
2441   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2442   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2443   if (ishdf5) {
2444 #if defined(PETSC_HAVE_HDF5)
2445     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2446 #else
2447     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2448 #endif
2449   }
2450   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2451   PetscFunctionReturn(0);
2452 }
2453 
2454 PetscErrorCode DMDestroy_Plex(DM dm)
2455 {
2456   DM_Plex       *mesh = (DM_Plex*) dm->data;
2457 
2458   PetscFunctionBegin;
2459   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL));
2460   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL));
2461   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL));
2462   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMInterpolateSolution_C", NULL));
2463   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2464   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C", NULL));
2465   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeGetDefault_C", NULL));
2466   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeSetDefault_C", NULL));
2467   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"MatComputeNeumannOverlap_C",NULL));
2468   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderGetDefault_C", NULL));
2469   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderSetDefault_C", NULL));
2470   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C",NULL));
2471   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexSetOverlap_C",NULL));
2472   if (--mesh->refct > 0) PetscFunctionReturn(0);
2473   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2474   PetscCall(PetscFree(mesh->cones));
2475   PetscCall(PetscFree(mesh->coneOrientations));
2476   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2477   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2478   PetscCall(PetscFree(mesh->supports));
2479   PetscCall(PetscFree(mesh->facesTmp));
2480   PetscCall(PetscFree(mesh->tetgenOpts));
2481   PetscCall(PetscFree(mesh->triangleOpts));
2482   PetscCall(PetscFree(mesh->transformType));
2483   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2484   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2485   PetscCall(ISDestroy(&mesh->subpointIS));
2486   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2487   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2488   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2489   PetscCall(ISDestroy(&mesh->anchorIS));
2490   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2491   PetscCall(PetscFree(mesh->parents));
2492   PetscCall(PetscFree(mesh->childIDs));
2493   PetscCall(PetscSectionDestroy(&mesh->childSection));
2494   PetscCall(PetscFree(mesh->children));
2495   PetscCall(DMDestroy(&mesh->referenceTree));
2496   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2497   PetscCall(PetscFree(mesh->neighbors));
2498   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2499   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2500   PetscCall(PetscFree(mesh));
2501   PetscFunctionReturn(0);
2502 }
2503 
2504 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2505 {
2506   PetscSection           sectionGlobal;
2507   PetscInt               bs = -1, mbs;
2508   PetscInt               localSize, localStart = 0;
2509   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2510   MatType                mtype;
2511   ISLocalToGlobalMapping ltog;
2512 
2513   PetscFunctionBegin;
2514   PetscCall(MatInitializePackage());
2515   mtype = dm->mattype;
2516   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2517   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2518   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2519   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject) dm)));
2520   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2521   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2522   PetscCall(MatSetType(*J, mtype));
2523   PetscCall(MatSetFromOptions(*J));
2524   PetscCall(MatGetBlockSize(*J, &mbs));
2525   if (mbs > 1) bs = mbs;
2526   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2527   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2528   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2529   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2530   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2531   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2532   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2533   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2534   if (!isShell) {
2535     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2536     PetscInt  *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2537     PetscInt  pStart, pEnd, p, dof, cdof;
2538 
2539     PetscCall(DMGetLocalToGlobalMapping(dm,&ltog));
2540 
2541     PetscCall(PetscCalloc1(localSize, &pblocks));
2542     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2543     for (p = pStart; p < pEnd; ++p) {
2544       PetscInt bdof, offset;
2545 
2546       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2547       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2548       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2549       for (PetscInt i=0; i < dof - cdof; i++)
2550         pblocks[offset - localStart + i] = dof - cdof;
2551       dof  = dof < 0 ? -(dof+1) : dof;
2552       bdof = cdof && (dof-cdof) ? 1 : dof;
2553       if (dof) {
2554         if (bs < 0)          {bs = bdof;}
2555         else if (bs != bdof) {bs = 1;}
2556       }
2557     }
2558     /* Must have same blocksize on all procs (some might have no points) */
2559     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2560     bsLocal[1] = bs;
2561     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
2562     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2563     else bs = bsMinMax[0];
2564     bs = PetscMax(1,bs);
2565     PetscCall(MatSetLocalToGlobalMapping(*J,ltog,ltog));
2566     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2567       PetscCall(MatSetBlockSize(*J, bs));
2568       PetscCall(MatSetUp(*J));
2569     } else {
2570       PetscCall(PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu));
2571       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2572       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2573     }
2574     { // Consolidate blocks
2575       PetscInt nblocks = 0;
2576       for (PetscInt i=0; i<localSize; i += PetscMax(1, pblocks[i])) {
2577         if (pblocks[i] == 0) continue;
2578         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2579         for (PetscInt j=1; j<pblocks[i]; j++) {
2580            PetscCheck(pblocks[i+j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i+j]);
2581         }
2582       }
2583       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2584     }
2585     PetscCall(PetscFree(pblocks));
2586   }
2587   PetscCall(MatSetDM(*J, dm));
2588   PetscFunctionReturn(0);
2589 }
2590 
2591 /*@
2592   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2593 
2594   Not collective
2595 
2596   Input Parameter:
2597 . mesh - The DMPlex
2598 
2599   Output Parameters:
2600 . subsection - The subdomain section
2601 
2602   Level: developer
2603 
2604 .seealso:
2605 @*/
2606 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2607 {
2608   DM_Plex       *mesh = (DM_Plex*) dm->data;
2609 
2610   PetscFunctionBegin;
2611   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2612   if (!mesh->subdomainSection) {
2613     PetscSection section;
2614     PetscSF      sf;
2615 
2616     PetscCall(PetscSFCreate(PETSC_COMM_SELF,&sf));
2617     PetscCall(DMGetLocalSection(dm,&section));
2618     PetscCall(PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection));
2619     PetscCall(PetscSFDestroy(&sf));
2620   }
2621   *subsection = mesh->subdomainSection;
2622   PetscFunctionReturn(0);
2623 }
2624 
2625 /*@
2626   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2627 
2628   Not collective
2629 
2630   Input Parameter:
2631 . mesh - The DMPlex
2632 
2633   Output Parameters:
2634 + pStart - The first mesh point
2635 - pEnd   - The upper bound for mesh points
2636 
2637   Level: beginner
2638 
2639 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2640 @*/
2641 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2642 {
2643   DM_Plex       *mesh = (DM_Plex*) dm->data;
2644 
2645   PetscFunctionBegin;
2646   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2647   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2648   PetscFunctionReturn(0);
2649 }
2650 
2651 /*@
2652   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2653 
2654   Not collective
2655 
2656   Input Parameters:
2657 + mesh - The DMPlex
2658 . pStart - The first mesh point
2659 - pEnd   - The upper bound for mesh points
2660 
2661   Output Parameters:
2662 
2663   Level: beginner
2664 
2665 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2666 @*/
2667 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2668 {
2669   DM_Plex       *mesh = (DM_Plex*) dm->data;
2670 
2671   PetscFunctionBegin;
2672   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2673   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2674   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2675   PetscFunctionReturn(0);
2676 }
2677 
2678 /*@
2679   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2680 
2681   Not collective
2682 
2683   Input Parameters:
2684 + mesh - The DMPlex
2685 - p - The point, which must lie in the chart set with DMPlexSetChart()
2686 
2687   Output Parameter:
2688 . size - The cone size for point p
2689 
2690   Level: beginner
2691 
2692 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2693 @*/
2694 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2695 {
2696   DM_Plex       *mesh = (DM_Plex*) dm->data;
2697 
2698   PetscFunctionBegin;
2699   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2700   PetscValidIntPointer(size, 3);
2701   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2702   PetscFunctionReturn(0);
2703 }
2704 
2705 /*@
2706   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2707 
2708   Not collective
2709 
2710   Input Parameters:
2711 + mesh - The DMPlex
2712 . p - The point, which must lie in the chart set with DMPlexSetChart()
2713 - size - The cone size for point p
2714 
2715   Output Parameter:
2716 
2717   Note:
2718   This should be called after DMPlexSetChart().
2719 
2720   Level: beginner
2721 
2722 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2723 @*/
2724 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2725 {
2726   DM_Plex       *mesh = (DM_Plex*) dm->data;
2727 
2728   PetscFunctionBegin;
2729   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2730   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2731   PetscFunctionReturn(0);
2732 }
2733 
2734 /*@
2735   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2736 
2737   Not collective
2738 
2739   Input Parameters:
2740 + mesh - The DMPlex
2741 . p - The point, which must lie in the chart set with DMPlexSetChart()
2742 - size - The additional cone size for point p
2743 
2744   Output Parameter:
2745 
2746   Note:
2747   This should be called after DMPlexSetChart().
2748 
2749   Level: beginner
2750 
2751 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2752 @*/
2753 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2754 {
2755   DM_Plex       *mesh = (DM_Plex*) dm->data;
2756   PetscFunctionBegin;
2757   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2758   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2759   PetscFunctionReturn(0);
2760 }
2761 
2762 /*@C
2763   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2764 
2765   Not collective
2766 
2767   Input Parameters:
2768 + dm - The DMPlex
2769 - p - The point, which must lie in the chart set with DMPlexSetChart()
2770 
2771   Output Parameter:
2772 . cone - An array of points which are on the in-edges for point p
2773 
2774   Level: beginner
2775 
2776   Fortran Notes:
2777   Since it returns an array, this routine is only available in Fortran 90, and you must
2778   include petsc.h90 in your code.
2779   You must also call DMPlexRestoreCone() after you finish using the returned array.
2780   DMPlexRestoreCone() is not needed/available in C.
2781 
2782 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2783 @*/
2784 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2785 {
2786   DM_Plex       *mesh = (DM_Plex*) dm->data;
2787   PetscInt       off;
2788 
2789   PetscFunctionBegin;
2790   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2791   PetscValidPointer(cone, 3);
2792   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2793   *cone = &mesh->cones[off];
2794   PetscFunctionReturn(0);
2795 }
2796 
2797 /*@C
2798   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2799 
2800   Not collective
2801 
2802   Input Parameters:
2803 + dm - The DMPlex
2804 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2805 
2806   Output Parameters:
2807 + pConesSection - PetscSection describing the layout of pCones
2808 - pCones - An array of points which are on the in-edges for the point set p
2809 
2810   Level: intermediate
2811 
2812 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2813 @*/
2814 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2815 {
2816   PetscSection        cs, newcs;
2817   PetscInt            *cones;
2818   PetscInt            *newarr=NULL;
2819   PetscInt            n;
2820 
2821   PetscFunctionBegin;
2822   PetscCall(DMPlexGetCones(dm, &cones));
2823   PetscCall(DMPlexGetConeSection(dm, &cs));
2824   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL));
2825   if (pConesSection) *pConesSection = newcs;
2826   if (pCones) {
2827     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2828     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2829   }
2830   PetscFunctionReturn(0);
2831 }
2832 
2833 /*@
2834   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2835 
2836   Not collective
2837 
2838   Input Parameters:
2839 + dm - The DMPlex
2840 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2841 
2842   Output Parameter:
2843 . expandedPoints - An array of vertices recursively expanded from input points
2844 
2845   Level: advanced
2846 
2847   Notes:
2848   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2849   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2850 
2851 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2852 @*/
2853 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2854 {
2855   IS                  *expandedPointsAll;
2856   PetscInt            depth;
2857 
2858   PetscFunctionBegin;
2859   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2860   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2861   PetscValidPointer(expandedPoints, 3);
2862   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2863   *expandedPoints = expandedPointsAll[0];
2864   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2865   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2866   PetscFunctionReturn(0);
2867 }
2868 
2869 /*@
2870   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
2871 
2872   Not collective
2873 
2874   Input Parameters:
2875 + dm - The DMPlex
2876 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2877 
2878   Output Parameters:
2879 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2880 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2881 - sections - (optional) An array of sections which describe mappings from points to their cone points
2882 
2883   Level: advanced
2884 
2885   Notes:
2886   Like DMPlexGetConeTuple() but recursive.
2887 
2888   Array expandedPoints has size equal to depth. Each expandedPoints[d] contains DAG points with maximum depth d, recursively cone-wise expanded from the input points.
2889   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2890 
2891   Array section has size equal to depth.  Each PetscSection sections[d] realizes mapping from expandedPoints[d+1] (section points) to expandedPoints[d] (section dofs) as follows:
2892   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2893   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2894 
2895 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2896 @*/
2897 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2898 {
2899   const PetscInt      *arr0=NULL, *cone=NULL;
2900   PetscInt            *arr=NULL, *newarr=NULL;
2901   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
2902   IS                  *expandedPoints_;
2903   PetscSection        *sections_;
2904 
2905   PetscFunctionBegin;
2906   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2907   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2908   if (depth) PetscValidIntPointer(depth, 3);
2909   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2910   if (sections) PetscValidPointer(sections, 5);
2911   PetscCall(ISGetLocalSize(points, &n));
2912   PetscCall(ISGetIndices(points, &arr0));
2913   PetscCall(DMPlexGetDepth(dm, &depth_));
2914   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2915   PetscCall(PetscCalloc1(depth_, &sections_));
2916   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
2917   for (d=depth_-1; d>=0; d--) {
2918     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2919     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2920     for (i=0; i<n; i++) {
2921       PetscCall(DMPlexGetDepthStratum(dm, d+1, &start, &end));
2922       if (arr[i] >= start && arr[i] < end) {
2923         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2924         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2925       } else {
2926         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2927       }
2928     }
2929     PetscCall(PetscSectionSetUp(sections_[d]));
2930     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2931     PetscCall(PetscMalloc1(newn, &newarr));
2932     for (i=0; i<n; i++) {
2933       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2934       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2935       if (cn > 1) {
2936         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2937         PetscCall(PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt)));
2938       } else {
2939         newarr[co] = arr[i];
2940       }
2941     }
2942     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2943     arr = newarr;
2944     n = newn;
2945   }
2946   PetscCall(ISRestoreIndices(points, &arr0));
2947   *depth = depth_;
2948   if (expandedPoints) *expandedPoints = expandedPoints_;
2949   else {
2950     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2951     PetscCall(PetscFree(expandedPoints_));
2952   }
2953   if (sections) *sections = sections_;
2954   else {
2955     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2956     PetscCall(PetscFree(sections_));
2957   }
2958   PetscFunctionReturn(0);
2959 }
2960 
2961 /*@
2962   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2963 
2964   Not collective
2965 
2966   Input Parameters:
2967 + dm - The DMPlex
2968 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2969 
2970   Output Parameters:
2971 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2972 . expandedPoints - (optional) An array of recursively expanded cones
2973 - sections - (optional) An array of sections which describe mappings from points to their cone points
2974 
2975   Level: advanced
2976 
2977   Notes:
2978   See DMPlexGetConeRecursive() for details.
2979 
2980 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2981 @*/
2982 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2983 {
2984   PetscInt            d, depth_;
2985 
2986   PetscFunctionBegin;
2987   PetscCall(DMPlexGetDepth(dm, &depth_));
2988   PetscCheck(!depth || *depth == depth_,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2989   if (depth) *depth = 0;
2990   if (expandedPoints) {
2991     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2992     PetscCall(PetscFree(*expandedPoints));
2993   }
2994   if (sections)  {
2995     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2996     PetscCall(PetscFree(*sections));
2997   }
2998   PetscFunctionReturn(0);
2999 }
3000 
3001 /*@
3002   DMPlexSetCone - Set the points on the in-edges for this point in the DAG; that is these are the points that cover the specific point
3003 
3004   Not collective
3005 
3006   Input Parameters:
3007 + mesh - The DMPlex
3008 . p - The point, which must lie in the chart set with DMPlexSetChart()
3009 - cone - An array of points which are on the in-edges for point p
3010 
3011   Output Parameter:
3012 
3013   Note:
3014   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3015 
3016   Level: beginner
3017 
3018 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3019 @*/
3020 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3021 {
3022   DM_Plex       *mesh = (DM_Plex*) dm->data;
3023   PetscInt       pStart, pEnd;
3024   PetscInt       dof, off, c;
3025 
3026   PetscFunctionBegin;
3027   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3028   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3029   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3030   if (dof) PetscValidIntPointer(cone, 3);
3031   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3032   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3033   for (c = 0; c < dof; ++c) {
3034     PetscCheck(!(cone[c] < pStart) && !(cone[c] >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", cone[c], pStart, pEnd);
3035     mesh->cones[off+c] = cone[c];
3036   }
3037   PetscFunctionReturn(0);
3038 }
3039 
3040 /*@C
3041   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3042 
3043   Not collective
3044 
3045   Input Parameters:
3046 + mesh - The DMPlex
3047 - p - The point, which must lie in the chart set with DMPlexSetChart()
3048 
3049   Output Parameter:
3050 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3051                     integer giving the prescription for cone traversal.
3052 
3053   Level: beginner
3054 
3055   Notes:
3056   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3057   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3058   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3059   with the identity.
3060 
3061   Fortran Notes:
3062   Since it returns an array, this routine is only available in Fortran 90, and you must
3063   include petsc.h90 in your code.
3064   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3065   DMPlexRestoreConeOrientation() is not needed/available in C.
3066 
3067 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3068 @*/
3069 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3070 {
3071   DM_Plex       *mesh = (DM_Plex*) dm->data;
3072   PetscInt       off;
3073 
3074   PetscFunctionBegin;
3075   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3076   if (PetscDefined(USE_DEBUG)) {
3077     PetscInt dof;
3078     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3079     if (dof) PetscValidPointer(coneOrientation, 3);
3080   }
3081   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3082 
3083   *coneOrientation = &mesh->coneOrientations[off];
3084   PetscFunctionReturn(0);
3085 }
3086 
3087 /*@
3088   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3089 
3090   Not collective
3091 
3092   Input Parameters:
3093 + mesh - The DMPlex
3094 . p - The point, which must lie in the chart set with DMPlexSetChart()
3095 - coneOrientation - An array of orientations
3096   Output Parameter:
3097 
3098   Notes:
3099   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3100 
3101   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3102 
3103   Level: beginner
3104 
3105 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3106 @*/
3107 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3108 {
3109   DM_Plex       *mesh = (DM_Plex*) dm->data;
3110   PetscInt       pStart, pEnd;
3111   PetscInt       dof, off, c;
3112 
3113   PetscFunctionBegin;
3114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3115   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3116   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3117   if (dof) PetscValidIntPointer(coneOrientation, 3);
3118   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3119   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3120   for (c = 0; c < dof; ++c) {
3121     PetscInt cdof, o = coneOrientation[c];
3122 
3123     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof));
3124     PetscCheck(!o || (o >= -(cdof+1) && o < cdof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ". %" PetscInt_FMT ")", o, -(cdof+1), cdof);
3125     mesh->coneOrientations[off+c] = o;
3126   }
3127   PetscFunctionReturn(0);
3128 }
3129 
3130 /*@
3131   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3132 
3133   Not collective
3134 
3135   Input Parameters:
3136 + mesh - The DMPlex
3137 . p - The point, which must lie in the chart set with DMPlexSetChart()
3138 . conePos - The local index in the cone where the point should be put
3139 - conePoint - The mesh point to insert
3140 
3141   Level: beginner
3142 
3143 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3144 @*/
3145 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3146 {
3147   DM_Plex       *mesh = (DM_Plex*) dm->data;
3148   PetscInt       pStart, pEnd;
3149   PetscInt       dof, off;
3150 
3151   PetscFunctionBegin;
3152   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3153   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3154   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3155   PetscCheck(!(conePoint < pStart) && !(conePoint >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", conePoint, pStart, pEnd);
3156   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3157   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3158   PetscCheck(!(conePos < 0) && !(conePos >= dof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3159   mesh->cones[off+conePos] = conePoint;
3160   PetscFunctionReturn(0);
3161 }
3162 
3163 /*@
3164   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3165 
3166   Not collective
3167 
3168   Input Parameters:
3169 + mesh - The DMPlex
3170 . p - The point, which must lie in the chart set with DMPlexSetChart()
3171 . conePos - The local index in the cone where the point should be put
3172 - coneOrientation - The point orientation to insert
3173 
3174   Level: beginner
3175 
3176   Notes:
3177   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3178 
3179 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3180 @*/
3181 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3182 {
3183   DM_Plex       *mesh = (DM_Plex*) dm->data;
3184   PetscInt       pStart, pEnd;
3185   PetscInt       dof, off;
3186 
3187   PetscFunctionBegin;
3188   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3189   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3190   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3191   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3192   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3193   PetscCheck(!(conePos < 0) && !(conePos >= dof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3194   mesh->coneOrientations[off+conePos] = coneOrientation;
3195   PetscFunctionReturn(0);
3196 }
3197 
3198 /*@
3199   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3200 
3201   Not collective
3202 
3203   Input Parameters:
3204 + mesh - The DMPlex
3205 - p - The point, which must lie in the chart set with DMPlexSetChart()
3206 
3207   Output Parameter:
3208 . size - The support size for point p
3209 
3210   Level: beginner
3211 
3212 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3213 @*/
3214 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3215 {
3216   DM_Plex       *mesh = (DM_Plex*) dm->data;
3217 
3218   PetscFunctionBegin;
3219   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3220   PetscValidIntPointer(size, 3);
3221   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3222   PetscFunctionReturn(0);
3223 }
3224 
3225 /*@
3226   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3227 
3228   Not collective
3229 
3230   Input Parameters:
3231 + mesh - The DMPlex
3232 . p - The point, which must lie in the chart set with DMPlexSetChart()
3233 - size - The support size for point p
3234 
3235   Output Parameter:
3236 
3237   Note:
3238   This should be called after DMPlexSetChart().
3239 
3240   Level: beginner
3241 
3242 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3243 @*/
3244 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3245 {
3246   DM_Plex       *mesh = (DM_Plex*) dm->data;
3247 
3248   PetscFunctionBegin;
3249   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3250   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3251   PetscFunctionReturn(0);
3252 }
3253 
3254 /*@C
3255   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3256 
3257   Not collective
3258 
3259   Input Parameters:
3260 + mesh - The DMPlex
3261 - p - The point, which must lie in the chart set with DMPlexSetChart()
3262 
3263   Output Parameter:
3264 . support - An array of points which are on the out-edges for point p
3265 
3266   Level: beginner
3267 
3268   Fortran Notes:
3269   Since it returns an array, this routine is only available in Fortran 90, and you must
3270   include petsc.h90 in your code.
3271   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3272   DMPlexRestoreSupport() is not needed/available in C.
3273 
3274 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3275 @*/
3276 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3277 {
3278   DM_Plex       *mesh = (DM_Plex*) dm->data;
3279   PetscInt       off;
3280 
3281   PetscFunctionBegin;
3282   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3283   PetscValidPointer(support, 3);
3284   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3285   *support = &mesh->supports[off];
3286   PetscFunctionReturn(0);
3287 }
3288 
3289 /*@
3290   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3291 
3292   Not collective
3293 
3294   Input Parameters:
3295 + mesh - The DMPlex
3296 . p - The point, which must lie in the chart set with DMPlexSetChart()
3297 - support - An array of points which are on the out-edges for point p
3298 
3299   Output Parameter:
3300 
3301   Note:
3302   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3303 
3304   Level: beginner
3305 
3306 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3307 @*/
3308 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3309 {
3310   DM_Plex       *mesh = (DM_Plex*) dm->data;
3311   PetscInt       pStart, pEnd;
3312   PetscInt       dof, off, c;
3313 
3314   PetscFunctionBegin;
3315   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3316   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3317   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3318   if (dof) PetscValidIntPointer(support, 3);
3319   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3320   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3321   for (c = 0; c < dof; ++c) {
3322     PetscCheck(!(support[c] < pStart) && !(support[c] >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", support[c], pStart, pEnd);
3323     mesh->supports[off+c] = support[c];
3324   }
3325   PetscFunctionReturn(0);
3326 }
3327 
3328 /*@
3329   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3330 
3331   Not collective
3332 
3333   Input Parameters:
3334 + mesh - The DMPlex
3335 . p - The point, which must lie in the chart set with DMPlexSetChart()
3336 . supportPos - The local index in the cone where the point should be put
3337 - supportPoint - The mesh point to insert
3338 
3339   Level: beginner
3340 
3341 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3342 @*/
3343 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3344 {
3345   DM_Plex       *mesh = (DM_Plex*) dm->data;
3346   PetscInt       pStart, pEnd;
3347   PetscInt       dof, off;
3348 
3349   PetscFunctionBegin;
3350   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3351   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3352   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3353   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3354   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3355   PetscCheck(!(supportPoint < pStart) && !(supportPoint >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", supportPoint, pStart, pEnd);
3356   PetscCheck(supportPos < dof,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", supportPos, p, dof);
3357   mesh->supports[off+supportPos] = supportPoint;
3358   PetscFunctionReturn(0);
3359 }
3360 
3361 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3362 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3363 {
3364   switch (ct) {
3365     case DM_POLYTOPE_SEGMENT:
3366       if (o == -1) return -2;
3367       break;
3368     case DM_POLYTOPE_TRIANGLE:
3369       if (o == -3) return -1;
3370       if (o == -2) return -3;
3371       if (o == -1) return -2;
3372       break;
3373     case DM_POLYTOPE_QUADRILATERAL:
3374       if (o == -4) return -2;
3375       if (o == -3) return -1;
3376       if (o == -2) return -4;
3377       if (o == -1) return -3;
3378       break;
3379     default: return o;
3380   }
3381   return o;
3382 }
3383 
3384 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3385 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3386 {
3387   switch (ct) {
3388     case DM_POLYTOPE_SEGMENT:
3389       if ((o == -2) || (o == 1)) return -1;
3390       if (o == -1) return 0;
3391       break;
3392     case DM_POLYTOPE_TRIANGLE:
3393       if (o == -3) return -2;
3394       if (o == -2) return -1;
3395       if (o == -1) return -3;
3396       break;
3397     case DM_POLYTOPE_QUADRILATERAL:
3398       if (o == -4) return -2;
3399       if (o == -3) return -1;
3400       if (o == -2) return -4;
3401       if (o == -1) return -3;
3402       break;
3403     default: return o;
3404   }
3405   return o;
3406 }
3407 
3408 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3409 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3410 {
3411   PetscInt       pStart, pEnd, p;
3412 
3413   PetscFunctionBegin;
3414   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3415   for (p = pStart; p < pEnd; ++p) {
3416     const PetscInt *cone, *ornt;
3417     PetscInt        coneSize, c;
3418 
3419     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3420     PetscCall(DMPlexGetCone(dm, p, &cone));
3421     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3422     for (c = 0; c < coneSize; ++c) {
3423       DMPolytopeType ct;
3424       const PetscInt o = ornt[c];
3425 
3426       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3427       switch (ct) {
3428         case DM_POLYTOPE_SEGMENT:
3429           if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3430           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3431           break;
3432         case DM_POLYTOPE_TRIANGLE:
3433           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3434           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3435           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3436           break;
3437         case DM_POLYTOPE_QUADRILATERAL:
3438           if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3439           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3440           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3441           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3442           break;
3443         default: break;
3444       }
3445     }
3446   }
3447   PetscFunctionReturn(0);
3448 }
3449 
3450 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3451 {
3452   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3453   PetscInt       *closure;
3454   const PetscInt *tmp = NULL, *tmpO = NULL;
3455   PetscInt        off = 0, tmpSize, t;
3456 
3457   PetscFunctionBeginHot;
3458   if (ornt) {
3459     PetscCall(DMPlexGetCellType(dm, p, &ct));
3460     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3461   }
3462   if (*points) {
3463     closure = *points;
3464   } else {
3465     PetscInt maxConeSize, maxSupportSize;
3466     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3467     PetscCall(DMGetWorkArray(dm, 2*(PetscMax(maxConeSize, maxSupportSize)+1), MPIU_INT, &closure));
3468   }
3469   if (useCone) {
3470     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3471     PetscCall(DMPlexGetCone(dm, p, &tmp));
3472     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3473   } else {
3474     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3475     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3476   }
3477   if (ct == DM_POLYTOPE_UNKNOWN) {
3478     closure[off++] = p;
3479     closure[off++] = 0;
3480     for (t = 0; t < tmpSize; ++t) {
3481       closure[off++] = tmp[t];
3482       closure[off++] = tmpO ? tmpO[t] : 0;
3483     }
3484   } else {
3485     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3486 
3487     /* We assume that cells with a valid type have faces with a valid type */
3488     closure[off++] = p;
3489     closure[off++] = ornt;
3490     for (t = 0; t < tmpSize; ++t) {
3491       DMPolytopeType ft;
3492 
3493       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3494       closure[off++] = tmp[arr[t]];
3495       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3496     }
3497   }
3498   if (numPoints) *numPoints = tmpSize+1;
3499   if (points)    *points    = closure;
3500   PetscFunctionReturn(0);
3501 }
3502 
3503 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3504 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3505 {
3506   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3507   const PetscInt *cone, *ornt;
3508   PetscInt       *pts,  *closure = NULL;
3509   DMPolytopeType  ft;
3510   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3511   PetscInt        dim, coneSize, c, d, clSize, cl;
3512 
3513   PetscFunctionBeginHot;
3514   PetscCall(DMGetDimension(dm, &dim));
3515   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3516   PetscCall(DMPlexGetCone(dm, point, &cone));
3517   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3518   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3519   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    dim+1)-1)/(maxConeSize-1))    : dim+1;
3520   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim+1)-1)/(maxSupportSize-1)) : dim+1;
3521   maxSize       = PetscMax(coneSeries, supportSeries);
3522   if (*points) {pts  = *points;}
3523   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &pts));
3524   c    = 0;
3525   pts[c++] = point;
3526   pts[c++] = o;
3527   PetscCall(DMPlexGetCellType(dm, cone[arr[0*2+0]], &ft));
3528   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[0*2+1], ornt[0]), useCone, &clSize, &closure));
3529   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3530   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[1*2+1], ornt[1]), useCone, &clSize, &closure));
3531   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3532   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3533   for (d = 2; d < coneSize; ++d) {
3534     PetscCall(DMPlexGetCellType(dm, cone[arr[d*2+0]], &ft));
3535     pts[c++] = cone[arr[d*2+0]];
3536     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]);
3537   }
3538   if (dim >= 3) {
3539     for (d = 2; d < coneSize; ++d) {
3540       const PetscInt  fpoint = cone[arr[d*2+0]];
3541       const PetscInt *fcone, *fornt;
3542       PetscInt        fconeSize, fc, i;
3543 
3544       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3545       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]));
3546       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3547       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3548       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3549       for (fc = 0; fc < fconeSize; ++fc) {
3550         const PetscInt cp = fcone[farr[fc*2+0]];
3551         const PetscInt co = farr[fc*2+1];
3552 
3553         for (i = 0; i < c; i += 2) if (pts[i] == cp) break;
3554         if (i == c) {
3555           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3556           pts[c++] = cp;
3557           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc*2+0]]);
3558         }
3559       }
3560     }
3561   }
3562   *numPoints = c/2;
3563   *points    = pts;
3564   PetscFunctionReturn(0);
3565 }
3566 
3567 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3568 {
3569   DMPolytopeType ct;
3570   PetscInt      *closure, *fifo;
3571   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3572   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3573   PetscInt       depth, maxSize;
3574 
3575   PetscFunctionBeginHot;
3576   PetscCall(DMPlexGetDepth(dm, &depth));
3577   if (depth == 1) {
3578     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3579     PetscFunctionReturn(0);
3580   }
3581   PetscCall(DMPlexGetCellType(dm, p, &ct));
3582   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3583   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3584     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3585     PetscFunctionReturn(0);
3586   }
3587   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3588   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    depth+1)-1)/(maxConeSize-1))    : depth+1;
3589   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth+1)-1)/(maxSupportSize-1)) : depth+1;
3590   maxSize       = PetscMax(coneSeries, supportSeries);
3591   PetscCall(DMGetWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3592   if (*points) {closure = *points;}
3593   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &closure));
3594   closure[closureSize++] = p;
3595   closure[closureSize++] = ornt;
3596   fifo[fifoSize++]       = p;
3597   fifo[fifoSize++]       = ornt;
3598   fifo[fifoSize++]       = ct;
3599   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3600   while (fifoSize - fifoStart) {
3601     const PetscInt       q    = fifo[fifoStart++];
3602     const PetscInt       o    = fifo[fifoStart++];
3603     const DMPolytopeType qt   = (DMPolytopeType) fifo[fifoStart++];
3604     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3605     const PetscInt      *tmp, *tmpO;
3606     PetscInt             tmpSize, t;
3607 
3608     if (PetscDefined(USE_DEBUG)) {
3609       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt)/2;
3610       PetscCheck(!o || !(o >= nO || o < -nO),PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid orientation %" PetscInt_FMT " not in [%" PetscInt_FMT ",%" PetscInt_FMT ") for %s %" PetscInt_FMT, o, -nO, nO, DMPolytopeTypes[qt], q);
3611     }
3612     if (useCone) {
3613       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3614       PetscCall(DMPlexGetCone(dm, q, &tmp));
3615       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3616     } else {
3617       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3618       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3619       tmpO = NULL;
3620     }
3621     for (t = 0; t < tmpSize; ++t) {
3622       const PetscInt ip = useCone && qarr ? qarr[t*2]   : t;
3623       const PetscInt io = useCone && qarr ? qarr[t*2+1] : 0;
3624       const PetscInt cp = tmp[ip];
3625       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3626       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3627       PetscInt       c;
3628 
3629       /* Check for duplicate */
3630       for (c = 0; c < closureSize; c += 2) {
3631         if (closure[c] == cp) break;
3632       }
3633       if (c == closureSize) {
3634         closure[closureSize++] = cp;
3635         closure[closureSize++] = co;
3636         fifo[fifoSize++]       = cp;
3637         fifo[fifoSize++]       = co;
3638         fifo[fifoSize++]       = ct;
3639       }
3640     }
3641   }
3642   PetscCall(DMRestoreWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3643   if (numPoints) *numPoints = closureSize/2;
3644   if (points)    *points    = closure;
3645   PetscFunctionReturn(0);
3646 }
3647 
3648 /*@C
3649   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3650 
3651   Not collective
3652 
3653   Input Parameters:
3654 + dm      - The DMPlex
3655 . p       - The mesh point
3656 - useCone - PETSC_TRUE for the closure, otherwise return the star
3657 
3658   Input/Output Parameter:
3659 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3660            if NULL on input, internal storage will be returned, otherwise the provided array is used
3661 
3662   Output Parameter:
3663 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3664 
3665   Note:
3666   If using internal storage (points is NULL on input), each call overwrites the last output.
3667 
3668   Fortran Notes:
3669   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3670 
3671   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3672 
3673   Level: beginner
3674 
3675 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3676 @*/
3677 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3678 {
3679   PetscFunctionBeginHot;
3680   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3681   if (numPoints) PetscValidIntPointer(numPoints, 4);
3682   if (points)    PetscValidPointer(points, 5);
3683   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3684   PetscFunctionReturn(0);
3685 }
3686 
3687 /*@C
3688   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3689 
3690   Not collective
3691 
3692   Input Parameters:
3693 + dm        - The DMPlex
3694 . p         - The mesh point
3695 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3696 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3697 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3698 
3699   Note:
3700   If not using internal storage (points is not NULL on input), this call is unnecessary
3701 
3702   Fortran Notes:
3703   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3704 
3705   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3706 
3707   Level: beginner
3708 
3709 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3710 @*/
3711 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3712 {
3713   PetscFunctionBeginHot;
3714   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3715   if (numPoints) *numPoints = 0;
3716   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3717   PetscFunctionReturn(0);
3718 }
3719 
3720 /*@
3721   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3722 
3723   Not collective
3724 
3725   Input Parameter:
3726 . mesh - The DMPlex
3727 
3728   Output Parameters:
3729 + maxConeSize - The maximum number of in-edges
3730 - maxSupportSize - The maximum number of out-edges
3731 
3732   Level: beginner
3733 
3734 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3735 @*/
3736 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3737 {
3738   DM_Plex *mesh = (DM_Plex*) dm->data;
3739 
3740   PetscFunctionBegin;
3741   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3742   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3743   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3744   PetscFunctionReturn(0);
3745 }
3746 
3747 PetscErrorCode DMSetUp_Plex(DM dm)
3748 {
3749   DM_Plex       *mesh = (DM_Plex*) dm->data;
3750   PetscInt       size, maxSupportSize;
3751 
3752   PetscFunctionBegin;
3753   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3754   PetscCall(PetscSectionSetUp(mesh->coneSection));
3755   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3756   PetscCall(PetscMalloc1(size, &mesh->cones));
3757   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3758   PetscCall(PetscLogObjectMemory((PetscObject) dm, size*2*sizeof(PetscInt)));
3759   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3760   if (maxSupportSize) {
3761     PetscCall(PetscSectionSetUp(mesh->supportSection));
3762     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3763     PetscCall(PetscMalloc1(size, &mesh->supports));
3764     PetscCall(PetscLogObjectMemory((PetscObject) dm, size*sizeof(PetscInt)));
3765   }
3766   PetscFunctionReturn(0);
3767 }
3768 
3769 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3770 {
3771   PetscFunctionBegin;
3772   if (subdm) PetscCall(DMClone(dm, subdm));
3773   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3774   if (subdm) {(*subdm)->useNatural = dm->useNatural;}
3775   if (dm->useNatural && dm->sfMigration) {
3776     PetscSF        sfNatural;
3777 
3778     (*subdm)->sfMigration = dm->sfMigration;
3779     PetscCall(PetscObjectReference((PetscObject) dm->sfMigration));
3780     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3781     (*subdm)->sfNatural = sfNatural;
3782   }
3783   PetscFunctionReturn(0);
3784 }
3785 
3786 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3787 {
3788   PetscInt       i = 0;
3789 
3790   PetscFunctionBegin;
3791   PetscCall(DMClone(dms[0], superdm));
3792   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3793   (*superdm)->useNatural = PETSC_FALSE;
3794   for (i = 0; i < len; i++) {
3795     if (dms[i]->useNatural && dms[i]->sfMigration) {
3796       PetscSF        sfNatural;
3797 
3798       (*superdm)->sfMigration = dms[i]->sfMigration;
3799       PetscCall(PetscObjectReference((PetscObject) dms[i]->sfMigration));
3800       (*superdm)->useNatural = PETSC_TRUE;
3801       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3802       (*superdm)->sfNatural = sfNatural;
3803       break;
3804     }
3805   }
3806   PetscFunctionReturn(0);
3807 }
3808 
3809 /*@
3810   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3811 
3812   Not collective
3813 
3814   Input Parameter:
3815 . mesh - The DMPlex
3816 
3817   Output Parameter:
3818 
3819   Note:
3820   This should be called after all calls to DMPlexSetCone()
3821 
3822   Level: beginner
3823 
3824 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3825 @*/
3826 PetscErrorCode DMPlexSymmetrize(DM dm)
3827 {
3828   DM_Plex       *mesh = (DM_Plex*) dm->data;
3829   PetscInt      *offsets;
3830   PetscInt       supportSize;
3831   PetscInt       pStart, pEnd, p;
3832 
3833   PetscFunctionBegin;
3834   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3835   PetscCheck(!mesh->supports,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3836   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0));
3837   /* Calculate support sizes */
3838   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3839   for (p = pStart; p < pEnd; ++p) {
3840     PetscInt dof, off, c;
3841 
3842     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3843     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3844     for (c = off; c < off+dof; ++c) {
3845       PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3846     }
3847   }
3848   PetscCall(PetscSectionSetUp(mesh->supportSection));
3849   /* Calculate supports */
3850   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3851   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3852   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3853   for (p = pStart; p < pEnd; ++p) {
3854     PetscInt dof, off, c;
3855 
3856     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3857     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3858     for (c = off; c < off+dof; ++c) {
3859       const PetscInt q = mesh->cones[c];
3860       PetscInt       offS;
3861 
3862       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3863 
3864       mesh->supports[offS+offsets[q]] = p;
3865       ++offsets[q];
3866     }
3867   }
3868   PetscCall(PetscFree(offsets));
3869   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0));
3870   PetscFunctionReturn(0);
3871 }
3872 
3873 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3874 {
3875   IS             stratumIS;
3876 
3877   PetscFunctionBegin;
3878   if (pStart >= pEnd) PetscFunctionReturn(0);
3879   if (PetscDefined(USE_DEBUG)) {
3880     PetscInt  qStart, qEnd, numLevels, level;
3881     PetscBool overlap = PETSC_FALSE;
3882     PetscCall(DMLabelGetNumValues(label, &numLevels));
3883     for (level = 0; level < numLevels; level++) {
3884       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3885       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
3886     }
3887     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);
3888   }
3889   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS));
3890   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3891   PetscCall(ISDestroy(&stratumIS));
3892   PetscFunctionReturn(0);
3893 }
3894 
3895 /*@
3896   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3897   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3898   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3899   the DAG.
3900 
3901   Collective on dm
3902 
3903   Input Parameter:
3904 . mesh - The DMPlex
3905 
3906   Output Parameter:
3907 
3908   Notes:
3909   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3910   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3911   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3912   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3913   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3914 
3915   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3916   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3917   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
3918   to interpolate only that one (e0), so that
3919 $  cone(c0) = {e0, v2}
3920 $  cone(e0) = {v0, v1}
3921   If DMPlexStratify() is run on this mesh, it will give depths
3922 $  depth 0 = {v0, v1, v2}
3923 $  depth 1 = {e0, c0}
3924   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3925 
3926   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3927 
3928   Level: beginner
3929 
3930 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3931 @*/
3932 PetscErrorCode DMPlexStratify(DM dm)
3933 {
3934   DM_Plex       *mesh = (DM_Plex*) dm->data;
3935   DMLabel        label;
3936   PetscInt       pStart, pEnd, p;
3937   PetscInt       numRoots = 0, numLeaves = 0;
3938 
3939   PetscFunctionBegin;
3940   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3941   PetscCall(PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0));
3942 
3943   /* Create depth label */
3944   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3945   PetscCall(DMCreateLabel(dm, "depth"));
3946   PetscCall(DMPlexGetDepthLabel(dm, &label));
3947 
3948   {
3949     /* Initialize roots and count leaves */
3950     PetscInt sMin = PETSC_MAX_INT;
3951     PetscInt sMax = PETSC_MIN_INT;
3952     PetscInt coneSize, supportSize;
3953 
3954     for (p = pStart; p < pEnd; ++p) {
3955       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3956       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3957       if (!coneSize && supportSize) {
3958         sMin = PetscMin(p, sMin);
3959         sMax = PetscMax(p, sMax);
3960         ++numRoots;
3961       } else if (!supportSize && coneSize) {
3962         ++numLeaves;
3963       } else if (!supportSize && !coneSize) {
3964         /* Isolated points */
3965         sMin = PetscMin(p, sMin);
3966         sMax = PetscMax(p, sMax);
3967       }
3968     }
3969     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1));
3970   }
3971 
3972   if (numRoots + numLeaves == (pEnd - pStart)) {
3973     PetscInt sMin = PETSC_MAX_INT;
3974     PetscInt sMax = PETSC_MIN_INT;
3975     PetscInt coneSize, supportSize;
3976 
3977     for (p = pStart; p < pEnd; ++p) {
3978       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3979       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3980       if (!supportSize && coneSize) {
3981         sMin = PetscMin(p, sMin);
3982         sMax = PetscMax(p, sMax);
3983       }
3984     }
3985     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1));
3986   } else {
3987     PetscInt level = 0;
3988     PetscInt qStart, qEnd, q;
3989 
3990     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3991     while (qEnd > qStart) {
3992       PetscInt sMin = PETSC_MAX_INT;
3993       PetscInt sMax = PETSC_MIN_INT;
3994 
3995       for (q = qStart; q < qEnd; ++q) {
3996         const PetscInt *support;
3997         PetscInt        supportSize, s;
3998 
3999         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4000         PetscCall(DMPlexGetSupport(dm, q, &support));
4001         for (s = 0; s < supportSize; ++s) {
4002           sMin = PetscMin(support[s], sMin);
4003           sMax = PetscMax(support[s], sMax);
4004         }
4005       }
4006       PetscCall(DMLabelGetNumValues(label, &level));
4007       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1));
4008       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4009     }
4010   }
4011   { /* just in case there is an empty process */
4012     PetscInt numValues, maxValues = 0, v;
4013 
4014     PetscCall(DMLabelGetNumValues(label, &numValues));
4015     PetscCallMPI(MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm)));
4016     for (v = numValues; v < maxValues; v++) {
4017       PetscCall(DMLabelAddStratum(label, v));
4018     }
4019   }
4020   PetscCall(PetscObjectStateGet((PetscObject) label, &mesh->depthState));
4021   PetscCall(PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0));
4022   PetscFunctionReturn(0);
4023 }
4024 
4025 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4026 {
4027   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4028   PetscInt       dim, depth, pheight, coneSize;
4029 
4030   PetscFunctionBeginHot;
4031   PetscCall(DMGetDimension(dm, &dim));
4032   PetscCall(DMPlexGetDepth(dm, &depth));
4033   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4034   pheight = depth - pdepth;
4035   if (depth <= 1) {
4036     switch (pdepth) {
4037       case 0: ct = DM_POLYTOPE_POINT;break;
4038       case 1:
4039         switch (coneSize) {
4040           case 2: ct = DM_POLYTOPE_SEGMENT;break;
4041           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4042           case 4:
4043           switch (dim) {
4044             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
4045             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
4046             default: break;
4047           }
4048           break;
4049         case 5: ct = DM_POLYTOPE_PYRAMID;break;
4050         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4051         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
4052         default: break;
4053       }
4054     }
4055   } else {
4056     if (pdepth == 0) {
4057       ct = DM_POLYTOPE_POINT;
4058     } else if (pheight == 0) {
4059       switch (dim) {
4060         case 1:
4061           switch (coneSize) {
4062             case 2: ct = DM_POLYTOPE_SEGMENT;break;
4063             default: break;
4064           }
4065           break;
4066         case 2:
4067           switch (coneSize) {
4068             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4069             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4070             default: break;
4071           }
4072           break;
4073         case 3:
4074           switch (coneSize) {
4075             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
4076             case 5:
4077             {
4078               const PetscInt *cone;
4079               PetscInt        faceConeSize;
4080 
4081               PetscCall(DMPlexGetCone(dm, p, &cone));
4082               PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4083               switch (faceConeSize) {
4084                 case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4085                 case 4: ct = DM_POLYTOPE_PYRAMID;break;
4086               }
4087             }
4088             break;
4089             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
4090             default: break;
4091           }
4092           break;
4093         default: break;
4094       }
4095     } else if (pheight > 0) {
4096       switch (coneSize) {
4097         case 2: ct = DM_POLYTOPE_SEGMENT;break;
4098         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4099         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4100         default: break;
4101       }
4102     }
4103   }
4104   *pt = ct;
4105   PetscFunctionReturn(0);
4106 }
4107 
4108 /*@
4109   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4110 
4111   Collective on dm
4112 
4113   Input Parameter:
4114 . mesh - The DMPlex
4115 
4116   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4117 
4118   Level: developer
4119 
4120   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4121   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4122   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4123 
4124 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4125 @*/
4126 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4127 {
4128   DM_Plex       *mesh;
4129   DMLabel        ctLabel;
4130   PetscInt       pStart, pEnd, p;
4131 
4132   PetscFunctionBegin;
4133   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4134   mesh = (DM_Plex *) dm->data;
4135   PetscCall(DMCreateLabel(dm, "celltype"));
4136   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4137   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4138   for (p = pStart; p < pEnd; ++p) {
4139     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4140     PetscInt       pdepth;
4141 
4142     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4143     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4144     PetscCheck(ct != DM_POLYTOPE_UNKNOWN,PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4145     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4146   }
4147   PetscCall(PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState));
4148   PetscCall(PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view"));
4149   PetscFunctionReturn(0);
4150 }
4151 
4152 /*@C
4153   DMPlexGetJoin - Get an array for the join of the set of points
4154 
4155   Not Collective
4156 
4157   Input Parameters:
4158 + dm - The DMPlex object
4159 . numPoints - The number of input points for the join
4160 - points - The input points
4161 
4162   Output Parameters:
4163 + numCoveredPoints - The number of points in the join
4164 - coveredPoints - The points in the join
4165 
4166   Level: intermediate
4167 
4168   Note: Currently, this is restricted to a single level join
4169 
4170   Fortran Notes:
4171   Since it returns an array, this routine is only available in Fortran 90, and you must
4172   include petsc.h90 in your code.
4173 
4174   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4175 
4176 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4177 @*/
4178 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4179 {
4180   DM_Plex       *mesh = (DM_Plex*) dm->data;
4181   PetscInt      *join[2];
4182   PetscInt       joinSize, i = 0;
4183   PetscInt       dof, off, p, c, m;
4184   PetscInt       maxSupportSize;
4185 
4186   PetscFunctionBegin;
4187   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4188   PetscValidIntPointer(points, 3);
4189   PetscValidIntPointer(numCoveredPoints, 4);
4190   PetscValidPointer(coveredPoints, 5);
4191   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4192   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4193   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4194   /* Copy in support of first point */
4195   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4196   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4197   for (joinSize = 0; joinSize < dof; ++joinSize) {
4198     join[i][joinSize] = mesh->supports[off+joinSize];
4199   }
4200   /* Check each successive support */
4201   for (p = 1; p < numPoints; ++p) {
4202     PetscInt newJoinSize = 0;
4203 
4204     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4205     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4206     for (c = 0; c < dof; ++c) {
4207       const PetscInt point = mesh->supports[off+c];
4208 
4209       for (m = 0; m < joinSize; ++m) {
4210         if (point == join[i][m]) {
4211           join[1-i][newJoinSize++] = point;
4212           break;
4213         }
4214       }
4215     }
4216     joinSize = newJoinSize;
4217     i        = 1-i;
4218   }
4219   *numCoveredPoints = joinSize;
4220   *coveredPoints    = join[i];
4221   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1-i]));
4222   PetscFunctionReturn(0);
4223 }
4224 
4225 /*@C
4226   DMPlexRestoreJoin - Restore an array for the join of the set of points
4227 
4228   Not Collective
4229 
4230   Input Parameters:
4231 + dm - The DMPlex object
4232 . numPoints - The number of input points for the join
4233 - points - The input points
4234 
4235   Output Parameters:
4236 + numCoveredPoints - The number of points in the join
4237 - coveredPoints - The points in the join
4238 
4239   Fortran Notes:
4240   Since it returns an array, this routine is only available in Fortran 90, and you must
4241   include petsc.h90 in your code.
4242 
4243   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4244 
4245   Level: intermediate
4246 
4247 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4248 @*/
4249 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4250 {
4251   PetscFunctionBegin;
4252   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4253   if (points) PetscValidIntPointer(points,3);
4254   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4255   PetscValidPointer(coveredPoints, 5);
4256   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4257   if (numCoveredPoints) *numCoveredPoints = 0;
4258   PetscFunctionReturn(0);
4259 }
4260 
4261 /*@C
4262   DMPlexGetFullJoin - Get an array for the join of the set of points
4263 
4264   Not Collective
4265 
4266   Input Parameters:
4267 + dm - The DMPlex object
4268 . numPoints - The number of input points for the join
4269 - points - The input points
4270 
4271   Output Parameters:
4272 + numCoveredPoints - The number of points in the join
4273 - coveredPoints - The points in the join
4274 
4275   Fortran Notes:
4276   Since it returns an array, this routine is only available in Fortran 90, and you must
4277   include petsc.h90 in your code.
4278 
4279   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4280 
4281   Level: intermediate
4282 
4283 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4284 @*/
4285 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4286 {
4287   PetscInt      *offsets, **closures;
4288   PetscInt      *join[2];
4289   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
4290   PetscInt       p, d, c, m, ms;
4291 
4292   PetscFunctionBegin;
4293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4294   PetscValidIntPointer(points, 3);
4295   PetscValidIntPointer(numCoveredPoints, 4);
4296   PetscValidPointer(coveredPoints, 5);
4297 
4298   PetscCall(DMPlexGetDepth(dm, &depth));
4299   PetscCall(PetscCalloc1(numPoints, &closures));
4300   PetscCall(DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4301   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4302   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
4303   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4304   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4305 
4306   for (p = 0; p < numPoints; ++p) {
4307     PetscInt closureSize;
4308 
4309     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4310 
4311     offsets[p*(depth+2)+0] = 0;
4312     for (d = 0; d < depth+1; ++d) {
4313       PetscInt pStart, pEnd, i;
4314 
4315       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4316       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
4317         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4318           offsets[p*(depth+2)+d+1] = i;
4319           break;
4320         }
4321       }
4322       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
4323     }
4324     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);
4325   }
4326   for (d = 0; d < depth+1; ++d) {
4327     PetscInt dof;
4328 
4329     /* Copy in support of first point */
4330     dof = offsets[d+1] - offsets[d];
4331     for (joinSize = 0; joinSize < dof; ++joinSize) {
4332       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
4333     }
4334     /* Check each successive cone */
4335     for (p = 1; p < numPoints && joinSize; ++p) {
4336       PetscInt newJoinSize = 0;
4337 
4338       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
4339       for (c = 0; c < dof; ++c) {
4340         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
4341 
4342         for (m = 0; m < joinSize; ++m) {
4343           if (point == join[i][m]) {
4344             join[1-i][newJoinSize++] = point;
4345             break;
4346           }
4347         }
4348       }
4349       joinSize = newJoinSize;
4350       i        = 1-i;
4351     }
4352     if (joinSize) break;
4353   }
4354   *numCoveredPoints = joinSize;
4355   *coveredPoints    = join[i];
4356   for (p = 0; p < numPoints; ++p) {
4357     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4358   }
4359   PetscCall(PetscFree(closures));
4360   PetscCall(DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4361   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1-i]));
4362   PetscFunctionReturn(0);
4363 }
4364 
4365 /*@C
4366   DMPlexGetMeet - Get an array for the meet of the set of points
4367 
4368   Not Collective
4369 
4370   Input Parameters:
4371 + dm - The DMPlex object
4372 . numPoints - The number of input points for the meet
4373 - points - The input points
4374 
4375   Output Parameters:
4376 + numCoveredPoints - The number of points in the meet
4377 - coveredPoints - The points in the meet
4378 
4379   Level: intermediate
4380 
4381   Note: Currently, this is restricted to a single level meet
4382 
4383   Fortran Notes:
4384   Since it returns an array, this routine is only available in Fortran 90, and you must
4385   include petsc.h90 in your code.
4386 
4387   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4388 
4389 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4390 @*/
4391 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4392 {
4393   DM_Plex       *mesh = (DM_Plex*) dm->data;
4394   PetscInt      *meet[2];
4395   PetscInt       meetSize, i = 0;
4396   PetscInt       dof, off, p, c, m;
4397   PetscInt       maxConeSize;
4398 
4399   PetscFunctionBegin;
4400   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4401   PetscValidIntPointer(points, 3);
4402   PetscValidIntPointer(numCoveringPoints, 4);
4403   PetscValidPointer(coveringPoints, 5);
4404   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4405   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4406   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4407   /* Copy in cone of first point */
4408   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4409   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4410   for (meetSize = 0; meetSize < dof; ++meetSize) {
4411     meet[i][meetSize] = mesh->cones[off+meetSize];
4412   }
4413   /* Check each successive cone */
4414   for (p = 1; p < numPoints; ++p) {
4415     PetscInt newMeetSize = 0;
4416 
4417     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4418     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4419     for (c = 0; c < dof; ++c) {
4420       const PetscInt point = mesh->cones[off+c];
4421 
4422       for (m = 0; m < meetSize; ++m) {
4423         if (point == meet[i][m]) {
4424           meet[1-i][newMeetSize++] = point;
4425           break;
4426         }
4427       }
4428     }
4429     meetSize = newMeetSize;
4430     i        = 1-i;
4431   }
4432   *numCoveringPoints = meetSize;
4433   *coveringPoints    = meet[i];
4434   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1-i]));
4435   PetscFunctionReturn(0);
4436 }
4437 
4438 /*@C
4439   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4440 
4441   Not Collective
4442 
4443   Input Parameters:
4444 + dm - The DMPlex object
4445 . numPoints - The number of input points for the meet
4446 - points - The input points
4447 
4448   Output Parameters:
4449 + numCoveredPoints - The number of points in the meet
4450 - coveredPoints - The points in the meet
4451 
4452   Level: intermediate
4453 
4454   Fortran Notes:
4455   Since it returns an array, this routine is only available in Fortran 90, and you must
4456   include petsc.h90 in your code.
4457 
4458   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4459 
4460 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4461 @*/
4462 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4463 {
4464   PetscFunctionBegin;
4465   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4466   if (points) PetscValidIntPointer(points,3);
4467   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4468   PetscValidPointer(coveredPoints,5);
4469   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4470   if (numCoveredPoints) *numCoveredPoints = 0;
4471   PetscFunctionReturn(0);
4472 }
4473 
4474 /*@C
4475   DMPlexGetFullMeet - Get an array for the meet of the set of points
4476 
4477   Not Collective
4478 
4479   Input Parameters:
4480 + dm - The DMPlex object
4481 . numPoints - The number of input points for the meet
4482 - points - The input points
4483 
4484   Output Parameters:
4485 + numCoveredPoints - The number of points in the meet
4486 - coveredPoints - The points in the meet
4487 
4488   Level: intermediate
4489 
4490   Fortran Notes:
4491   Since it returns an array, this routine is only available in Fortran 90, and you must
4492   include petsc.h90 in your code.
4493 
4494   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4495 
4496 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4497 @*/
4498 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4499 {
4500   PetscInt      *offsets, **closures;
4501   PetscInt      *meet[2];
4502   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
4503   PetscInt       p, h, c, m, mc;
4504 
4505   PetscFunctionBegin;
4506   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4507   PetscValidIntPointer(points, 3);
4508   PetscValidIntPointer(numCoveredPoints, 4);
4509   PetscValidPointer(coveredPoints, 5);
4510 
4511   PetscCall(DMPlexGetDepth(dm, &height));
4512   PetscCall(PetscMalloc1(numPoints, &closures));
4513   PetscCall(DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4514   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4515   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
4516   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4517   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4518 
4519   for (p = 0; p < numPoints; ++p) {
4520     PetscInt closureSize;
4521 
4522     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4523 
4524     offsets[p*(height+2)+0] = 0;
4525     for (h = 0; h < height+1; ++h) {
4526       PetscInt pStart, pEnd, i;
4527 
4528       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4529       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
4530         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4531           offsets[p*(height+2)+h+1] = i;
4532           break;
4533         }
4534       }
4535       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
4536     }
4537     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);
4538   }
4539   for (h = 0; h < height+1; ++h) {
4540     PetscInt dof;
4541 
4542     /* Copy in cone of first point */
4543     dof = offsets[h+1] - offsets[h];
4544     for (meetSize = 0; meetSize < dof; ++meetSize) {
4545       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
4546     }
4547     /* Check each successive cone */
4548     for (p = 1; p < numPoints && meetSize; ++p) {
4549       PetscInt newMeetSize = 0;
4550 
4551       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
4552       for (c = 0; c < dof; ++c) {
4553         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
4554 
4555         for (m = 0; m < meetSize; ++m) {
4556           if (point == meet[i][m]) {
4557             meet[1-i][newMeetSize++] = point;
4558             break;
4559           }
4560         }
4561       }
4562       meetSize = newMeetSize;
4563       i        = 1-i;
4564     }
4565     if (meetSize) break;
4566   }
4567   *numCoveredPoints = meetSize;
4568   *coveredPoints    = meet[i];
4569   for (p = 0; p < numPoints; ++p) {
4570     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4571   }
4572   PetscCall(PetscFree(closures));
4573   PetscCall(DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4574   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1-i]));
4575   PetscFunctionReturn(0);
4576 }
4577 
4578 /*@C
4579   DMPlexEqual - Determine if two DMs have the same topology
4580 
4581   Not Collective
4582 
4583   Input Parameters:
4584 + dmA - A DMPlex object
4585 - dmB - A DMPlex object
4586 
4587   Output Parameters:
4588 . equal - PETSC_TRUE if the topologies are identical
4589 
4590   Level: intermediate
4591 
4592   Notes:
4593   We are not solving graph isomorphism, so we do not permutation.
4594 
4595 .seealso: `DMPlexGetCone()`
4596 @*/
4597 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4598 {
4599   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4600 
4601   PetscFunctionBegin;
4602   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4603   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4604   PetscValidBoolPointer(equal, 3);
4605 
4606   *equal = PETSC_FALSE;
4607   PetscCall(DMPlexGetDepth(dmA, &depth));
4608   PetscCall(DMPlexGetDepth(dmB, &depthB));
4609   if (depth != depthB) PetscFunctionReturn(0);
4610   PetscCall(DMPlexGetChart(dmA, &pStart,  &pEnd));
4611   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4612   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4613   for (p = pStart; p < pEnd; ++p) {
4614     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4615     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4616 
4617     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4618     PetscCall(DMPlexGetCone(dmA, p, &cone));
4619     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4620     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4621     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4622     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4623     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4624     for (c = 0; c < coneSize; ++c) {
4625       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4626       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4627     }
4628     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4629     PetscCall(DMPlexGetSupport(dmA, p, &support));
4630     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4631     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4632     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4633     for (s = 0; s < supportSize; ++s) {
4634       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4635     }
4636   }
4637   *equal = PETSC_TRUE;
4638   PetscFunctionReturn(0);
4639 }
4640 
4641 /*@C
4642   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4643 
4644   Not Collective
4645 
4646   Input Parameters:
4647 + dm         - The DMPlex
4648 . cellDim    - The cell dimension
4649 - numCorners - The number of vertices on a cell
4650 
4651   Output Parameters:
4652 . numFaceVertices - The number of vertices on a face
4653 
4654   Level: developer
4655 
4656   Notes:
4657   Of course this can only work for a restricted set of symmetric shapes
4658 
4659 .seealso: `DMPlexGetCone()`
4660 @*/
4661 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4662 {
4663   MPI_Comm       comm;
4664 
4665   PetscFunctionBegin;
4666   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
4667   PetscValidIntPointer(numFaceVertices,4);
4668   switch (cellDim) {
4669   case 0:
4670     *numFaceVertices = 0;
4671     break;
4672   case 1:
4673     *numFaceVertices = 1;
4674     break;
4675   case 2:
4676     switch (numCorners) {
4677     case 3: /* triangle */
4678       *numFaceVertices = 2; /* Edge has 2 vertices */
4679       break;
4680     case 4: /* quadrilateral */
4681       *numFaceVertices = 2; /* Edge has 2 vertices */
4682       break;
4683     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
4684       *numFaceVertices = 3; /* Edge has 3 vertices */
4685       break;
4686     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4687       *numFaceVertices = 3; /* Edge has 3 vertices */
4688       break;
4689     default:
4690       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4691     }
4692     break;
4693   case 3:
4694     switch (numCorners) {
4695     case 4: /* tetradehdron */
4696       *numFaceVertices = 3; /* Face has 3 vertices */
4697       break;
4698     case 6: /* tet cohesive cells */
4699       *numFaceVertices = 4; /* Face has 4 vertices */
4700       break;
4701     case 8: /* hexahedron */
4702       *numFaceVertices = 4; /* Face has 4 vertices */
4703       break;
4704     case 9: /* tet cohesive Lagrange cells */
4705       *numFaceVertices = 6; /* Face has 6 vertices */
4706       break;
4707     case 10: /* quadratic tetrahedron */
4708       *numFaceVertices = 6; /* Face has 6 vertices */
4709       break;
4710     case 12: /* hex cohesive Lagrange cells */
4711       *numFaceVertices = 6; /* Face has 6 vertices */
4712       break;
4713     case 18: /* quadratic tet cohesive Lagrange cells */
4714       *numFaceVertices = 6; /* Face has 6 vertices */
4715       break;
4716     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4717       *numFaceVertices = 9; /* Face has 9 vertices */
4718       break;
4719     default:
4720       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4721     }
4722     break;
4723   default:
4724     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4725   }
4726   PetscFunctionReturn(0);
4727 }
4728 
4729 /*@
4730   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4731 
4732   Not Collective
4733 
4734   Input Parameter:
4735 . dm    - The DMPlex object
4736 
4737   Output Parameter:
4738 . depthLabel - The DMLabel recording point depth
4739 
4740   Level: developer
4741 
4742 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4743 @*/
4744 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4745 {
4746   PetscFunctionBegin;
4747   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4748   PetscValidPointer(depthLabel, 2);
4749   *depthLabel = dm->depthLabel;
4750   PetscFunctionReturn(0);
4751 }
4752 
4753 /*@
4754   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4755 
4756   Not Collective
4757 
4758   Input Parameter:
4759 . dm    - The DMPlex object
4760 
4761   Output Parameter:
4762 . depth - The number of strata (breadth first levels) in the DAG
4763 
4764   Level: developer
4765 
4766   Notes:
4767   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4768   The point depth is described more in detail in DMPlexGetDepthStratum().
4769   An empty mesh gives -1.
4770 
4771 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4772 @*/
4773 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4774 {
4775   DMLabel        label;
4776   PetscInt       d = 0;
4777 
4778   PetscFunctionBegin;
4779   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4780   PetscValidIntPointer(depth, 2);
4781   PetscCall(DMPlexGetDepthLabel(dm, &label));
4782   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4783   *depth = d-1;
4784   PetscFunctionReturn(0);
4785 }
4786 
4787 /*@
4788   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4789 
4790   Not Collective
4791 
4792   Input Parameters:
4793 + dm    - The DMPlex object
4794 - depth - The requested depth
4795 
4796   Output Parameters:
4797 + start - The first point at this depth
4798 - end   - One beyond the last point at this depth
4799 
4800   Notes:
4801   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4802   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4803   higher dimension, e.g., "edges".
4804 
4805   Level: developer
4806 
4807 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4808 @*/
4809 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4810 {
4811   DMLabel        label;
4812   PetscInt       pStart, pEnd;
4813 
4814   PetscFunctionBegin;
4815   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4816   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4817   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4818   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4819   if (pStart == pEnd) PetscFunctionReturn(0);
4820   if (depth < 0) {
4821     if (start) *start = pStart;
4822     if (end)   *end   = pEnd;
4823     PetscFunctionReturn(0);
4824   }
4825   PetscCall(DMPlexGetDepthLabel(dm, &label));
4826   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4827   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4828   PetscFunctionReturn(0);
4829 }
4830 
4831 /*@
4832   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4833 
4834   Not Collective
4835 
4836   Input Parameters:
4837 + dm     - The DMPlex object
4838 - height - The requested height
4839 
4840   Output Parameters:
4841 + start - The first point at this height
4842 - end   - One beyond the last point at this height
4843 
4844   Notes:
4845   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4846   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4847   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4848 
4849   Level: developer
4850 
4851 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4852 @*/
4853 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4854 {
4855   DMLabel        label;
4856   PetscInt       depth, pStart, pEnd;
4857 
4858   PetscFunctionBegin;
4859   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4860   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4861   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4862   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4863   if (pStart == pEnd) PetscFunctionReturn(0);
4864   if (height < 0) {
4865     if (start) *start = pStart;
4866     if (end)   *end   = pEnd;
4867     PetscFunctionReturn(0);
4868   }
4869   PetscCall(DMPlexGetDepthLabel(dm, &label));
4870   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4871   PetscCall(DMLabelGetNumValues(label, &depth));
4872   PetscCall(DMLabelGetStratumBounds(label, depth-1-height, start, end));
4873   PetscFunctionReturn(0);
4874 }
4875 
4876 /*@
4877   DMPlexGetPointDepth - Get the depth of a given point
4878 
4879   Not Collective
4880 
4881   Input Parameters:
4882 + dm    - The DMPlex object
4883 - point - The point
4884 
4885   Output Parameter:
4886 . depth - The depth of the point
4887 
4888   Level: intermediate
4889 
4890 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4891 @*/
4892 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4893 {
4894   PetscFunctionBegin;
4895   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4896   PetscValidIntPointer(depth, 3);
4897   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4898   PetscFunctionReturn(0);
4899 }
4900 
4901 /*@
4902   DMPlexGetPointHeight - Get the height of a given point
4903 
4904   Not Collective
4905 
4906   Input Parameters:
4907 + dm    - The DMPlex object
4908 - point - The point
4909 
4910   Output Parameter:
4911 . height - The height of the point
4912 
4913   Level: intermediate
4914 
4915 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4916 @*/
4917 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4918 {
4919   PetscInt       n, pDepth;
4920 
4921   PetscFunctionBegin;
4922   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4923   PetscValidIntPointer(height, 3);
4924   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4925   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4926   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
4927   PetscFunctionReturn(0);
4928 }
4929 
4930 /*@
4931   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4932 
4933   Not Collective
4934 
4935   Input Parameter:
4936 . dm - The DMPlex object
4937 
4938   Output Parameter:
4939 . celltypeLabel - The DMLabel recording cell polytope type
4940 
4941   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4942   DMCreateLabel(dm, "celltype") beforehand.
4943 
4944   Level: developer
4945 
4946 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4947 @*/
4948 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
4949 {
4950   PetscFunctionBegin;
4951   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4952   PetscValidPointer(celltypeLabel, 2);
4953   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4954   *celltypeLabel = dm->celltypeLabel;
4955   PetscFunctionReturn(0);
4956 }
4957 
4958 /*@
4959   DMPlexGetCellType - Get the polytope type of a given cell
4960 
4961   Not Collective
4962 
4963   Input Parameters:
4964 + dm   - The DMPlex object
4965 - cell - The cell
4966 
4967   Output Parameter:
4968 . celltype - The polytope type of the cell
4969 
4970   Level: intermediate
4971 
4972 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4973 @*/
4974 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
4975 {
4976   DMLabel        label;
4977   PetscInt       ct;
4978 
4979   PetscFunctionBegin;
4980   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4981   PetscValidPointer(celltype, 3);
4982   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4983   PetscCall(DMLabelGetValue(label, cell, &ct));
4984   PetscCheck(ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4985   *celltype = (DMPolytopeType) ct;
4986   PetscFunctionReturn(0);
4987 }
4988 
4989 /*@
4990   DMPlexSetCellType - Set the polytope type of a given cell
4991 
4992   Not Collective
4993 
4994   Input Parameters:
4995 + dm   - The DMPlex object
4996 . cell - The cell
4997 - celltype - The polytope type of the cell
4998 
4999   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
5000   is executed. This function will override the computed type. However, if automatic classification will not succeed
5001   and a user wants to manually specify all types, the classification must be disabled by calling
5002   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5003 
5004   Level: advanced
5005 
5006 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5007 @*/
5008 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5009 {
5010   DMLabel        label;
5011 
5012   PetscFunctionBegin;
5013   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5014   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5015   PetscCall(DMLabelSetValue(label, cell, celltype));
5016   PetscFunctionReturn(0);
5017 }
5018 
5019 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5020 {
5021   PetscSection   section, s;
5022   Mat            m;
5023   PetscInt       maxHeight;
5024 
5025   PetscFunctionBegin;
5026   PetscCall(DMClone(dm, cdm));
5027   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5028   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5029   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5030   PetscCall(DMSetLocalSection(*cdm, section));
5031   PetscCall(PetscSectionDestroy(&section));
5032   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5033   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5034   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5035   PetscCall(PetscSectionDestroy(&s));
5036   PetscCall(MatDestroy(&m));
5037 
5038   PetscCall(DMSetNumFields(*cdm, 1));
5039   PetscCall(DMCreateDS(*cdm));
5040   PetscFunctionReturn(0);
5041 }
5042 
5043 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5044 {
5045   Vec coordsLocal, cellCoordsLocal;
5046   DM  coordsDM,    cellCoordsDM;
5047 
5048   PetscFunctionBegin;
5049   *field = NULL;
5050   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5051   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5052   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5053   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5054   if (coordsLocal && coordsDM) {
5055     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5056     else                                 PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5057   }
5058   PetscFunctionReturn(0);
5059 }
5060 
5061 /*@C
5062   DMPlexGetConeSection - Return a section which describes the layout of cone data
5063 
5064   Not Collective
5065 
5066   Input Parameters:
5067 . dm        - The DMPlex object
5068 
5069   Output Parameter:
5070 . section - The PetscSection object
5071 
5072   Level: developer
5073 
5074 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5075 @*/
5076 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5077 {
5078   DM_Plex *mesh = (DM_Plex*) dm->data;
5079 
5080   PetscFunctionBegin;
5081   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5082   if (section) *section = mesh->coneSection;
5083   PetscFunctionReturn(0);
5084 }
5085 
5086 /*@C
5087   DMPlexGetSupportSection - Return a section which describes the layout of support data
5088 
5089   Not Collective
5090 
5091   Input Parameters:
5092 . dm        - The DMPlex object
5093 
5094   Output Parameter:
5095 . section - The PetscSection object
5096 
5097   Level: developer
5098 
5099 .seealso: `DMPlexGetConeSection()`
5100 @*/
5101 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5102 {
5103   DM_Plex *mesh = (DM_Plex*) dm->data;
5104 
5105   PetscFunctionBegin;
5106   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5107   if (section) *section = mesh->supportSection;
5108   PetscFunctionReturn(0);
5109 }
5110 
5111 /*@C
5112   DMPlexGetCones - Return cone data
5113 
5114   Not Collective
5115 
5116   Input Parameters:
5117 . dm        - The DMPlex object
5118 
5119   Output Parameter:
5120 . cones - The cone for each point
5121 
5122   Level: developer
5123 
5124 .seealso: `DMPlexGetConeSection()`
5125 @*/
5126 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5127 {
5128   DM_Plex *mesh = (DM_Plex*) dm->data;
5129 
5130   PetscFunctionBegin;
5131   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5132   if (cones) *cones = mesh->cones;
5133   PetscFunctionReturn(0);
5134 }
5135 
5136 /*@C
5137   DMPlexGetConeOrientations - Return cone orientation data
5138 
5139   Not Collective
5140 
5141   Input Parameters:
5142 . dm        - The DMPlex object
5143 
5144   Output Parameter:
5145 . coneOrientations - The array of cone orientations for all points
5146 
5147   Level: developer
5148 
5149   Notes:
5150   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5151 
5152   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5153 
5154 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5155 @*/
5156 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5157 {
5158   DM_Plex *mesh = (DM_Plex*) dm->data;
5159 
5160   PetscFunctionBegin;
5161   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5162   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5163   PetscFunctionReturn(0);
5164 }
5165 
5166 /******************************** FEM Support **********************************/
5167 
5168 /*
5169  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5170  representing a line in the section.
5171 */
5172 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
5173 {
5174   PetscFunctionBeginHot;
5175   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5176   if (line < 0) {
5177     *k = 0;
5178     *Nc = 0;
5179   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
5180     *k = 1;
5181   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
5182     /* An order k SEM disc has k-1 dofs on an edge */
5183     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5184     *k = *k / *Nc + 1;
5185   }
5186   PetscFunctionReturn(0);
5187 }
5188 
5189 /*@
5190 
5191   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5192   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5193   section provided (or the section of the DM).
5194 
5195   Input Parameters:
5196 + dm      - The DM
5197 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5198 - section - The PetscSection to reorder, or NULL for the default section
5199 
5200   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5201   degree of the basis.
5202 
5203   Example:
5204   A typical interpolated single-quad mesh might order points as
5205 .vb
5206   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5207 
5208   v4 -- e6 -- v3
5209   |           |
5210   e7    c0    e8
5211   |           |
5212   v1 -- e5 -- v2
5213 .ve
5214 
5215   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5216   dofs in the order of points, e.g.,
5217 .vb
5218     c0 -> [0,1,2,3]
5219     v1 -> [4]
5220     ...
5221     e5 -> [8, 9]
5222 .ve
5223 
5224   which corresponds to the dofs
5225 .vb
5226     6   10  11  7
5227     13  2   3   15
5228     12  0   1   14
5229     4   8   9   5
5230 .ve
5231 
5232   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5233 .vb
5234   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5235 .ve
5236 
5237   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5238 .vb
5239    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5240 .ve
5241 
5242   Level: developer
5243 
5244 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5245 @*/
5246 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5247 {
5248   DMLabel        label;
5249   PetscInt       dim, depth = -1, eStart = -1, Nf;
5250   PetscBool      vertexchart;
5251 
5252   PetscFunctionBegin;
5253   PetscCall(DMGetDimension(dm, &dim));
5254   if (dim < 1) PetscFunctionReturn(0);
5255   if (point < 0) {
5256     PetscInt sStart,sEnd;
5257 
5258     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5259     point = sEnd-sStart ? sStart : point;
5260   }
5261   PetscCall(DMPlexGetDepthLabel(dm, &label));
5262   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5263   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5264   if (depth == 1) {eStart = point;}
5265   else if  (depth == dim) {
5266     const PetscInt *cone;
5267 
5268     PetscCall(DMPlexGetCone(dm, point, &cone));
5269     if (dim == 2) eStart = cone[0];
5270     else if (dim == 3) {
5271       const PetscInt *cone2;
5272       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5273       eStart = cone2[0];
5274     } 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);
5275   } 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);
5276   {                             /* Determine whether the chart covers all points or just vertices. */
5277     PetscInt pStart,pEnd,cStart,cEnd;
5278     PetscCall(DMPlexGetDepthStratum(dm,0,&pStart,&pEnd));
5279     PetscCall(PetscSectionGetChart(section,&cStart,&cEnd));
5280     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5281     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5282     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5283   }
5284   PetscCall(PetscSectionGetNumFields(section, &Nf));
5285   for (PetscInt d=1; d<=dim; d++) {
5286     PetscInt k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5287     PetscInt *perm;
5288 
5289     for (f = 0; f < Nf; ++f) {
5290       PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5291       size += PetscPowInt(k+1, d)*Nc;
5292     }
5293     PetscCall(PetscMalloc1(size, &perm));
5294     for (f = 0; f < Nf; ++f) {
5295       switch (d) {
5296       case 1:
5297         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5298         /*
5299          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5300          We want              [ vtx0; edge of length k-1; vtx1 ]
5301          */
5302         for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
5303         for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
5304         for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
5305         foffset = offset;
5306         break;
5307       case 2:
5308         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5309         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5310         /* The SEM order is
5311 
5312          v_lb, {e_b}, v_rb,
5313          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5314          v_lt, reverse {e_t}, v_rt
5315          */
5316         {
5317           const PetscInt of   = 0;
5318           const PetscInt oeb  = of   + PetscSqr(k-1);
5319           const PetscInt oer  = oeb  + (k-1);
5320           const PetscInt oet  = oer  + (k-1);
5321           const PetscInt oel  = oet  + (k-1);
5322           const PetscInt ovlb = oel  + (k-1);
5323           const PetscInt ovrb = ovlb + 1;
5324           const PetscInt ovrt = ovrb + 1;
5325           const PetscInt ovlt = ovrt + 1;
5326           PetscInt       o;
5327 
5328           /* bottom */
5329           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
5330           for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5331           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
5332           /* middle */
5333           for (i = 0; i < k-1; ++i) {
5334             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
5335             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;
5336             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
5337           }
5338           /* top */
5339           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
5340           for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5341           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
5342           foffset = offset;
5343         }
5344         break;
5345       case 3:
5346         /* The original hex closure is
5347 
5348          {c,
5349          f_b, f_t, f_f, f_b, f_r, f_l,
5350          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5351          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5352          */
5353         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5354         /* The SEM order is
5355          Bottom Slice
5356          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5357          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5358          v_blb, {e_bb}, v_brb,
5359 
5360          Middle Slice (j)
5361          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5362          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5363          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5364 
5365          Top Slice
5366          v_tlf, {e_tf}, v_trf,
5367          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5368          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5369          */
5370         {
5371           const PetscInt oc    = 0;
5372           const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
5373           const PetscInt oft   = ofb   + PetscSqr(k-1);
5374           const PetscInt off   = oft   + PetscSqr(k-1);
5375           const PetscInt ofk   = off   + PetscSqr(k-1);
5376           const PetscInt ofr   = ofk   + PetscSqr(k-1);
5377           const PetscInt ofl   = ofr   + PetscSqr(k-1);
5378           const PetscInt oebl  = ofl   + PetscSqr(k-1);
5379           const PetscInt oebb  = oebl  + (k-1);
5380           const PetscInt oebr  = oebb  + (k-1);
5381           const PetscInt oebf  = oebr  + (k-1);
5382           const PetscInt oetf  = oebf  + (k-1);
5383           const PetscInt oetr  = oetf  + (k-1);
5384           const PetscInt oetb  = oetr  + (k-1);
5385           const PetscInt oetl  = oetb  + (k-1);
5386           const PetscInt oerf  = oetl  + (k-1);
5387           const PetscInt oelf  = oerf  + (k-1);
5388           const PetscInt oelb  = oelf  + (k-1);
5389           const PetscInt oerb  = oelb  + (k-1);
5390           const PetscInt ovblf = oerb  + (k-1);
5391           const PetscInt ovblb = ovblf + 1;
5392           const PetscInt ovbrb = ovblb + 1;
5393           const PetscInt ovbrf = ovbrb + 1;
5394           const PetscInt ovtlf = ovbrf + 1;
5395           const PetscInt ovtrf = ovtlf + 1;
5396           const PetscInt ovtrb = ovtrf + 1;
5397           const PetscInt ovtlb = ovtrb + 1;
5398           PetscInt       o, n;
5399 
5400           /* Bottom Slice */
5401           /*   bottom */
5402           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
5403           for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5404           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
5405           /*   middle */
5406           for (i = 0; i < k-1; ++i) {
5407             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
5408             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;}
5409             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
5410           }
5411           /*   top */
5412           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
5413           for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5414           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
5415 
5416           /* Middle Slice */
5417           for (j = 0; j < k-1; ++j) {
5418             /*   bottom */
5419             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
5420             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;
5421             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
5422             /*   middle */
5423             for (i = 0; i < k-1; ++i) {
5424               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
5425               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;
5426               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
5427             }
5428             /*   top */
5429             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
5430             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;
5431             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
5432           }
5433 
5434           /* Top Slice */
5435           /*   bottom */
5436           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
5437           for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5438           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
5439           /*   middle */
5440           for (i = 0; i < k-1; ++i) {
5441             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
5442             for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
5443             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
5444           }
5445           /*   top */
5446           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
5447           for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5448           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
5449 
5450           foffset = offset;
5451         }
5452         break;
5453       default: SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5454       }
5455     }
5456     PetscCheck(offset == size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5457     /* Check permutation */
5458     {
5459       PetscInt *check;
5460 
5461       PetscCall(PetscMalloc1(size, &check));
5462       for (i = 0; i < size; ++i) {
5463         check[i] = -1;
5464         PetscCheck(perm[i] >= 0 && perm[i] < size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5465       }
5466       for (i = 0; i < size; ++i) check[perm[i]] = i;
5467       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5468       PetscCall(PetscFree(check));
5469     }
5470     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size, PETSC_OWN_POINTER, perm));
5471     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5472       PetscInt *loc_perm;
5473       PetscCall(PetscMalloc1(size*2, &loc_perm));
5474       for (PetscInt i=0; i<size; i++) {
5475         loc_perm[i] = perm[i];
5476         loc_perm[size+i] = size + perm[i];
5477       }
5478       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size*2, PETSC_OWN_POINTER, loc_perm));
5479     }
5480   }
5481   PetscFunctionReturn(0);
5482 }
5483 
5484 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5485 {
5486   PetscDS        prob;
5487   PetscInt       depth, Nf, h;
5488   DMLabel        label;
5489 
5490   PetscFunctionBeginHot;
5491   PetscCall(DMGetDS(dm, &prob));
5492   Nf      = prob->Nf;
5493   label   = dm->depthLabel;
5494   *dspace = NULL;
5495   if (field < Nf) {
5496     PetscObject disc = prob->disc[field];
5497 
5498     if (disc->classid == PETSCFE_CLASSID) {
5499       PetscDualSpace dsp;
5500 
5501       PetscCall(PetscFEGetDualSpace((PetscFE)disc,&dsp));
5502       PetscCall(DMLabelGetNumValues(label,&depth));
5503       PetscCall(DMLabelGetValue(label,point,&h));
5504       h    = depth - 1 - h;
5505       if (h) {
5506         PetscCall(PetscDualSpaceGetHeightSubspace(dsp,h,dspace));
5507       } else {
5508         *dspace = dsp;
5509       }
5510     }
5511   }
5512   PetscFunctionReturn(0);
5513 }
5514 
5515 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5516 {
5517   PetscScalar    *array;
5518   const PetscScalar *vArray;
5519   const PetscInt *cone, *coneO;
5520   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
5521 
5522   PetscFunctionBeginHot;
5523   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5524   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5525   PetscCall(DMPlexGetCone(dm, point, &cone));
5526   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5527   if (!values || !*values) {
5528     if ((point >= pStart) && (point < pEnd)) {
5529       PetscInt dof;
5530 
5531       PetscCall(PetscSectionGetDof(section, point, &dof));
5532       size += dof;
5533     }
5534     for (p = 0; p < numPoints; ++p) {
5535       const PetscInt cp = cone[p];
5536       PetscInt       dof;
5537 
5538       if ((cp < pStart) || (cp >= pEnd)) continue;
5539       PetscCall(PetscSectionGetDof(section, cp, &dof));
5540       size += dof;
5541     }
5542     if (!values) {
5543       if (csize) *csize = size;
5544       PetscFunctionReturn(0);
5545     }
5546     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5547   } else {
5548     array = *values;
5549   }
5550   size = 0;
5551   PetscCall(VecGetArrayRead(v, &vArray));
5552   if ((point >= pStart) && (point < pEnd)) {
5553     PetscInt     dof, off, d;
5554     const PetscScalar *varr;
5555 
5556     PetscCall(PetscSectionGetDof(section, point, &dof));
5557     PetscCall(PetscSectionGetOffset(section, point, &off));
5558     varr = &vArray[off];
5559     for (d = 0; d < dof; ++d, ++offset) {
5560       array[offset] = varr[d];
5561     }
5562     size += dof;
5563   }
5564   for (p = 0; p < numPoints; ++p) {
5565     const PetscInt cp = cone[p];
5566     PetscInt       o  = coneO[p];
5567     PetscInt       dof, off, d;
5568     const PetscScalar *varr;
5569 
5570     if ((cp < pStart) || (cp >= pEnd)) continue;
5571     PetscCall(PetscSectionGetDof(section, cp, &dof));
5572     PetscCall(PetscSectionGetOffset(section, cp, &off));
5573     varr = &vArray[off];
5574     if (o >= 0) {
5575       for (d = 0; d < dof; ++d, ++offset) {
5576         array[offset] = varr[d];
5577       }
5578     } else {
5579       for (d = dof-1; d >= 0; --d, ++offset) {
5580         array[offset] = varr[d];
5581       }
5582     }
5583     size += dof;
5584   }
5585   PetscCall(VecRestoreArrayRead(v, &vArray));
5586   if (!*values) {
5587     if (csize) *csize = size;
5588     *values = array;
5589   } else {
5590     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5591     *csize = size;
5592   }
5593   PetscFunctionReturn(0);
5594 }
5595 
5596 /* Compress out points not in the section */
5597 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5598 {
5599   const PetscInt np = *numPoints;
5600   PetscInt       pStart, pEnd, p, q;
5601 
5602   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5603   for (p = 0, q = 0; p < np; ++p) {
5604     const PetscInt r = points[p*2];
5605     if ((r >= pStart) && (r < pEnd)) {
5606       points[q*2]   = r;
5607       points[q*2+1] = points[p*2+1];
5608       ++q;
5609     }
5610   }
5611   *numPoints = q;
5612   return 0;
5613 }
5614 
5615 /* Compressed closure does not apply closure permutation */
5616 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5617 {
5618   const PetscInt *cla = NULL;
5619   PetscInt       np, *pts = NULL;
5620 
5621   PetscFunctionBeginHot;
5622   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints));
5623   if (*clPoints) {
5624     PetscInt dof, off;
5625 
5626     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5627     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5628     PetscCall(ISGetIndices(*clPoints, &cla));
5629     np   = dof/2;
5630     pts  = (PetscInt *) &cla[off];
5631   } else {
5632     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5633     PetscCall(CompressPoints_Private(section, &np, pts));
5634   }
5635   *numPoints = np;
5636   *points    = pts;
5637   *clp       = cla;
5638   PetscFunctionReturn(0);
5639 }
5640 
5641 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5642 {
5643   PetscFunctionBeginHot;
5644   if (!*clPoints) {
5645     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5646   } else {
5647     PetscCall(ISRestoreIndices(*clPoints, clp));
5648   }
5649   *numPoints = 0;
5650   *points    = NULL;
5651   *clSec     = NULL;
5652   *clPoints  = NULL;
5653   *clp       = NULL;
5654   PetscFunctionReturn(0);
5655 }
5656 
5657 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5658 {
5659   PetscInt          offset = 0, p;
5660   const PetscInt    **perms = NULL;
5661   const PetscScalar **flips = NULL;
5662 
5663   PetscFunctionBeginHot;
5664   *size = 0;
5665   PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
5666   for (p = 0; p < numPoints; p++) {
5667     const PetscInt    point = points[2*p];
5668     const PetscInt    *perm = perms ? perms[p] : NULL;
5669     const PetscScalar *flip = flips ? flips[p] : NULL;
5670     PetscInt          dof, off, d;
5671     const PetscScalar *varr;
5672 
5673     PetscCall(PetscSectionGetDof(section, point, &dof));
5674     PetscCall(PetscSectionGetOffset(section, point, &off));
5675     varr = &vArray[off];
5676     if (clperm) {
5677       if (perm) {
5678         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5679       } else {
5680         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5681       }
5682       if (flip) {
5683         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5684       }
5685     } else {
5686       if (perm) {
5687         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5688       } else {
5689         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5690       }
5691       if (flip) {
5692         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5693       }
5694     }
5695     offset += dof;
5696   }
5697   PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
5698   *size = offset;
5699   PetscFunctionReturn(0);
5700 }
5701 
5702 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[])
5703 {
5704   PetscInt          offset = 0, f;
5705 
5706   PetscFunctionBeginHot;
5707   *size = 0;
5708   for (f = 0; f < numFields; ++f) {
5709     PetscInt          p;
5710     const PetscInt    **perms = NULL;
5711     const PetscScalar **flips = NULL;
5712 
5713     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5714     for (p = 0; p < numPoints; p++) {
5715       const PetscInt    point = points[2*p];
5716       PetscInt          fdof, foff, b;
5717       const PetscScalar *varr;
5718       const PetscInt    *perm = perms ? perms[p] : NULL;
5719       const PetscScalar *flip = flips ? flips[p] : NULL;
5720 
5721       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5722       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5723       varr = &vArray[foff];
5724       if (clperm) {
5725         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5726         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5727         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5728       } else {
5729         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5730         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5731         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5732       }
5733       offset += fdof;
5734     }
5735     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5736   }
5737   *size = offset;
5738   PetscFunctionReturn(0);
5739 }
5740 
5741 /*@C
5742   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5743 
5744   Not collective
5745 
5746   Input Parameters:
5747 + dm - The DM
5748 . section - The section describing the layout in v, or NULL to use the default section
5749 . v - The local vector
5750 - point - The point in the DM
5751 
5752   Input/Output Parameters:
5753 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5754 - values - An array to use for the values, or NULL to have it allocated automatically;
5755            if the user provided NULL, it is a borrowed array and should not be freed
5756 
5757 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5758 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5759 $ assembly function, and a user may already have allocated storage for this operation.
5760 $
5761 $ A typical use could be
5762 $
5763 $  values = NULL;
5764 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5765 $  for (cl = 0; cl < clSize; ++cl) {
5766 $    <Compute on closure>
5767 $  }
5768 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5769 $
5770 $ or
5771 $
5772 $  PetscMalloc1(clMaxSize, &values);
5773 $  for (p = pStart; p < pEnd; ++p) {
5774 $    clSize = clMaxSize;
5775 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5776 $    for (cl = 0; cl < clSize; ++cl) {
5777 $      <Compute on closure>
5778 $    }
5779 $  }
5780 $  PetscFree(values);
5781 
5782   Fortran Notes:
5783   Since it returns an array, this routine is only available in Fortran 90, and you must
5784   include petsc.h90 in your code.
5785 
5786   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5787 
5788   Level: intermediate
5789 
5790 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5791 @*/
5792 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5793 {
5794   PetscSection       clSection;
5795   IS                 clPoints;
5796   PetscInt          *points = NULL;
5797   const PetscInt    *clp, *perm;
5798   PetscInt           depth, numFields, numPoints, asize;
5799 
5800   PetscFunctionBeginHot;
5801   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5802   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5803   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5804   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5805   PetscCall(DMPlexGetDepth(dm, &depth));
5806   PetscCall(PetscSectionGetNumFields(section, &numFields));
5807   if (depth == 1 && numFields < 2) {
5808     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5809     PetscFunctionReturn(0);
5810   }
5811   /* Get points */
5812   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5813   /* Get sizes */
5814   asize = 0;
5815   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5816     PetscInt dof;
5817     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5818     asize += dof;
5819   }
5820   if (values) {
5821     const PetscScalar *vArray;
5822     PetscInt          size;
5823 
5824     if (*values) {
5825       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);
5826     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5827     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm));
5828     PetscCall(VecGetArrayRead(v, &vArray));
5829     /* Get values */
5830     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5831     else               PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5832     PetscCheck(asize == size,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5833     /* Cleanup array */
5834     PetscCall(VecRestoreArrayRead(v, &vArray));
5835   }
5836   if (csize) *csize = asize;
5837   /* Cleanup points */
5838   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5839   PetscFunctionReturn(0);
5840 }
5841 
5842 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5843 {
5844   DMLabel            depthLabel;
5845   PetscSection       clSection;
5846   IS                 clPoints;
5847   PetscScalar       *array;
5848   const PetscScalar *vArray;
5849   PetscInt          *points = NULL;
5850   const PetscInt    *clp, *perm = NULL;
5851   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5852 
5853   PetscFunctionBeginHot;
5854   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5855   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5856   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5857   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5858   PetscCall(DMPlexGetDepth(dm, &mdepth));
5859   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5860   PetscCall(PetscSectionGetNumFields(section, &numFields));
5861   if (mdepth == 1 && numFields < 2) {
5862     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5863     PetscFunctionReturn(0);
5864   }
5865   /* Get points */
5866   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5867   for (clsize=0,p=0; p<Np; p++) {
5868     PetscInt dof;
5869     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
5870     clsize += dof;
5871   }
5872   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm));
5873   /* Filter points */
5874   for (p = 0; p < numPoints*2; p += 2) {
5875     PetscInt dep;
5876 
5877     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5878     if (dep != depth) continue;
5879     points[Np*2+0] = points[p];
5880     points[Np*2+1] = points[p+1];
5881     ++Np;
5882   }
5883   /* Get array */
5884   if (!values || !*values) {
5885     PetscInt asize = 0, dof;
5886 
5887     for (p = 0; p < Np*2; p += 2) {
5888       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5889       asize += dof;
5890     }
5891     if (!values) {
5892       PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5893       if (csize) *csize = asize;
5894       PetscFunctionReturn(0);
5895     }
5896     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5897   } else {
5898     array = *values;
5899   }
5900   PetscCall(VecGetArrayRead(v, &vArray));
5901   /* Get values */
5902   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5903   else               PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5904   /* Cleanup points */
5905   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5906   /* Cleanup array */
5907   PetscCall(VecRestoreArrayRead(v, &vArray));
5908   if (!*values) {
5909     if (csize) *csize = size;
5910     *values = array;
5911   } else {
5912     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5913     *csize = size;
5914   }
5915   PetscFunctionReturn(0);
5916 }
5917 
5918 /*@C
5919   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5920 
5921   Not collective
5922 
5923   Input Parameters:
5924 + dm - The DM
5925 . section - The section describing the layout in v, or NULL to use the default section
5926 . v - The local vector
5927 . point - The point in the DM
5928 . csize - The number of values in the closure, or NULL
5929 - values - The array of values, which is a borrowed array and should not be freed
5930 
5931   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5932 
5933   Fortran Notes:
5934   Since it returns an array, this routine is only available in Fortran 90, and you must
5935   include petsc.h90 in your code.
5936 
5937   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5938 
5939   Level: intermediate
5940 
5941 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5942 @*/
5943 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5944 {
5945   PetscInt       size = 0;
5946 
5947   PetscFunctionBegin;
5948   /* Should work without recalculating size */
5949   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values));
5950   *values = NULL;
5951   PetscFunctionReturn(0);
5952 }
5953 
5954 static inline void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5955 static inline void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5956 
5957 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[])
5958 {
5959   PetscInt        cdof;   /* The number of constraints on this point */
5960   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5961   PetscScalar    *a;
5962   PetscInt        off, cind = 0, k;
5963 
5964   PetscFunctionBegin;
5965   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5966   PetscCall(PetscSectionGetOffset(section, point, &off));
5967   a    = &array[off];
5968   if (!cdof || setBC) {
5969     if (clperm) {
5970       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5971       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5972     } else {
5973       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5974       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5975     }
5976   } else {
5977     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5978     if (clperm) {
5979       if (perm) {for (k = 0; k < dof; ++k) {
5980           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5981           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5982         }
5983       } else {
5984         for (k = 0; k < dof; ++k) {
5985           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5986           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5987         }
5988       }
5989     } else {
5990       if (perm) {
5991         for (k = 0; k < dof; ++k) {
5992           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5993           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
5994         }
5995       } else {
5996         for (k = 0; k < dof; ++k) {
5997           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5998           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
5999         }
6000       }
6001     }
6002   }
6003   PetscFunctionReturn(0);
6004 }
6005 
6006 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[])
6007 {
6008   PetscInt        cdof;   /* The number of constraints on this point */
6009   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6010   PetscScalar    *a;
6011   PetscInt        off, cind = 0, k;
6012 
6013   PetscFunctionBegin;
6014   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6015   PetscCall(PetscSectionGetOffset(section, point, &off));
6016   a    = &array[off];
6017   if (cdof) {
6018     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6019     if (clperm) {
6020       if (perm) {
6021         for (k = 0; k < dof; ++k) {
6022           if ((cind < cdof) && (k == cdofs[cind])) {
6023             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
6024             cind++;
6025           }
6026         }
6027       } else {
6028         for (k = 0; k < dof; ++k) {
6029           if ((cind < cdof) && (k == cdofs[cind])) {
6030             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
6031             cind++;
6032           }
6033         }
6034       }
6035     } else {
6036       if (perm) {
6037         for (k = 0; k < dof; ++k) {
6038           if ((cind < cdof) && (k == cdofs[cind])) {
6039             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6040             cind++;
6041           }
6042         }
6043       } else {
6044         for (k = 0; k < dof; ++k) {
6045           if ((cind < cdof) && (k == cdofs[cind])) {
6046             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6047             cind++;
6048           }
6049         }
6050       }
6051     }
6052   }
6053   PetscFunctionReturn(0);
6054 }
6055 
6056 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[])
6057 {
6058   PetscScalar    *a;
6059   PetscInt        fdof, foff, fcdof, foffset = *offset;
6060   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6061   PetscInt        cind = 0, b;
6062 
6063   PetscFunctionBegin;
6064   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6065   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6066   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6067   a    = &array[foff];
6068   if (!fcdof || setBC) {
6069     if (clperm) {
6070       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
6071       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
6072     } else {
6073       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
6074       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
6075     }
6076   } else {
6077     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6078     if (clperm) {
6079       if (perm) {
6080         for (b = 0; b < fdof; b++) {
6081           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6082           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6083         }
6084       } else {
6085         for (b = 0; b < fdof; b++) {
6086           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6087           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6088         }
6089       }
6090     } else {
6091       if (perm) {
6092         for (b = 0; b < fdof; b++) {
6093           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6094           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6095         }
6096       } else {
6097         for (b = 0; b < fdof; b++) {
6098           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6099           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6100         }
6101       }
6102     }
6103   }
6104   *offset += fdof;
6105   PetscFunctionReturn(0);
6106 }
6107 
6108 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[])
6109 {
6110   PetscScalar    *a;
6111   PetscInt        fdof, foff, fcdof, foffset = *offset;
6112   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6113   PetscInt        Nc, cind = 0, ncind = 0, b;
6114   PetscBool       ncSet, fcSet;
6115 
6116   PetscFunctionBegin;
6117   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6118   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6119   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6120   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6121   a    = &array[foff];
6122   if (fcdof) {
6123     /* We just override fcdof and fcdofs with Ncc and comps */
6124     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6125     if (clperm) {
6126       if (perm) {
6127         if (comps) {
6128           for (b = 0; b < fdof; b++) {
6129             ncSet = fcSet = PETSC_FALSE;
6130             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6131             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6132             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
6133           }
6134         } else {
6135           for (b = 0; b < fdof; b++) {
6136             if ((cind < fcdof) && (b == fcdofs[cind])) {
6137               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6138               ++cind;
6139             }
6140           }
6141         }
6142       } else {
6143         if (comps) {
6144           for (b = 0; b < fdof; b++) {
6145             ncSet = fcSet = PETSC_FALSE;
6146             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6147             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6148             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
6149           }
6150         } else {
6151           for (b = 0; b < fdof; b++) {
6152             if ((cind < fcdof) && (b == fcdofs[cind])) {
6153               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6154               ++cind;
6155             }
6156           }
6157         }
6158       }
6159     } else {
6160       if (perm) {
6161         if (comps) {
6162           for (b = 0; b < fdof; b++) {
6163             ncSet = fcSet = PETSC_FALSE;
6164             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6165             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6166             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
6167           }
6168         } else {
6169           for (b = 0; b < fdof; b++) {
6170             if ((cind < fcdof) && (b == fcdofs[cind])) {
6171               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6172               ++cind;
6173             }
6174           }
6175         }
6176       } else {
6177         if (comps) {
6178           for (b = 0; b < fdof; b++) {
6179             ncSet = fcSet = PETSC_FALSE;
6180             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6181             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6182             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
6183           }
6184         } else {
6185           for (b = 0; b < fdof; b++) {
6186             if ((cind < fcdof) && (b == fcdofs[cind])) {
6187               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6188               ++cind;
6189             }
6190           }
6191         }
6192       }
6193     }
6194   }
6195   *offset += fdof;
6196   PetscFunctionReturn(0);
6197 }
6198 
6199 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6200 {
6201   PetscScalar    *array;
6202   const PetscInt *cone, *coneO;
6203   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6204 
6205   PetscFunctionBeginHot;
6206   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6207   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6208   PetscCall(DMPlexGetCone(dm, point, &cone));
6209   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6210   PetscCall(VecGetArray(v, &array));
6211   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6212     const PetscInt cp = !p ? point : cone[p-1];
6213     const PetscInt o  = !p ? 0     : coneO[p-1];
6214 
6215     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
6216     PetscCall(PetscSectionGetDof(section, cp, &dof));
6217     /* ADD_VALUES */
6218     {
6219       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6220       PetscScalar    *a;
6221       PetscInt        cdof, coff, cind = 0, k;
6222 
6223       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6224       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6225       a    = &array[coff];
6226       if (!cdof) {
6227         if (o >= 0) {
6228           for (k = 0; k < dof; ++k) {
6229             a[k] += values[off+k];
6230           }
6231         } else {
6232           for (k = 0; k < dof; ++k) {
6233             a[k] += values[off+dof-k-1];
6234           }
6235         }
6236       } else {
6237         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6238         if (o >= 0) {
6239           for (k = 0; k < dof; ++k) {
6240             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6241             a[k] += values[off+k];
6242           }
6243         } else {
6244           for (k = 0; k < dof; ++k) {
6245             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6246             a[k] += values[off+dof-k-1];
6247           }
6248         }
6249       }
6250     }
6251   }
6252   PetscCall(VecRestoreArray(v, &array));
6253   PetscFunctionReturn(0);
6254 }
6255 
6256 /*@C
6257   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6258 
6259   Not collective
6260 
6261   Input Parameters:
6262 + dm - The DM
6263 . section - The section describing the layout in v, or NULL to use the default section
6264 . v - The local vector
6265 . point - The point in the DM
6266 . values - The array of values
6267 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6268          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6269 
6270   Fortran Notes:
6271   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6272 
6273   Level: intermediate
6274 
6275 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6276 @*/
6277 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6278 {
6279   PetscSection    clSection;
6280   IS              clPoints;
6281   PetscScalar    *array;
6282   PetscInt       *points = NULL;
6283   const PetscInt *clp, *clperm = NULL;
6284   PetscInt        depth, numFields, numPoints, p, clsize;
6285 
6286   PetscFunctionBeginHot;
6287   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6288   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6289   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6290   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6291   PetscCall(DMPlexGetDepth(dm, &depth));
6292   PetscCall(PetscSectionGetNumFields(section, &numFields));
6293   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6294     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6295     PetscFunctionReturn(0);
6296   }
6297   /* Get points */
6298   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6299   for (clsize=0,p=0; p<numPoints; p++) {
6300     PetscInt dof;
6301     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
6302     clsize += dof;
6303   }
6304   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
6305   /* Get array */
6306   PetscCall(VecGetArray(v, &array));
6307   /* Get values */
6308   if (numFields > 0) {
6309     PetscInt offset = 0, f;
6310     for (f = 0; f < numFields; ++f) {
6311       const PetscInt    **perms = NULL;
6312       const PetscScalar **flips = NULL;
6313 
6314       PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6315       switch (mode) {
6316       case INSERT_VALUES:
6317         for (p = 0; p < numPoints; p++) {
6318           const PetscInt    point = points[2*p];
6319           const PetscInt    *perm = perms ? perms[p] : NULL;
6320           const PetscScalar *flip = flips ? flips[p] : NULL;
6321           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6322         } break;
6323       case INSERT_ALL_VALUES:
6324         for (p = 0; p < numPoints; p++) {
6325           const PetscInt    point = points[2*p];
6326           const PetscInt    *perm = perms ? perms[p] : NULL;
6327           const PetscScalar *flip = flips ? flips[p] : NULL;
6328           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6329         } break;
6330       case INSERT_BC_VALUES:
6331         for (p = 0; p < numPoints; p++) {
6332           const PetscInt    point = points[2*p];
6333           const PetscInt    *perm = perms ? perms[p] : NULL;
6334           const PetscScalar *flip = flips ? flips[p] : NULL;
6335           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6336         } break;
6337       case ADD_VALUES:
6338         for (p = 0; p < numPoints; p++) {
6339           const PetscInt    point = points[2*p];
6340           const PetscInt    *perm = perms ? perms[p] : NULL;
6341           const PetscScalar *flip = flips ? flips[p] : NULL;
6342           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6343         } break;
6344       case ADD_ALL_VALUES:
6345         for (p = 0; p < numPoints; p++) {
6346           const PetscInt    point = points[2*p];
6347           const PetscInt    *perm = perms ? perms[p] : NULL;
6348           const PetscScalar *flip = flips ? flips[p] : NULL;
6349           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6350         } break;
6351       case ADD_BC_VALUES:
6352         for (p = 0; p < numPoints; p++) {
6353           const PetscInt    point = points[2*p];
6354           const PetscInt    *perm = perms ? perms[p] : NULL;
6355           const PetscScalar *flip = flips ? flips[p] : NULL;
6356           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6357         } break;
6358       default:
6359         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6360       }
6361       PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6362     }
6363   } else {
6364     PetscInt dof, off;
6365     const PetscInt    **perms = NULL;
6366     const PetscScalar **flips = NULL;
6367 
6368     PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
6369     switch (mode) {
6370     case INSERT_VALUES:
6371       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6372         const PetscInt    point = points[2*p];
6373         const PetscInt    *perm = perms ? perms[p] : NULL;
6374         const PetscScalar *flip = flips ? flips[p] : NULL;
6375         PetscCall(PetscSectionGetDof(section, point, &dof));
6376         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6377       } break;
6378     case INSERT_ALL_VALUES:
6379       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6380         const PetscInt    point = points[2*p];
6381         const PetscInt    *perm = perms ? perms[p] : NULL;
6382         const PetscScalar *flip = flips ? flips[p] : NULL;
6383         PetscCall(PetscSectionGetDof(section, point, &dof));
6384         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
6385       } break;
6386     case INSERT_BC_VALUES:
6387       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6388         const PetscInt    point = points[2*p];
6389         const PetscInt    *perm = perms ? perms[p] : NULL;
6390         const PetscScalar *flip = flips ? flips[p] : NULL;
6391         PetscCall(PetscSectionGetDof(section, point, &dof));
6392         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
6393       } break;
6394     case ADD_VALUES:
6395       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6396         const PetscInt    point = points[2*p];
6397         const PetscInt    *perm = perms ? perms[p] : NULL;
6398         const PetscScalar *flip = flips ? flips[p] : NULL;
6399         PetscCall(PetscSectionGetDof(section, point, &dof));
6400         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
6401       } break;
6402     case ADD_ALL_VALUES:
6403       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6404         const PetscInt    point = points[2*p];
6405         const PetscInt    *perm = perms ? perms[p] : NULL;
6406         const PetscScalar *flip = flips ? flips[p] : NULL;
6407         PetscCall(PetscSectionGetDof(section, point, &dof));
6408         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
6409       } break;
6410     case ADD_BC_VALUES:
6411       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6412         const PetscInt    point = points[2*p];
6413         const PetscInt    *perm = perms ? perms[p] : NULL;
6414         const PetscScalar *flip = flips ? flips[p] : NULL;
6415         PetscCall(PetscSectionGetDof(section, point, &dof));
6416         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
6417       } break;
6418     default:
6419       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6420     }
6421     PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
6422   }
6423   /* Cleanup points */
6424   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6425   /* Cleanup array */
6426   PetscCall(VecRestoreArray(v, &array));
6427   PetscFunctionReturn(0);
6428 }
6429 
6430 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6431 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
6432 {
6433   PetscFunctionBegin;
6434   if (label) {
6435     PetscBool contains;
6436     PetscInt  fdof;
6437 
6438     PetscCall(DMLabelStratumHasPoint(label, labelId, point, &contains));
6439     if (!contains) {
6440       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6441       *offset += fdof;
6442       PetscFunctionReturn(1);
6443     }
6444   }
6445   PetscFunctionReturn(0);
6446 }
6447 
6448 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6449 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)
6450 {
6451   PetscSection    clSection;
6452   IS              clPoints;
6453   PetscScalar    *array;
6454   PetscInt       *points = NULL;
6455   const PetscInt *clp;
6456   PetscInt        numFields, numPoints, p;
6457   PetscInt        offset = 0, f;
6458 
6459   PetscFunctionBeginHot;
6460   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6461   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6462   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6463   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6464   PetscCall(PetscSectionGetNumFields(section, &numFields));
6465   /* Get points */
6466   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6467   /* Get array */
6468   PetscCall(VecGetArray(v, &array));
6469   /* Get values */
6470   for (f = 0; f < numFields; ++f) {
6471     const PetscInt    **perms = NULL;
6472     const PetscScalar **flips = NULL;
6473 
6474     if (!fieldActive[f]) {
6475       for (p = 0; p < numPoints*2; p += 2) {
6476         PetscInt fdof;
6477         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6478         offset += fdof;
6479       }
6480       continue;
6481     }
6482     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6483     switch (mode) {
6484     case INSERT_VALUES:
6485       for (p = 0; p < numPoints; p++) {
6486         const PetscInt    point = points[2*p];
6487         const PetscInt    *perm = perms ? perms[p] : NULL;
6488         const PetscScalar *flip = flips ? flips[p] : NULL;
6489         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6490         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6491       } break;
6492     case INSERT_ALL_VALUES:
6493       for (p = 0; p < numPoints; p++) {
6494         const PetscInt    point = points[2*p];
6495         const PetscInt    *perm = perms ? perms[p] : NULL;
6496         const PetscScalar *flip = flips ? flips[p] : NULL;
6497         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6498         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6499       } break;
6500     case INSERT_BC_VALUES:
6501       for (p = 0; p < numPoints; p++) {
6502         const PetscInt    point = points[2*p];
6503         const PetscInt    *perm = perms ? perms[p] : NULL;
6504         const PetscScalar *flip = flips ? flips[p] : NULL;
6505         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6506         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6507       } break;
6508     case ADD_VALUES:
6509       for (p = 0; p < numPoints; p++) {
6510         const PetscInt    point = points[2*p];
6511         const PetscInt    *perm = perms ? perms[p] : NULL;
6512         const PetscScalar *flip = flips ? flips[p] : NULL;
6513         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6514         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6515       } break;
6516     case ADD_ALL_VALUES:
6517       for (p = 0; p < numPoints; p++) {
6518         const PetscInt    point = points[2*p];
6519         const PetscInt    *perm = perms ? perms[p] : NULL;
6520         const PetscScalar *flip = flips ? flips[p] : NULL;
6521         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6522         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6523       } break;
6524     default:
6525       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6526     }
6527     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6528   }
6529   /* Cleanup points */
6530   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6531   /* Cleanup array */
6532   PetscCall(VecRestoreArray(v, &array));
6533   PetscFunctionReturn(0);
6534 }
6535 
6536 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6537 {
6538   PetscMPIInt    rank;
6539   PetscInt       i, j;
6540 
6541   PetscFunctionBegin;
6542   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6543   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6544   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6545   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6546   numCIndices = numCIndices ? numCIndices : numRIndices;
6547   if (!values) PetscFunctionReturn(0);
6548   for (i = 0; i < numRIndices; i++) {
6549     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6550     for (j = 0; j < numCIndices; j++) {
6551 #if defined(PETSC_USE_COMPLEX)
6552       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j])));
6553 #else
6554       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]));
6555 #endif
6556     }
6557     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6558   }
6559   PetscFunctionReturn(0);
6560 }
6561 
6562 /*
6563   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6564 
6565   Input Parameters:
6566 + section - The section for this data layout
6567 . islocal - Is the section (and thus indices being requested) local or global?
6568 . point   - The point contributing dofs with these indices
6569 . off     - The global offset of this point
6570 . loff    - The local offset of each field
6571 . setBC   - The flag determining whether to include indices of boundary values
6572 . perm    - A permutation of the dofs on this point, or NULL
6573 - indperm - A permutation of the entire indices array, or NULL
6574 
6575   Output Parameter:
6576 . indices - Indices for dofs on this point
6577 
6578   Level: developer
6579 
6580   Note: The indices could be local or global, depending on the value of 'off'.
6581 */
6582 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6583 {
6584   PetscInt        dof;   /* The number of unknowns on this point */
6585   PetscInt        cdof;  /* The number of constraints on this point */
6586   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6587   PetscInt        cind = 0, k;
6588 
6589   PetscFunctionBegin;
6590   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6591   PetscCall(PetscSectionGetDof(section, point, &dof));
6592   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6593   if (!cdof || setBC) {
6594     for (k = 0; k < dof; ++k) {
6595       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6596       const PetscInt ind    = indperm ? indperm[preind] : preind;
6597 
6598       indices[ind] = off + k;
6599     }
6600   } else {
6601     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6602     for (k = 0; k < dof; ++k) {
6603       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6604       const PetscInt ind    = indperm ? indperm[preind] : preind;
6605 
6606       if ((cind < cdof) && (k == cdofs[cind])) {
6607         /* Insert check for returning constrained indices */
6608         indices[ind] = -(off+k+1);
6609         ++cind;
6610       } else {
6611         indices[ind] = off + k - (islocal ? 0 : cind);
6612       }
6613     }
6614   }
6615   *loff += dof;
6616   PetscFunctionReturn(0);
6617 }
6618 
6619 /*
6620  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6621 
6622  Input Parameters:
6623 + section - a section (global or local)
6624 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6625 . point - point within section
6626 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6627 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6628 . setBC - identify constrained (boundary condition) points via involution.
6629 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6630 . permsoff - offset
6631 - indperm - index permutation
6632 
6633  Output Parameter:
6634 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6635 . indices - array to hold indices (as defined by section) of each dof associated with point
6636 
6637  Notes:
6638  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6639  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6640  in the local vector.
6641 
6642  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6643  significant).  It is invalid to call with a global section and setBC=true.
6644 
6645  Developer Note:
6646  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6647  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6648  offset could be obtained from the section instead of passing it explicitly as we do now.
6649 
6650  Example:
6651  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6652  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6653  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6654  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.
6655 
6656  Level: developer
6657 */
6658 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[])
6659 {
6660   PetscInt       numFields, foff, f;
6661 
6662   PetscFunctionBegin;
6663   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6664   PetscCall(PetscSectionGetNumFields(section, &numFields));
6665   for (f = 0, foff = 0; f < numFields; ++f) {
6666     PetscInt        fdof, cfdof;
6667     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6668     PetscInt        cind = 0, b;
6669     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6670 
6671     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6672     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6673     if (!cfdof || setBC) {
6674       for (b = 0; b < fdof; ++b) {
6675         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6676         const PetscInt ind    = indperm ? indperm[preind] : preind;
6677 
6678         indices[ind] = off+foff+b;
6679       }
6680     } else {
6681       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6682       for (b = 0; b < fdof; ++b) {
6683         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6684         const PetscInt ind    = indperm ? indperm[preind] : preind;
6685 
6686         if ((cind < cfdof) && (b == fcdofs[cind])) {
6687           indices[ind] = -(off+foff+b+1);
6688           ++cind;
6689         } else {
6690           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6691         }
6692       }
6693     }
6694     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6695     foffs[f] += fdof;
6696   }
6697   PetscFunctionReturn(0);
6698 }
6699 
6700 /*
6701   This version believes the globalSection offsets for each field, rather than just the point offset
6702 
6703  . foffs - The offset into 'indices' for each field, since it is segregated by field
6704 
6705  Notes:
6706  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6707  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6708 */
6709 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6710 {
6711   PetscInt       numFields, foff, f;
6712 
6713   PetscFunctionBegin;
6714   PetscCall(PetscSectionGetNumFields(section, &numFields));
6715   for (f = 0; f < numFields; ++f) {
6716     PetscInt        fdof, cfdof;
6717     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6718     PetscInt        cind = 0, b;
6719     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6720 
6721     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6722     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6723     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6724     if (!cfdof) {
6725       for (b = 0; b < fdof; ++b) {
6726         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6727         const PetscInt ind    = indperm ? indperm[preind] : preind;
6728 
6729         indices[ind] = foff+b;
6730       }
6731     } else {
6732       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6733       for (b = 0; b < fdof; ++b) {
6734         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6735         const PetscInt ind    = indperm ? indperm[preind] : preind;
6736 
6737         if ((cind < cfdof) && (b == fcdofs[cind])) {
6738           indices[ind] = -(foff+b+1);
6739           ++cind;
6740         } else {
6741           indices[ind] = foff+b-cind;
6742         }
6743       }
6744     }
6745     foffs[f] += fdof;
6746   }
6747   PetscFunctionReturn(0);
6748 }
6749 
6750 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)
6751 {
6752   Mat             cMat;
6753   PetscSection    aSec, cSec;
6754   IS              aIS;
6755   PetscInt        aStart = -1, aEnd = -1;
6756   const PetscInt  *anchors;
6757   PetscInt        numFields, f, p, q, newP = 0;
6758   PetscInt        newNumPoints = 0, newNumIndices = 0;
6759   PetscInt        *newPoints, *indices, *newIndices;
6760   PetscInt        maxAnchor, maxDof;
6761   PetscInt        newOffsets[32];
6762   PetscInt        *pointMatOffsets[32];
6763   PetscInt        *newPointOffsets[32];
6764   PetscScalar     *pointMat[32];
6765   PetscScalar     *newValues=NULL,*tmpValues;
6766   PetscBool       anyConstrained = PETSC_FALSE;
6767 
6768   PetscFunctionBegin;
6769   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6770   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6771   PetscCall(PetscSectionGetNumFields(section, &numFields));
6772 
6773   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
6774   /* if there are point-to-point constraints */
6775   if (aSec) {
6776     PetscCall(PetscArrayzero(newOffsets, 32));
6777     PetscCall(ISGetIndices(aIS,&anchors));
6778     PetscCall(PetscSectionGetChart(aSec,&aStart,&aEnd));
6779     /* figure out how many points are going to be in the new element matrix
6780      * (we allow double counting, because it's all just going to be summed
6781      * into the global matrix anyway) */
6782     for (p = 0; p < 2*numPoints; p+=2) {
6783       PetscInt b    = points[p];
6784       PetscInt bDof = 0, bSecDof;
6785 
6786       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6787       if (!bSecDof) {
6788         continue;
6789       }
6790       if (b >= aStart && b < aEnd) {
6791         PetscCall(PetscSectionGetDof(aSec,b,&bDof));
6792       }
6793       if (bDof) {
6794         /* this point is constrained */
6795         /* it is going to be replaced by its anchors */
6796         PetscInt bOff, q;
6797 
6798         anyConstrained = PETSC_TRUE;
6799         newNumPoints  += bDof;
6800         PetscCall(PetscSectionGetOffset(aSec,b,&bOff));
6801         for (q = 0; q < bDof; q++) {
6802           PetscInt a = anchors[bOff + q];
6803           PetscInt aDof;
6804 
6805           PetscCall(PetscSectionGetDof(section,a,&aDof));
6806           newNumIndices += aDof;
6807           for (f = 0; f < numFields; ++f) {
6808             PetscInt fDof;
6809 
6810             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6811             newOffsets[f+1] += fDof;
6812           }
6813         }
6814       }
6815       else {
6816         /* this point is not constrained */
6817         newNumPoints++;
6818         newNumIndices += bSecDof;
6819         for (f = 0; f < numFields; ++f) {
6820           PetscInt fDof;
6821 
6822           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6823           newOffsets[f+1] += fDof;
6824         }
6825       }
6826     }
6827   }
6828   if (!anyConstrained) {
6829     if (outNumPoints)  *outNumPoints  = 0;
6830     if (outNumIndices) *outNumIndices = 0;
6831     if (outPoints)     *outPoints     = NULL;
6832     if (outValues)     *outValues     = NULL;
6833     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6834     PetscFunctionReturn(0);
6835   }
6836 
6837   if (outNumPoints)  *outNumPoints  = newNumPoints;
6838   if (outNumIndices) *outNumIndices = newNumIndices;
6839 
6840   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6841 
6842   if (!outPoints && !outValues) {
6843     if (offsets) {
6844       for (f = 0; f <= numFields; f++) {
6845         offsets[f] = newOffsets[f];
6846       }
6847     }
6848     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6849     PetscFunctionReturn(0);
6850   }
6851 
6852   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6853 
6854   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6855 
6856   /* workspaces */
6857   if (numFields) {
6858     for (f = 0; f < numFields; f++) {
6859       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
6860       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
6861     }
6862   }
6863   else {
6864     PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
6865     PetscCall(DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]));
6866   }
6867 
6868   /* get workspaces for the point-to-point matrices */
6869   if (numFields) {
6870     PetscInt totalOffset, totalMatOffset;
6871 
6872     for (p = 0; p < numPoints; p++) {
6873       PetscInt b    = points[2*p];
6874       PetscInt bDof = 0, bSecDof;
6875 
6876       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6877       if (!bSecDof) {
6878         for (f = 0; f < numFields; f++) {
6879           newPointOffsets[f][p + 1] = 0;
6880           pointMatOffsets[f][p + 1] = 0;
6881         }
6882         continue;
6883       }
6884       if (b >= aStart && b < aEnd) {
6885         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6886       }
6887       if (bDof) {
6888         for (f = 0; f < numFields; f++) {
6889           PetscInt fDof, q, bOff, allFDof = 0;
6890 
6891           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6892           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6893           for (q = 0; q < bDof; q++) {
6894             PetscInt a = anchors[bOff + q];
6895             PetscInt aFDof;
6896 
6897             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6898             allFDof += aFDof;
6899           }
6900           newPointOffsets[f][p+1] = allFDof;
6901           pointMatOffsets[f][p+1] = fDof * allFDof;
6902         }
6903       }
6904       else {
6905         for (f = 0; f < numFields; f++) {
6906           PetscInt fDof;
6907 
6908           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6909           newPointOffsets[f][p+1] = fDof;
6910           pointMatOffsets[f][p+1] = 0;
6911         }
6912       }
6913     }
6914     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6915       newPointOffsets[f][0] = totalOffset;
6916       pointMatOffsets[f][0] = totalMatOffset;
6917       for (p = 0; p < numPoints; p++) {
6918         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6919         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6920       }
6921       totalOffset    = newPointOffsets[f][numPoints];
6922       totalMatOffset = pointMatOffsets[f][numPoints];
6923       PetscCall(DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
6924     }
6925   }
6926   else {
6927     for (p = 0; p < numPoints; p++) {
6928       PetscInt b    = points[2*p];
6929       PetscInt bDof = 0, bSecDof;
6930 
6931       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6932       if (!bSecDof) {
6933         newPointOffsets[0][p + 1] = 0;
6934         pointMatOffsets[0][p + 1] = 0;
6935         continue;
6936       }
6937       if (b >= aStart && b < aEnd) {
6938         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6939       }
6940       if (bDof) {
6941         PetscInt bOff, q, allDof = 0;
6942 
6943         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6944         for (q = 0; q < bDof; q++) {
6945           PetscInt a = anchors[bOff + q], aDof;
6946 
6947           PetscCall(PetscSectionGetDof(section, a, &aDof));
6948           allDof += aDof;
6949         }
6950         newPointOffsets[0][p+1] = allDof;
6951         pointMatOffsets[0][p+1] = bSecDof * allDof;
6952       }
6953       else {
6954         newPointOffsets[0][p+1] = bSecDof;
6955         pointMatOffsets[0][p+1] = 0;
6956       }
6957     }
6958     newPointOffsets[0][0] = 0;
6959     pointMatOffsets[0][0] = 0;
6960     for (p = 0; p < numPoints; p++) {
6961       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6962       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6963     }
6964     PetscCall(DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
6965   }
6966 
6967   /* output arrays */
6968   PetscCall(DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
6969 
6970   /* get the point-to-point matrices; construct newPoints */
6971   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6972   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6973   PetscCall(DMGetWorkArray(dm,maxDof,MPIU_INT,&indices));
6974   PetscCall(DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
6975   if (numFields) {
6976     for (p = 0, newP = 0; p < numPoints; p++) {
6977       PetscInt b    = points[2*p];
6978       PetscInt o    = points[2*p+1];
6979       PetscInt bDof = 0, bSecDof;
6980 
6981       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6982       if (!bSecDof) {
6983         continue;
6984       }
6985       if (b >= aStart && b < aEnd) {
6986         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6987       }
6988       if (bDof) {
6989         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6990 
6991         fStart[0] = 0;
6992         fEnd[0]   = 0;
6993         for (f = 0; f < numFields; f++) {
6994           PetscInt fDof;
6995 
6996           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
6997           fStart[f+1] = fStart[f] + fDof;
6998           fEnd[f+1]   = fStart[f+1];
6999         }
7000         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7001         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7002 
7003         fAnchorStart[0] = 0;
7004         fAnchorEnd[0]   = 0;
7005         for (f = 0; f < numFields; f++) {
7006           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7007 
7008           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
7009           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
7010         }
7011         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7012         for (q = 0; q < bDof; q++) {
7013           PetscInt a = anchors[bOff + q], aOff;
7014 
7015           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7016           newPoints[2*(newP + q)]     = a;
7017           newPoints[2*(newP + q) + 1] = 0;
7018           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7019           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7020         }
7021         newP += bDof;
7022 
7023         if (outValues) {
7024           /* get the point-to-point submatrix */
7025           for (f = 0; f < numFields; f++) {
7026             PetscCall(MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]));
7027           }
7028         }
7029       }
7030       else {
7031         newPoints[2 * newP]     = b;
7032         newPoints[2 * newP + 1] = o;
7033         newP++;
7034       }
7035     }
7036   } else {
7037     for (p = 0; p < numPoints; p++) {
7038       PetscInt b    = points[2*p];
7039       PetscInt o    = points[2*p+1];
7040       PetscInt bDof = 0, bSecDof;
7041 
7042       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7043       if (!bSecDof) {
7044         continue;
7045       }
7046       if (b >= aStart && b < aEnd) {
7047         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7048       }
7049       if (bDof) {
7050         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7051 
7052         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7053         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7054 
7055         PetscCall(PetscSectionGetOffset (aSec, b, &bOff));
7056         for (q = 0; q < bDof; q++) {
7057           PetscInt a = anchors[bOff + q], aOff;
7058 
7059           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7060 
7061           newPoints[2*(newP + q)]     = a;
7062           newPoints[2*(newP + q) + 1] = 0;
7063           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7064           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7065         }
7066         newP += bDof;
7067 
7068         /* get the point-to-point submatrix */
7069         if (outValues) {
7070           PetscCall(MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]));
7071         }
7072       }
7073       else {
7074         newPoints[2 * newP]     = b;
7075         newPoints[2 * newP + 1] = o;
7076         newP++;
7077       }
7078     }
7079   }
7080 
7081   if (outValues) {
7082     PetscCall(DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7083     PetscCall(PetscArrayzero(tmpValues,newNumIndices*numIndices));
7084     /* multiply constraints on the right */
7085     if (numFields) {
7086       for (f = 0; f < numFields; f++) {
7087         PetscInt oldOff = offsets[f];
7088 
7089         for (p = 0; p < numPoints; p++) {
7090           PetscInt cStart = newPointOffsets[f][p];
7091           PetscInt b      = points[2 * p];
7092           PetscInt c, r, k;
7093           PetscInt dof;
7094 
7095           PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7096           if (!dof) {
7097             continue;
7098           }
7099           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7100             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
7101             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
7102 
7103             for (r = 0; r < numIndices; r++) {
7104               for (c = 0; c < nCols; c++) {
7105                 for (k = 0; k < dof; k++) {
7106                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7107                 }
7108               }
7109             }
7110           }
7111           else {
7112             /* copy this column as is */
7113             for (r = 0; r < numIndices; r++) {
7114               for (c = 0; c < dof; c++) {
7115                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7116               }
7117             }
7118           }
7119           oldOff += dof;
7120         }
7121       }
7122     }
7123     else {
7124       PetscInt oldOff = 0;
7125       for (p = 0; p < numPoints; p++) {
7126         PetscInt cStart = newPointOffsets[0][p];
7127         PetscInt b      = points[2 * p];
7128         PetscInt c, r, k;
7129         PetscInt dof;
7130 
7131         PetscCall(PetscSectionGetDof(section,b,&dof));
7132         if (!dof) {
7133           continue;
7134         }
7135         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7136           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
7137           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
7138 
7139           for (r = 0; r < numIndices; r++) {
7140             for (c = 0; c < nCols; c++) {
7141               for (k = 0; k < dof; k++) {
7142                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7143               }
7144             }
7145           }
7146         }
7147         else {
7148           /* copy this column as is */
7149           for (r = 0; r < numIndices; r++) {
7150             for (c = 0; c < dof; c++) {
7151               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7152             }
7153           }
7154         }
7155         oldOff += dof;
7156       }
7157     }
7158 
7159     if (multiplyLeft) {
7160       PetscCall(DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues));
7161       PetscCall(PetscArrayzero(newValues,newNumIndices*newNumIndices));
7162       /* multiply constraints transpose on the left */
7163       if (numFields) {
7164         for (f = 0; f < numFields; f++) {
7165           PetscInt oldOff = offsets[f];
7166 
7167           for (p = 0; p < numPoints; p++) {
7168             PetscInt rStart = newPointOffsets[f][p];
7169             PetscInt b      = points[2 * p];
7170             PetscInt c, r, k;
7171             PetscInt dof;
7172 
7173             PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7174             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7175               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
7176               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
7177 
7178               for (r = 0; r < nRows; r++) {
7179                 for (c = 0; c < newNumIndices; c++) {
7180                   for (k = 0; k < dof; k++) {
7181                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7182                   }
7183                 }
7184               }
7185             }
7186             else {
7187               /* copy this row as is */
7188               for (r = 0; r < dof; r++) {
7189                 for (c = 0; c < newNumIndices; c++) {
7190                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7191                 }
7192               }
7193             }
7194             oldOff += dof;
7195           }
7196         }
7197       }
7198       else {
7199         PetscInt oldOff = 0;
7200 
7201         for (p = 0; p < numPoints; p++) {
7202           PetscInt rStart = newPointOffsets[0][p];
7203           PetscInt b      = points[2 * p];
7204           PetscInt c, r, k;
7205           PetscInt dof;
7206 
7207           PetscCall(PetscSectionGetDof(section,b,&dof));
7208           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7209             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
7210             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
7211 
7212             for (r = 0; r < nRows; r++) {
7213               for (c = 0; c < newNumIndices; c++) {
7214                 for (k = 0; k < dof; k++) {
7215                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7216                 }
7217               }
7218             }
7219           }
7220           else {
7221             /* copy this row as is */
7222             for (r = 0; r < dof; r++) {
7223               for (c = 0; c < newNumIndices; c++) {
7224                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7225               }
7226             }
7227           }
7228           oldOff += dof;
7229         }
7230       }
7231 
7232       PetscCall(DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7233     }
7234     else {
7235       newValues = tmpValues;
7236     }
7237   }
7238 
7239   /* clean up */
7240   PetscCall(DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices));
7241   PetscCall(DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
7242 
7243   if (numFields) {
7244     for (f = 0; f < numFields; f++) {
7245       PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
7246       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
7247       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
7248     }
7249   }
7250   else {
7251     PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
7252     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
7253     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]));
7254   }
7255   PetscCall(ISRestoreIndices(aIS,&anchors));
7256 
7257   /* output */
7258   if (outPoints) {
7259     *outPoints = newPoints;
7260   }
7261   else {
7262     PetscCall(DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
7263   }
7264   if (outValues) {
7265     *outValues = newValues;
7266   }
7267   for (f = 0; f <= numFields; f++) {
7268     offsets[f] = newOffsets[f];
7269   }
7270   PetscFunctionReturn(0);
7271 }
7272 
7273 /*@C
7274   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7275 
7276   Not collective
7277 
7278   Input Parameters:
7279 + dm         - The DM
7280 . section    - The PetscSection describing the points (a local section)
7281 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7282 . point      - The point defining the closure
7283 - useClPerm  - Use the closure point permutation if available
7284 
7285   Output Parameters:
7286 + numIndices - The number of dof indices in the closure of point with the input sections
7287 . indices    - The dof indices
7288 . outOffsets - Array to write the field offsets into, or NULL
7289 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7290 
7291   Notes:
7292   Must call DMPlexRestoreClosureIndices() to free allocated memory
7293 
7294   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7295   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7296   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7297   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7298   indices (with the above semantics) are implied.
7299 
7300   Level: advanced
7301 
7302 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7303 @*/
7304 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7305                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7306 {
7307   /* Closure ordering */
7308   PetscSection        clSection;
7309   IS                  clPoints;
7310   const PetscInt     *clp;
7311   PetscInt           *points;
7312   const PetscInt     *clperm = NULL;
7313   /* Dof permutation and sign flips */
7314   const PetscInt    **perms[32] = {NULL};
7315   const PetscScalar **flips[32] = {NULL};
7316   PetscScalar        *valCopy   = NULL;
7317   /* Hanging node constraints */
7318   PetscInt           *pointsC = NULL;
7319   PetscScalar        *valuesC = NULL;
7320   PetscInt            NclC, NiC;
7321 
7322   PetscInt           *idx;
7323   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
7324   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7325 
7326   PetscFunctionBeginHot;
7327   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7328   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7329   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7330   if (numIndices) PetscValidIntPointer(numIndices, 6);
7331   if (indices)    PetscValidPointer(indices, 7);
7332   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7333   if (values)     PetscValidPointer(values, 9);
7334   PetscCall(PetscSectionGetNumFields(section, &Nf));
7335   PetscCheck(Nf <= 31,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7336   PetscCall(PetscArrayzero(offsets, 32));
7337   /* 1) Get points in closure */
7338   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7339   if (useClPerm) {
7340     PetscInt depth, clsize;
7341     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7342     for (clsize=0,p=0; p<Ncl; p++) {
7343       PetscInt dof;
7344       PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
7345       clsize += dof;
7346     }
7347     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
7348   }
7349   /* 2) Get number of indices on these points and field offsets from section */
7350   for (p = 0; p < Ncl*2; p += 2) {
7351     PetscInt dof, fdof;
7352 
7353     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7354     for (f = 0; f < Nf; ++f) {
7355       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7356       offsets[f+1] += fdof;
7357     }
7358     Ni += dof;
7359   }
7360   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
7361   PetscCheck(!Nf || offsets[Nf] == Ni,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7362   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7363   for (f = 0; f < PetscMax(1, Nf); ++f) {
7364     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7365     else    PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7366     /* may need to apply sign changes to the element matrix */
7367     if (values && flips[f]) {
7368       PetscInt foffset = offsets[f];
7369 
7370       for (p = 0; p < Ncl; ++p) {
7371         PetscInt           pnt  = points[2*p], fdof;
7372         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7373 
7374         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7375         else     PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7376         if (flip) {
7377           PetscInt i, j, k;
7378 
7379           if (!valCopy) {
7380             PetscCall(DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7381             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7382             *values = valCopy;
7383           }
7384           for (i = 0; i < fdof; ++i) {
7385             PetscScalar fval = flip[i];
7386 
7387             for (k = 0; k < Ni; ++k) {
7388               valCopy[Ni * (foffset + i) + k] *= fval;
7389               valCopy[Ni * k + (foffset + i)] *= fval;
7390             }
7391           }
7392         }
7393         foffset += fdof;
7394       }
7395     }
7396   }
7397   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7398   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7399   if (NclC) {
7400     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7401     for (f = 0; f < PetscMax(1, Nf); ++f) {
7402       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7403       else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7404     }
7405     for (f = 0; f < PetscMax(1, Nf); ++f) {
7406       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7407       else    PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7408     }
7409     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7410     Ncl     = NclC;
7411     Ni      = NiC;
7412     points  = pointsC;
7413     if (values) *values = valuesC;
7414   }
7415   /* 5) Calculate indices */
7416   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7417   if (Nf) {
7418     PetscInt  idxOff;
7419     PetscBool useFieldOffsets;
7420 
7421     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
7422     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7423     if (useFieldOffsets) {
7424       for (p = 0; p < Ncl; ++p) {
7425         const PetscInt pnt = points[p*2];
7426 
7427         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7428       }
7429     } else {
7430       for (p = 0; p < Ncl; ++p) {
7431         const PetscInt pnt = points[p*2];
7432 
7433         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7434         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7435          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7436          * global section. */
7437         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7438       }
7439     }
7440   } else {
7441     PetscInt off = 0, idxOff;
7442 
7443     for (p = 0; p < Ncl; ++p) {
7444       const PetscInt  pnt  = points[p*2];
7445       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7446 
7447       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7448       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7449        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7450       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7451     }
7452   }
7453   /* 6) Cleanup */
7454   for (f = 0; f < PetscMax(1, Nf); ++f) {
7455     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7456     else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7457   }
7458   if (NclC) {
7459     PetscCall(DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC));
7460   } else {
7461     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7462   }
7463 
7464   if (numIndices) *numIndices = Ni;
7465   if (indices)    *indices    = idx;
7466   PetscFunctionReturn(0);
7467 }
7468 
7469 /*@C
7470   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7471 
7472   Not collective
7473 
7474   Input Parameters:
7475 + dm         - The DM
7476 . section    - The PetscSection describing the points (a local section)
7477 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7478 . point      - The point defining the closure
7479 - useClPerm  - Use the closure point permutation if available
7480 
7481   Output Parameters:
7482 + numIndices - The number of dof indices in the closure of point with the input sections
7483 . indices    - The dof indices
7484 . outOffsets - Array to write the field offsets into, or NULL
7485 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7486 
7487   Notes:
7488   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7489 
7490   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7491   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7492   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7493   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7494   indices (with the above semantics) are implied.
7495 
7496   Level: advanced
7497 
7498 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7499 @*/
7500 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7501                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7502 {
7503   PetscFunctionBegin;
7504   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7505   PetscValidPointer(indices, 7);
7506   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7507   PetscFunctionReturn(0);
7508 }
7509 
7510 /*@C
7511   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7512 
7513   Not collective
7514 
7515   Input Parameters:
7516 + dm - The DM
7517 . section - The section describing the layout in v, or NULL to use the default section
7518 . globalSection - The section describing the layout in v, or NULL to use the default global section
7519 . A - The matrix
7520 . point - The point in the DM
7521 . values - The array of values
7522 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7523 
7524   Fortran Notes:
7525   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7526 
7527   Level: intermediate
7528 
7529 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7530 @*/
7531 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7532 {
7533   DM_Plex           *mesh = (DM_Plex*) dm->data;
7534   PetscInt          *indices;
7535   PetscInt           numIndices;
7536   const PetscScalar *valuesOrig = values;
7537   PetscErrorCode     ierr;
7538 
7539   PetscFunctionBegin;
7540   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7541   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7542   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7543   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7544   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7545   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7546 
7547   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7548 
7549   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7550   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7551   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7552   if (ierr) {
7553     PetscMPIInt    rank;
7554 
7555     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7556     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7557     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7558     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7559     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7560     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7561   }
7562   if (mesh->printFEM > 1) {
7563     PetscInt i;
7564     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7565     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7566     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7567   }
7568 
7569   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7570   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7571   PetscFunctionReturn(0);
7572 }
7573 
7574 /*@C
7575   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7576 
7577   Not collective
7578 
7579   Input Parameters:
7580 + dmRow - The DM for the row fields
7581 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7582 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7583 . dmCol - The DM for the column fields
7584 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7585 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7586 . A - The matrix
7587 . point - The point in the DMs
7588 . values - The array of values
7589 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7590 
7591   Level: intermediate
7592 
7593 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7594 @*/
7595 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7596 {
7597   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7598   PetscInt          *indicesRow, *indicesCol;
7599   PetscInt           numIndicesRow, numIndicesCol;
7600   const PetscScalar *valuesOrig = values;
7601   PetscErrorCode     ierr;
7602 
7603   PetscFunctionBegin;
7604   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7605   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7606   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7607   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7608   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7609   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7610   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7611   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7612   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7613   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7614   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7615 
7616   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7617   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7618 
7619   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7620   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7621   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7622   if (ierr) {
7623     PetscMPIInt    rank;
7624 
7625     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7626     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7627     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7628     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7629     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7630     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7631   }
7632 
7633   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7634   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7635   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7636   PetscFunctionReturn(0);
7637 }
7638 
7639 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7640 {
7641   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7642   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7643   PetscInt       *cpoints = NULL;
7644   PetscInt       *findices, *cindices;
7645   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7646   PetscInt        foffsets[32], coffsets[32];
7647   DMPolytopeType  ct;
7648   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7649   PetscErrorCode  ierr;
7650 
7651   PetscFunctionBegin;
7652   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7653   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7654   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7655   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7656   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7657   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7658   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7659   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7660   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7661   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7662   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7663   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7664   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7665   PetscCall(PetscArrayzero(foffsets, 32));
7666   PetscCall(PetscArrayzero(coffsets, 32));
7667   /* Column indices */
7668   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7669   maxFPoints = numCPoints;
7670   /* Compress out points not in the section */
7671   /*   TODO: Squeeze out points with 0 dof as well */
7672   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7673   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7674     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7675       cpoints[q*2]   = cpoints[p];
7676       cpoints[q*2+1] = cpoints[p+1];
7677       ++q;
7678     }
7679   }
7680   numCPoints = q;
7681   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7682     PetscInt fdof;
7683 
7684     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7685     if (!dof) continue;
7686     for (f = 0; f < numFields; ++f) {
7687       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7688       coffsets[f+1] += fdof;
7689     }
7690     numCIndices += dof;
7691   }
7692   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7693   /* Row indices */
7694   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7695   {
7696     DMPlexTransform tr;
7697     DMPolytopeType *rct;
7698     PetscInt       *rsize, *rcone, *rornt, Nt;
7699 
7700     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7701     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7702     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7703     numSubcells = rsize[Nt-1];
7704     PetscCall(DMPlexTransformDestroy(&tr));
7705   }
7706   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7707   for (r = 0, q = 0; r < numSubcells; ++r) {
7708     /* TODO Map from coarse to fine cells */
7709     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7710     /* Compress out points not in the section */
7711     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7712     for (p = 0; p < numFPoints*2; p += 2) {
7713       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7714         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7715         if (!dof) continue;
7716         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7717         if (s < q) continue;
7718         ftotpoints[q*2]   = fpoints[p];
7719         ftotpoints[q*2+1] = fpoints[p+1];
7720         ++q;
7721       }
7722     }
7723     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7724   }
7725   numFPoints = q;
7726   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7727     PetscInt fdof;
7728 
7729     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7730     if (!dof) continue;
7731     for (f = 0; f < numFields; ++f) {
7732       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7733       foffsets[f+1] += fdof;
7734     }
7735     numFIndices += dof;
7736   }
7737   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7738 
7739   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7740   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7741   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7742   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7743   if (numFields) {
7744     const PetscInt **permsF[32] = {NULL};
7745     const PetscInt **permsC[32] = {NULL};
7746 
7747     for (f = 0; f < numFields; f++) {
7748       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7749       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7750     }
7751     for (p = 0; p < numFPoints; p++) {
7752       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7753       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7754     }
7755     for (p = 0; p < numCPoints; p++) {
7756       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7757       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7758     }
7759     for (f = 0; f < numFields; f++) {
7760       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7761       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7762     }
7763   } else {
7764     const PetscInt **permsF = NULL;
7765     const PetscInt **permsC = NULL;
7766 
7767     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7768     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7769     for (p = 0, off = 0; p < numFPoints; p++) {
7770       const PetscInt *perm = permsF ? permsF[p] : NULL;
7771 
7772       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7773       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7774     }
7775     for (p = 0, off = 0; p < numCPoints; p++) {
7776       const PetscInt *perm = permsC ? permsC[p] : NULL;
7777 
7778       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7779       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7780     }
7781     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7782     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7783   }
7784   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7785   /* TODO: flips */
7786   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7787   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7788   if (ierr) {
7789     PetscMPIInt    rank;
7790 
7791     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7792     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7793     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7794     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7795     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7796   }
7797   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7798   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7799   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7800   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7801   PetscFunctionReturn(0);
7802 }
7803 
7804 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7805 {
7806   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7807   PetscInt      *cpoints = NULL;
7808   PetscInt       foffsets[32], coffsets[32];
7809   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7810   DMPolytopeType ct;
7811   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7812 
7813   PetscFunctionBegin;
7814   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7815   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7816   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7817   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7818   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7819   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7820   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7821   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7822   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7823   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7824   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7825   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7826   PetscCall(PetscArrayzero(foffsets, 32));
7827   PetscCall(PetscArrayzero(coffsets, 32));
7828   /* Column indices */
7829   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7830   maxFPoints = numCPoints;
7831   /* Compress out points not in the section */
7832   /*   TODO: Squeeze out points with 0 dof as well */
7833   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7834   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7835     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7836       cpoints[q*2]   = cpoints[p];
7837       cpoints[q*2+1] = cpoints[p+1];
7838       ++q;
7839     }
7840   }
7841   numCPoints = q;
7842   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7843     PetscInt fdof;
7844 
7845     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7846     if (!dof) continue;
7847     for (f = 0; f < numFields; ++f) {
7848       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7849       coffsets[f+1] += fdof;
7850     }
7851     numCIndices += dof;
7852   }
7853   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7854   /* Row indices */
7855   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7856   {
7857     DMPlexTransform tr;
7858     DMPolytopeType *rct;
7859     PetscInt       *rsize, *rcone, *rornt, Nt;
7860 
7861     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7862     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7863     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7864     numSubcells = rsize[Nt-1];
7865     PetscCall(DMPlexTransformDestroy(&tr));
7866   }
7867   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7868   for (r = 0, q = 0; r < numSubcells; ++r) {
7869     /* TODO Map from coarse to fine cells */
7870     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7871     /* Compress out points not in the section */
7872     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7873     for (p = 0; p < numFPoints*2; p += 2) {
7874       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7875         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7876         if (!dof) continue;
7877         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7878         if (s < q) continue;
7879         ftotpoints[q*2]   = fpoints[p];
7880         ftotpoints[q*2+1] = fpoints[p+1];
7881         ++q;
7882       }
7883     }
7884     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7885   }
7886   numFPoints = q;
7887   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7888     PetscInt fdof;
7889 
7890     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7891     if (!dof) continue;
7892     for (f = 0; f < numFields; ++f) {
7893       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7894       foffsets[f+1] += fdof;
7895     }
7896     numFIndices += dof;
7897   }
7898   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7899 
7900   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7901   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7902   if (numFields) {
7903     const PetscInt **permsF[32] = {NULL};
7904     const PetscInt **permsC[32] = {NULL};
7905 
7906     for (f = 0; f < numFields; f++) {
7907       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7908       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7909     }
7910     for (p = 0; p < numFPoints; p++) {
7911       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7912       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7913     }
7914     for (p = 0; p < numCPoints; p++) {
7915       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7916       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7917     }
7918     for (f = 0; f < numFields; f++) {
7919       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7920       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7921     }
7922   } else {
7923     const PetscInt **permsF = NULL;
7924     const PetscInt **permsC = NULL;
7925 
7926     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7927     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7928     for (p = 0, off = 0; p < numFPoints; p++) {
7929       const PetscInt *perm = permsF ? permsF[p] : NULL;
7930 
7931       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7932       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7933     }
7934     for (p = 0, off = 0; p < numCPoints; p++) {
7935       const PetscInt *perm = permsC ? permsC[p] : NULL;
7936 
7937       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7938       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7939     }
7940     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7941     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7942   }
7943   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7944   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7945   PetscFunctionReturn(0);
7946 }
7947 
7948 /*@C
7949   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7950 
7951   Input Parameter:
7952 . dm   - The DMPlex object
7953 
7954   Output Parameter:
7955 . cellHeight - The height of a cell
7956 
7957   Level: developer
7958 
7959 .seealso `DMPlexSetVTKCellHeight()`
7960 @*/
7961 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7962 {
7963   DM_Plex *mesh = (DM_Plex*) dm->data;
7964 
7965   PetscFunctionBegin;
7966   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7967   PetscValidIntPointer(cellHeight, 2);
7968   *cellHeight = mesh->vtkCellHeight;
7969   PetscFunctionReturn(0);
7970 }
7971 
7972 /*@C
7973   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7974 
7975   Input Parameters:
7976 + dm   - The DMPlex object
7977 - cellHeight - The height of a cell
7978 
7979   Level: developer
7980 
7981 .seealso `DMPlexGetVTKCellHeight()`
7982 @*/
7983 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7984 {
7985   DM_Plex *mesh = (DM_Plex*) dm->data;
7986 
7987   PetscFunctionBegin;
7988   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7989   mesh->vtkCellHeight = cellHeight;
7990   PetscFunctionReturn(0);
7991 }
7992 
7993 /*@
7994   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7995 
7996   Input Parameter:
7997 . dm - The DMPlex object
7998 
7999   Output Parameters:
8000 + gcStart - The first ghost cell, or NULL
8001 - gcEnd   - The upper bound on ghost cells, or NULL
8002 
8003   Level: advanced
8004 
8005 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8006 @*/
8007 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8008 {
8009   DMLabel        ctLabel;
8010 
8011   PetscFunctionBegin;
8012   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8013   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8014   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8015   // Reset label for fast lookup
8016   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8017   PetscFunctionReturn(0);
8018 }
8019 
8020 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8021 {
8022   PetscSection   section, globalSection;
8023   PetscInt      *numbers, p;
8024 
8025   PetscFunctionBegin;
8026   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
8027   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8028   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8029   for (p = pStart; p < pEnd; ++p) {
8030     PetscCall(PetscSectionSetDof(section, p, 1));
8031   }
8032   PetscCall(PetscSectionSetUp(section));
8033   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8034   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8035   for (p = pStart; p < pEnd; ++p) {
8036     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8037     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8038     else                       numbers[p-pStart] += shift;
8039   }
8040   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8041   if (globalSize) {
8042     PetscLayout layout;
8043     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8044     PetscCall(PetscLayoutGetSize(layout, globalSize));
8045     PetscCall(PetscLayoutDestroy(&layout));
8046   }
8047   PetscCall(PetscSectionDestroy(&section));
8048   PetscCall(PetscSectionDestroy(&globalSection));
8049   PetscFunctionReturn(0);
8050 }
8051 
8052 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8053 {
8054   PetscInt       cellHeight, cStart, cEnd;
8055 
8056   PetscFunctionBegin;
8057   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8058   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8059   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8060   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8061   PetscFunctionReturn(0);
8062 }
8063 
8064 /*@
8065   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8066 
8067   Input Parameter:
8068 . dm   - The DMPlex object
8069 
8070   Output Parameter:
8071 . globalCellNumbers - Global cell numbers for all cells on this process
8072 
8073   Level: developer
8074 
8075 .seealso `DMPlexGetVertexNumbering()`
8076 @*/
8077 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8078 {
8079   DM_Plex       *mesh = (DM_Plex*) dm->data;
8080 
8081   PetscFunctionBegin;
8082   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8083   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8084   *globalCellNumbers = mesh->globalCellNumbers;
8085   PetscFunctionReturn(0);
8086 }
8087 
8088 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8089 {
8090   PetscInt       vStart, vEnd;
8091 
8092   PetscFunctionBegin;
8093   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8094   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8095   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8096   PetscFunctionReturn(0);
8097 }
8098 
8099 /*@
8100   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8101 
8102   Input Parameter:
8103 . dm   - The DMPlex object
8104 
8105   Output Parameter:
8106 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8107 
8108   Level: developer
8109 
8110 .seealso `DMPlexGetCellNumbering()`
8111 @*/
8112 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8113 {
8114   DM_Plex       *mesh = (DM_Plex*) dm->data;
8115 
8116   PetscFunctionBegin;
8117   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8118   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8119   *globalVertexNumbers = mesh->globalVertexNumbers;
8120   PetscFunctionReturn(0);
8121 }
8122 
8123 /*@
8124   DMPlexCreatePointNumbering - Create a global numbering for all points.
8125 
8126   Collective on dm
8127 
8128   Input Parameter:
8129 . dm   - The DMPlex object
8130 
8131   Output Parameter:
8132 . globalPointNumbers - Global numbers for all points on this process
8133 
8134   Notes:
8135 
8136   The point numbering IS is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8137   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8138   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8139   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8140 
8141   The partitioned mesh is
8142 ```
8143  (2)--0--(3)--1--(4)    (1)--0--(2)
8144 ```
8145   and its global numbering is
8146 ```
8147   (3)--0--(4)--1--(5)--2--(6)
8148 ```
8149   Then the global numbering is provided as
8150 ```
8151 [0] Number of indices in set 5
8152 [0] 0 0
8153 [0] 1 1
8154 [0] 2 3
8155 [0] 3 4
8156 [0] 4 -6
8157 [1] Number of indices in set 3
8158 [1] 0 2
8159 [1] 1 5
8160 [1] 2 6
8161 ```
8162 
8163   Level: developer
8164 
8165 .seealso `DMPlexGetCellNumbering()`
8166 @*/
8167 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8168 {
8169   IS             nums[4];
8170   PetscInt       depths[4], gdepths[4], starts[4];
8171   PetscInt       depth, d, shift = 0;
8172 
8173   PetscFunctionBegin;
8174   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8175   PetscCall(DMPlexGetDepth(dm, &depth));
8176   /* For unstratified meshes use dim instead of depth */
8177   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8178   for (d = 0; d <= depth; ++d) {
8179     PetscInt end;
8180 
8181     depths[d] = depth-d;
8182     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8183     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8184   }
8185   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8186   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8187   for (d = 0; d <= depth; ++d) {
8188     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]);
8189   }
8190   for (d = 0; d <= depth; ++d) {
8191     PetscInt pStart, pEnd, gsize;
8192 
8193     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8194     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8195     shift += gsize;
8196   }
8197   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8198   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8199   PetscFunctionReturn(0);
8200 }
8201 
8202 /*@
8203   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8204 
8205   Input Parameter:
8206 . dm - The DMPlex object
8207 
8208   Output Parameter:
8209 . ranks - The rank field
8210 
8211   Options Database Keys:
8212 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8213 
8214   Level: intermediate
8215 
8216 .seealso: `DMView()`
8217 @*/
8218 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8219 {
8220   DM             rdm;
8221   PetscFE        fe;
8222   PetscScalar   *r;
8223   PetscMPIInt    rank;
8224   DMPolytopeType ct;
8225   PetscInt       dim, cStart, cEnd, c;
8226   PetscBool      simplex;
8227 
8228   PetscFunctionBeginUser;
8229   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8230   PetscValidPointer(ranks, 2);
8231   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8232   PetscCall(DMClone(dm, &rdm));
8233   PetscCall(DMGetDimension(rdm, &dim));
8234   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8235   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8236   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8237   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8238   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8239   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8240   PetscCall(PetscFEDestroy(&fe));
8241   PetscCall(DMCreateDS(rdm));
8242   PetscCall(DMCreateGlobalVector(rdm, ranks));
8243   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8244   PetscCall(VecGetArray(*ranks, &r));
8245   for (c = cStart; c < cEnd; ++c) {
8246     PetscScalar *lr;
8247 
8248     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8249     if (lr) *lr = rank;
8250   }
8251   PetscCall(VecRestoreArray(*ranks, &r));
8252   PetscCall(DMDestroy(&rdm));
8253   PetscFunctionReturn(0);
8254 }
8255 
8256 /*@
8257   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8258 
8259   Input Parameters:
8260 + dm    - The DMPlex
8261 - label - The DMLabel
8262 
8263   Output Parameter:
8264 . val - The label value field
8265 
8266   Options Database Keys:
8267 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8268 
8269   Level: intermediate
8270 
8271 .seealso: `DMView()`
8272 @*/
8273 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8274 {
8275   DM             rdm;
8276   PetscFE        fe;
8277   PetscScalar   *v;
8278   PetscInt       dim, cStart, cEnd, c;
8279 
8280   PetscFunctionBeginUser;
8281   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8282   PetscValidPointer(label, 2);
8283   PetscValidPointer(val, 3);
8284   PetscCall(DMClone(dm, &rdm));
8285   PetscCall(DMGetDimension(rdm, &dim));
8286   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8287   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8288   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8289   PetscCall(PetscFEDestroy(&fe));
8290   PetscCall(DMCreateDS(rdm));
8291   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8292   PetscCall(DMCreateGlobalVector(rdm, val));
8293   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8294   PetscCall(VecGetArray(*val, &v));
8295   for (c = cStart; c < cEnd; ++c) {
8296     PetscScalar *lv;
8297     PetscInt     cval;
8298 
8299     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8300     PetscCall(DMLabelGetValue(label, c, &cval));
8301     *lv = cval;
8302   }
8303   PetscCall(VecRestoreArray(*val, &v));
8304   PetscCall(DMDestroy(&rdm));
8305   PetscFunctionReturn(0);
8306 }
8307 
8308 /*@
8309   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8310 
8311   Input Parameter:
8312 . dm - The DMPlex object
8313 
8314   Notes:
8315   This is a useful diagnostic when creating meshes programmatically.
8316 
8317   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8318 
8319   Level: developer
8320 
8321 .seealso: `DMCreate()`, `DMSetFromOptions()`
8322 @*/
8323 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8324 {
8325   PetscSection    coneSection, supportSection;
8326   const PetscInt *cone, *support;
8327   PetscInt        coneSize, c, supportSize, s;
8328   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8329   PetscBool       storagecheck = PETSC_TRUE;
8330 
8331   PetscFunctionBegin;
8332   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8333   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8334   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8335   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8336   /* Check that point p is found in the support of its cone points, and vice versa */
8337   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8338   for (p = pStart; p < pEnd; ++p) {
8339     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8340     PetscCall(DMPlexGetCone(dm, p, &cone));
8341     for (c = 0; c < coneSize; ++c) {
8342       PetscBool dup = PETSC_FALSE;
8343       PetscInt  d;
8344       for (d = c-1; d >= 0; --d) {
8345         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8346       }
8347       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8348       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8349       for (s = 0; s < supportSize; ++s) {
8350         if (support[s] == p) break;
8351       }
8352       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8353         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8354         for (s = 0; s < coneSize; ++s) {
8355           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8356         }
8357         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8358         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8359         for (s = 0; s < supportSize; ++s) {
8360           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8361         }
8362         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8363         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]);
8364         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8365       }
8366     }
8367     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8368     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8369     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8370     PetscCall(DMPlexGetSupport(dm, p, &support));
8371     for (s = 0; s < supportSize; ++s) {
8372       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8373       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8374       for (c = 0; c < coneSize; ++c) {
8375         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8376         if (cone[c] != pp) { c = 0; break; }
8377         if (cone[c] == p) break;
8378       }
8379       if (c >= coneSize) {
8380         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8381         for (c = 0; c < supportSize; ++c) {
8382           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8383         }
8384         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8385         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8386         for (c = 0; c < coneSize; ++c) {
8387           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8388         }
8389         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8390         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8391       }
8392     }
8393   }
8394   if (storagecheck) {
8395     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8396     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8397     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8398   }
8399   PetscFunctionReturn(0);
8400 }
8401 
8402 /*
8403   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.
8404 */
8405 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8406 {
8407   DMPolytopeType  cct;
8408   PetscInt        ptpoints[4];
8409   const PetscInt *cone, *ccone, *ptcone;
8410   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8411 
8412   PetscFunctionBegin;
8413   *unsplit = 0;
8414   switch (ct) {
8415     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8416       ptpoints[npt++] = c;
8417       break;
8418     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8419       PetscCall(DMPlexGetCone(dm, c, &cone));
8420       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8421       for (cp = 0; cp < coneSize; ++cp) {
8422         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8423         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8424       }
8425       break;
8426     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8427     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8428       PetscCall(DMPlexGetCone(dm, c, &cone));
8429       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8430       for (cp = 0; cp < coneSize; ++cp) {
8431         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8432         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8433         for (ccp = 0; ccp < cconeSize; ++ccp) {
8434           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8435           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8436             PetscInt p;
8437             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8438             if (p == npt) ptpoints[npt++] = ccone[ccp];
8439           }
8440         }
8441       }
8442       break;
8443     default: break;
8444   }
8445   for (pt = 0; pt < npt; ++pt) {
8446     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8447     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8448   }
8449   PetscFunctionReturn(0);
8450 }
8451 
8452 /*@
8453   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8454 
8455   Input Parameters:
8456 + dm - The DMPlex object
8457 - cellHeight - Normally 0
8458 
8459   Notes:
8460   This is a useful diagnostic when creating meshes programmatically.
8461   Currently applicable only to homogeneous simplex or tensor meshes.
8462 
8463   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8464 
8465   Level: developer
8466 
8467 .seealso: `DMCreate()`, `DMSetFromOptions()`
8468 @*/
8469 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8470 {
8471   DMPlexInterpolatedFlag interp;
8472   DMPolytopeType         ct;
8473   PetscInt               vStart, vEnd, cStart, cEnd, c;
8474 
8475   PetscFunctionBegin;
8476   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8477   PetscCall(DMPlexIsInterpolated(dm, &interp));
8478   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8479   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8480   for (c = cStart; c < cEnd; ++c) {
8481     PetscInt *closure = NULL;
8482     PetscInt  coneSize, closureSize, cl, Nv = 0;
8483 
8484     PetscCall(DMPlexGetCellType(dm, c, &ct));
8485     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8486     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8487     if (interp == DMPLEX_INTERPOLATED_FULL) {
8488       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8489       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));
8490     }
8491     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8492     for (cl = 0; cl < closureSize*2; cl += 2) {
8493       const PetscInt p = closure[cl];
8494       if ((p >= vStart) && (p < vEnd)) ++Nv;
8495     }
8496     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8497     /* Special Case: Tensor faces with identified vertices */
8498     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8499       PetscInt unsplit;
8500 
8501       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8502       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8503     }
8504     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));
8505   }
8506   PetscFunctionReturn(0);
8507 }
8508 
8509 /*@
8510   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8511 
8512   Collective
8513 
8514   Input Parameters:
8515 + dm - The DMPlex object
8516 - cellHeight - Normally 0
8517 
8518   Notes:
8519   This is a useful diagnostic when creating meshes programmatically.
8520   This routine is only relevant for meshes that are fully interpolated across all ranks.
8521   It will error out if a partially interpolated mesh is given on some rank.
8522   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8523 
8524   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8525 
8526   Level: developer
8527 
8528 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8529 @*/
8530 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8531 {
8532   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8533   DMPlexInterpolatedFlag interpEnum;
8534 
8535   PetscFunctionBegin;
8536   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8537   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8538   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8539   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8540     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8541     PetscFunctionReturn(0);
8542   }
8543 
8544   PetscCall(DMGetDimension(dm, &dim));
8545   PetscCall(DMPlexGetDepth(dm, &depth));
8546   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8547   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8548     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8549     for (c = cStart; c < cEnd; ++c) {
8550       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8551       const DMPolytopeType *faceTypes;
8552       DMPolytopeType        ct;
8553       PetscInt              numFaces, coneSize, f;
8554       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8555 
8556       PetscCall(DMPlexGetCellType(dm, c, &ct));
8557       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8558       if (unsplit) continue;
8559       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8560       PetscCall(DMPlexGetCone(dm, c, &cone));
8561       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8562       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8563       for (cl = 0; cl < closureSize*2; cl += 2) {
8564         const PetscInt p = closure[cl];
8565         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8566       }
8567       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8568       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);
8569       for (f = 0; f < numFaces; ++f) {
8570         DMPolytopeType fct;
8571         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8572 
8573         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8574         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8575         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8576           const PetscInt p = fclosure[cl];
8577           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8578         }
8579         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]);
8580         for (v = 0; v < fnumCorners; ++v) {
8581           if (fclosure[v] != faces[fOff+v]) {
8582             PetscInt v1;
8583 
8584             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8585             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8586             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8587             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff+v1]));
8588             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8589             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]);
8590           }
8591         }
8592         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8593         fOff += faceSizes[f];
8594       }
8595       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8596       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8597     }
8598   }
8599   PetscFunctionReturn(0);
8600 }
8601 
8602 /*@
8603   DMPlexCheckGeometry - Check the geometry of mesh cells
8604 
8605   Input Parameter:
8606 . dm - The DMPlex object
8607 
8608   Notes:
8609   This is a useful diagnostic when creating meshes programmatically.
8610 
8611   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8612 
8613   Level: developer
8614 
8615 .seealso: `DMCreate()`, `DMSetFromOptions()`
8616 @*/
8617 PetscErrorCode DMPlexCheckGeometry(DM dm)
8618 {
8619   Vec       coordinates;
8620   PetscReal detJ, J[9], refVol = 1.0;
8621   PetscReal vol;
8622   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8623 
8624   PetscFunctionBegin;
8625   PetscCall(DMGetDimension(dm, &dim));
8626   PetscCall(DMGetCoordinateDim(dm, &dE));
8627   if (dim != dE) PetscFunctionReturn(0);
8628   PetscCall(DMPlexGetDepth(dm, &depth));
8629   for (d = 0; d < dim; ++d) refVol *= 2.0;
8630   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8631   /* Make sure local coordinates are created, because that step is collective */
8632   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8633   for (c = cStart; c < cEnd; ++c) {
8634     DMPolytopeType ct;
8635     PetscInt       unsplit;
8636     PetscBool      ignoreZeroVol = PETSC_FALSE;
8637 
8638     PetscCall(DMPlexGetCellType(dm, c, &ct));
8639     switch (ct) {
8640       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8641       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8642       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8643         ignoreZeroVol = PETSC_TRUE; break;
8644       default: break;
8645     }
8646     switch (ct) {
8647       case DM_POLYTOPE_TRI_PRISM:
8648       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8649       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8650       case DM_POLYTOPE_PYRAMID:
8651         continue;
8652       default: break;
8653     }
8654     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8655     if (unsplit) continue;
8656     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8657     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);
8658     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ*refVol)));
8659     /* This should work with periodicity since DG coordinates should be used */
8660     if (depth > 1) {
8661       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8662       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);
8663       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double) vol));
8664     }
8665   }
8666   PetscFunctionReturn(0);
8667 }
8668 
8669 /*@
8670   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8671 
8672   Collective
8673 
8674   Input Parameters:
8675 + dm - The DMPlex object
8676 - pointSF - The Point SF, or NULL for Point SF attached to DM
8677 
8678   Notes:
8679   This is mainly intended for debugging/testing purposes.
8680 
8681   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8682 
8683   Level: developer
8684 
8685 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8686 @*/
8687 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF)
8688 {
8689   PetscInt        l, nleaves, nroots, overlap;
8690   const PetscInt *locals;
8691   const PetscSFNode *remotes;
8692   PetscBool       distributed;
8693   MPI_Comm        comm;
8694   PetscMPIInt     rank;
8695 
8696   PetscFunctionBegin;
8697   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8698   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8699   else         pointSF = dm->sf;
8700   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8701   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8702   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8703   {
8704     PetscMPIInt    mpiFlag;
8705 
8706     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF),&mpiFlag));
8707     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)",mpiFlag);
8708   }
8709   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8710   PetscCall(DMPlexIsDistributed(dm, &distributed));
8711   if (!distributed) {
8712     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);
8713     PetscFunctionReturn(0);
8714   }
8715   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);
8716   PetscCall(DMPlexGetOverlap(dm, &overlap));
8717 
8718   /* Check SF graph is compatible with DMPlex chart */
8719   {
8720     PetscInt pStart, pEnd, maxLeaf;
8721 
8722     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8723     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8724     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd-pStart, nroots);
8725     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8726   }
8727 
8728   /* Check Point SF has no local points referenced */
8729   for (l = 0; l < nleaves; l++) {
8730     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);
8731   }
8732 
8733   /* Check there are no cells in interface */
8734   if (!overlap) {
8735     PetscInt cellHeight, cStart, cEnd;
8736 
8737     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8738     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8739     for (l = 0; l < nleaves; ++l) {
8740       const PetscInt point = locals ? locals[l] : l;
8741 
8742       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8743     }
8744   }
8745 
8746   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8747   {
8748     const PetscInt *rootdegree;
8749 
8750     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8751     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8752     for (l = 0; l < nleaves; ++l) {
8753       const PetscInt  point = locals ? locals[l] : l;
8754       const PetscInt *cone;
8755       PetscInt        coneSize, c, idx;
8756 
8757       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8758       PetscCall(DMPlexGetCone(dm, point, &cone));
8759       for (c = 0; c < coneSize; ++c) {
8760         if (!rootdegree[cone[c]]) {
8761           if (locals) {
8762             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8763           } else {
8764             idx = (cone[c] < nleaves) ? cone[c] : -1;
8765           }
8766           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8767         }
8768       }
8769     }
8770   }
8771   PetscFunctionReturn(0);
8772 }
8773 
8774 /*@
8775   DMPlexCheck - Perform various checks of Plex sanity
8776 
8777   Input Parameter:
8778 . dm - The DMPlex object
8779 
8780   Notes:
8781   This is a useful diagnostic when creating meshes programmatically.
8782 
8783   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8784 
8785   Currently does not include DMPlexCheckCellShape().
8786 
8787   Level: developer
8788 
8789 .seealso: DMCreate(), DMSetFromOptions()
8790 @*/
8791 PetscErrorCode DMPlexCheck(DM dm)
8792 {
8793   PetscInt cellHeight;
8794 
8795   PetscFunctionBegin;
8796   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8797   PetscCall(DMPlexCheckSymmetry(dm));
8798   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8799   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8800   PetscCall(DMPlexCheckGeometry(dm));
8801   PetscCall(DMPlexCheckPointSF(dm, NULL));
8802   PetscCall(DMPlexCheckInterfaceCones(dm));
8803   PetscFunctionReturn(0);
8804 }
8805 
8806 typedef struct cell_stats
8807 {
8808   PetscReal min, max, sum, squaresum;
8809   PetscInt  count;
8810 } cell_stats_t;
8811 
8812 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8813 {
8814   PetscInt i, N = *len;
8815 
8816   for (i = 0; i < N; i++) {
8817     cell_stats_t *A = (cell_stats_t *) a;
8818     cell_stats_t *B = (cell_stats_t *) b;
8819 
8820     B->min = PetscMin(A->min,B->min);
8821     B->max = PetscMax(A->max,B->max);
8822     B->sum += A->sum;
8823     B->squaresum += A->squaresum;
8824     B->count += A->count;
8825   }
8826 }
8827 
8828 /*@
8829   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8830 
8831   Collective on dm
8832 
8833   Input Parameters:
8834 + dm        - The DMPlex object
8835 . output    - If true, statistics will be displayed on stdout
8836 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8837 
8838   Notes:
8839   This is mainly intended for debugging/testing purposes.
8840 
8841   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8842 
8843   Level: developer
8844 
8845 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8846 @*/
8847 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8848 {
8849   DM             dmCoarse;
8850   cell_stats_t   stats, globalStats;
8851   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8852   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8853   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8854   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8855   PetscMPIInt    rank,size;
8856 
8857   PetscFunctionBegin;
8858   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8859   stats.min   = PETSC_MAX_REAL;
8860   stats.max   = PETSC_MIN_REAL;
8861   stats.sum   = stats.squaresum = 0.;
8862   stats.count = 0;
8863 
8864   PetscCallMPI(MPI_Comm_size(comm, &size));
8865   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8866   PetscCall(DMGetCoordinateDim(dm,&cdim));
8867   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8868   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8869   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8870   for (c = cStart; c < cEnd; c++) {
8871     PetscInt  i;
8872     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8873 
8874     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8875     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8876     for (i = 0; i < PetscSqr(cdim); ++i) {
8877       frobJ    += J[i] * J[i];
8878       frobInvJ += invJ[i] * invJ[i];
8879     }
8880     cond2 = frobJ * frobInvJ;
8881     cond  = PetscSqrtReal(cond2);
8882 
8883     stats.min        = PetscMin(stats.min,cond);
8884     stats.max        = PetscMax(stats.max,cond);
8885     stats.sum       += cond;
8886     stats.squaresum += cond2;
8887     stats.count++;
8888     if (output && cond > limit) {
8889       PetscSection coordSection;
8890       Vec          coordsLocal;
8891       PetscScalar *coords = NULL;
8892       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8893 
8894       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8895       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8896       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8897       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double) cond));
8898       for (i = 0; i < Nv/cdim; ++i) {
8899         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8900         for (d = 0; d < cdim; ++d) {
8901           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8902           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8903         }
8904         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8905       }
8906       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8907       for (cl = 0; cl < clSize*2; cl += 2) {
8908         const PetscInt edge = closure[cl];
8909 
8910         if ((edge >= eStart) && (edge < eEnd)) {
8911           PetscReal len;
8912 
8913           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8914           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double) len));
8915         }
8916       }
8917       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8918       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8919     }
8920   }
8921   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8922 
8923   if (size > 1) {
8924     PetscMPIInt   blockLengths[2] = {4,1};
8925     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8926     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8927     MPI_Op        statReduce;
8928 
8929     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8930     PetscCallMPI(MPI_Type_commit(&statType));
8931     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8932     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8933     PetscCallMPI(MPI_Op_free(&statReduce));
8934     PetscCallMPI(MPI_Type_free(&statType));
8935   } else {
8936     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8937   }
8938   if (rank == 0) {
8939     count = globalStats.count;
8940     min   = globalStats.min;
8941     max   = globalStats.max;
8942     mean  = globalStats.sum / globalStats.count;
8943     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8944   }
8945 
8946   if (output) {
8947     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));
8948   }
8949   PetscCall(PetscFree2(J,invJ));
8950 
8951   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8952   if (dmCoarse) {
8953     PetscBool isplex;
8954 
8955     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8956     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8957   }
8958   PetscFunctionReturn(0);
8959 }
8960 
8961 /*@
8962   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8963   orthogonal quality below given tolerance.
8964 
8965   Collective on dm
8966 
8967   Input Parameters:
8968 + dm   - The DMPlex object
8969 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8970 - atol - [0, 1] Absolute tolerance for tagging cells.
8971 
8972   Output Parameters:
8973 + OrthQual      - Vec containing orthogonal quality per cell
8974 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8975 
8976   Options Database Keys:
8977 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8978 supported.
8979 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8980 
8981   Notes:
8982   Orthogonal quality is given by the following formula:
8983 
8984   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8985 
8986   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
8987   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8988   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8989   calculating the cosine of the angle between these vectors.
8990 
8991   Orthogonal quality ranges from 1 (best) to 0 (worst).
8992 
8993   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8994   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8995 
8996   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8997 
8998   Level: intermediate
8999 
9000 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
9001 @*/
9002 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9003 {
9004   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9005   PetscInt                *idx;
9006   PetscScalar             *oqVals;
9007   const PetscScalar       *cellGeomArr, *faceGeomArr;
9008   PetscReal               *ci, *fi, *Ai;
9009   MPI_Comm                comm;
9010   Vec                     cellgeom, facegeom;
9011   DM                      dmFace, dmCell;
9012   IS                      glob;
9013   ISLocalToGlobalMapping  ltog;
9014   PetscViewer             vwr;
9015 
9016   PetscFunctionBegin;
9017   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9018   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
9019   PetscValidPointer(OrthQual, 4);
9020   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
9021   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
9022   PetscCall(DMGetDimension(dm, &nc));
9023   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9024   {
9025     DMPlexInterpolatedFlag interpFlag;
9026 
9027     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9028     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9029       PetscMPIInt rank;
9030 
9031       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9032       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9033     }
9034   }
9035   if (OrthQualLabel) {
9036     PetscValidPointer(OrthQualLabel, 5);
9037     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9038     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9039   } else {*OrthQualLabel = NULL;}
9040   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9041   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9042   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9043   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9044   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9045   PetscCall(VecCreate(comm, OrthQual));
9046   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9047   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
9048   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9049   PetscCall(VecSetUp(*OrthQual));
9050   PetscCall(ISDestroy(&glob));
9051   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9052   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9053   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9054   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9055   PetscCall(VecGetDM(cellgeom, &dmCell));
9056   PetscCall(VecGetDM(facegeom, &dmFace));
9057   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9058   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
9059     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9060     PetscInt           cellarr[2], *adj = NULL;
9061     PetscScalar        *cArr, *fArr;
9062     PetscReal          minvalc = 1.0, minvalf = 1.0;
9063     PetscFVCellGeom    *cg;
9064 
9065     idx[cellIter] = cell-cStart;
9066     cellarr[0] = cell;
9067     /* Make indexing into cellGeom easier */
9068     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9069     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9070     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9071     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9072     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
9073       PetscInt         i;
9074       const PetscInt   neigh = adj[cellneigh];
9075       PetscReal        normci = 0, normfi = 0, normai = 0;
9076       PetscFVCellGeom  *cgneigh;
9077       PetscFVFaceGeom  *fg;
9078 
9079       /* Don't count ourselves in the neighbor list */
9080       if (neigh == cell) continue;
9081       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9082       cellarr[1] = neigh;
9083       {
9084         PetscInt       numcovpts;
9085         const PetscInt *covpts;
9086 
9087         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9088         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9089         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9090       }
9091 
9092       /* Compute c_i, f_i and their norms */
9093       for (i = 0; i < nc; i++) {
9094         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9095         fi[i] = fg->centroid[i] - cg->centroid[i];
9096         Ai[i] = fg->normal[i];
9097         normci += PetscPowReal(ci[i], 2);
9098         normfi += PetscPowReal(fi[i], 2);
9099         normai += PetscPowReal(Ai[i], 2);
9100       }
9101       normci = PetscSqrtReal(normci);
9102       normfi = PetscSqrtReal(normfi);
9103       normai = PetscSqrtReal(normai);
9104 
9105       /* Normalize and compute for each face-cell-normal pair */
9106       for (i = 0; i < nc; i++) {
9107         ci[i] = ci[i]/normci;
9108         fi[i] = fi[i]/normfi;
9109         Ai[i] = Ai[i]/normai;
9110         /* PetscAbs because I don't know if normals are guaranteed to point out */
9111         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9112         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9113       }
9114       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9115         minvalc = PetscRealPart(cArr[cellneighiter]);
9116       }
9117       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9118         minvalf = PetscRealPart(fArr[cellneighiter]);
9119       }
9120     }
9121     PetscCall(PetscFree(adj));
9122     PetscCall(PetscFree2(cArr, fArr));
9123     /* Defer to cell if they're equal */
9124     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9125     if (OrthQualLabel) {
9126       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9127     }
9128   }
9129   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9130   PetscCall(VecAssemblyBegin(*OrthQual));
9131   PetscCall(VecAssemblyEnd(*OrthQual));
9132   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9133   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9134   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9135   if (OrthQualLabel) {
9136     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9137   }
9138   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9139   PetscCall(PetscViewerDestroy(&vwr));
9140   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9141   PetscFunctionReturn(0);
9142 }
9143 
9144 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9145  * interpolator construction */
9146 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9147 {
9148   PetscSection   section, newSection, gsection;
9149   PetscSF        sf;
9150   PetscBool      hasConstraints, ghasConstraints;
9151 
9152   PetscFunctionBegin;
9153   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9154   PetscValidPointer(odm,2);
9155   PetscCall(DMGetLocalSection(dm, &section));
9156   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9157   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9158   if (!ghasConstraints) {
9159     PetscCall(PetscObjectReference((PetscObject)dm));
9160     *odm = dm;
9161     PetscFunctionReturn(0);
9162   }
9163   PetscCall(DMClone(dm, odm));
9164   PetscCall(DMCopyFields(dm, *odm));
9165   PetscCall(DMGetLocalSection(*odm, &newSection));
9166   PetscCall(DMGetPointSF(*odm, &sf));
9167   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9168   PetscCall(DMSetGlobalSection(*odm, gsection));
9169   PetscCall(PetscSectionDestroy(&gsection));
9170   PetscFunctionReturn(0);
9171 }
9172 
9173 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9174 {
9175   DM             dmco, dmfo;
9176   Mat            interpo;
9177   Vec            rscale;
9178   Vec            cglobalo, clocal;
9179   Vec            fglobal, fglobalo, flocal;
9180   PetscBool      regular;
9181 
9182   PetscFunctionBegin;
9183   PetscCall(DMGetFullDM(dmc, &dmco));
9184   PetscCall(DMGetFullDM(dmf, &dmfo));
9185   PetscCall(DMSetCoarseDM(dmfo, dmco));
9186   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9187   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9188   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9189   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9190   PetscCall(DMCreateLocalVector(dmc, &clocal));
9191   PetscCall(VecSet(cglobalo, 0.));
9192   PetscCall(VecSet(clocal, 0.));
9193   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9194   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9195   PetscCall(DMCreateLocalVector(dmf, &flocal));
9196   PetscCall(VecSet(fglobal, 0.));
9197   PetscCall(VecSet(fglobalo, 0.));
9198   PetscCall(VecSet(flocal, 0.));
9199   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9200   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9201   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9202   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9203   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9204   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9205   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9206   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9207   *shift = fglobal;
9208   PetscCall(VecDestroy(&flocal));
9209   PetscCall(VecDestroy(&fglobalo));
9210   PetscCall(VecDestroy(&clocal));
9211   PetscCall(VecDestroy(&cglobalo));
9212   PetscCall(VecDestroy(&rscale));
9213   PetscCall(MatDestroy(&interpo));
9214   PetscCall(DMDestroy(&dmfo));
9215   PetscCall(DMDestroy(&dmco));
9216   PetscFunctionReturn(0);
9217 }
9218 
9219 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9220 {
9221   PetscObject    shifto;
9222   Vec            shift;
9223 
9224   PetscFunctionBegin;
9225   if (!interp) {
9226     Vec rscale;
9227 
9228     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9229     PetscCall(VecDestroy(&rscale));
9230   } else {
9231     PetscCall(PetscObjectReference((PetscObject)interp));
9232   }
9233   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9234   if (!shifto) {
9235     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9236     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9237     shifto = (PetscObject) shift;
9238     PetscCall(VecDestroy(&shift));
9239   }
9240   shift = (Vec) shifto;
9241   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9242   PetscCall(VecAXPY(fineSol, 1.0, shift));
9243   PetscCall(MatDestroy(&interp));
9244   PetscFunctionReturn(0);
9245 }
9246 
9247 /* Pointwise interpolation
9248      Just code FEM for now
9249      u^f = I u^c
9250      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9251      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9252      I_{ij} = psi^f_i phi^c_j
9253 */
9254 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9255 {
9256   PetscSection   gsc, gsf;
9257   PetscInt       m, n;
9258   void          *ctx;
9259   DM             cdm;
9260   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9261 
9262   PetscFunctionBegin;
9263   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9264   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9265   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9266   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9267 
9268   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9269   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9270   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9271   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9272   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9273 
9274   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9275   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9276   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9277   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9278   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9279   if (scaling) {
9280     /* Use naive scaling */
9281     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9282   }
9283   PetscFunctionReturn(0);
9284 }
9285 
9286 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9287 {
9288   VecScatter     ctx;
9289 
9290   PetscFunctionBegin;
9291   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9292   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9293   PetscCall(VecScatterDestroy(&ctx));
9294   PetscFunctionReturn(0);
9295 }
9296 
9297 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9298                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9299                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9300                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9301 {
9302   const PetscInt Nc = uOff[1] - uOff[0];
9303   PetscInt       c;
9304   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9305 }
9306 
9307 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9308 {
9309   DM             dmc;
9310   PetscDS        ds;
9311   Vec            ones, locmass;
9312   IS             cellIS;
9313   PetscFormKey   key;
9314   PetscInt       depth;
9315 
9316   PetscFunctionBegin;
9317   PetscCall(DMClone(dm, &dmc));
9318   PetscCall(DMCopyDisc(dm, dmc));
9319   PetscCall(DMGetDS(dmc, &ds));
9320   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9321   PetscCall(DMCreateGlobalVector(dmc, mass));
9322   PetscCall(DMGetLocalVector(dmc, &ones));
9323   PetscCall(DMGetLocalVector(dmc, &locmass));
9324   PetscCall(DMPlexGetDepth(dmc, &depth));
9325   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9326   PetscCall(VecSet(locmass, 0.0));
9327   PetscCall(VecSet(ones, 1.0));
9328   key.label = NULL;
9329   key.value = 0;
9330   key.field = 0;
9331   key.part  = 0;
9332   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9333   PetscCall(ISDestroy(&cellIS));
9334   PetscCall(VecSet(*mass, 0.0));
9335   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9336   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9337   PetscCall(DMRestoreLocalVector(dmc, &ones));
9338   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9339   PetscCall(DMDestroy(&dmc));
9340   PetscFunctionReturn(0);
9341 }
9342 
9343 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9344 {
9345   PetscSection   gsc, gsf;
9346   PetscInt       m, n;
9347   void          *ctx;
9348   DM             cdm;
9349   PetscBool      regular;
9350 
9351   PetscFunctionBegin;
9352   if (dmFine == dmCoarse) {
9353     DM            dmc;
9354     PetscDS       ds;
9355     PetscWeakForm wf;
9356     Vec           u;
9357     IS            cellIS;
9358     PetscFormKey  key;
9359     PetscInt      depth;
9360 
9361     PetscCall(DMClone(dmFine, &dmc));
9362     PetscCall(DMCopyDisc(dmFine, dmc));
9363     PetscCall(DMGetDS(dmc, &ds));
9364     PetscCall(PetscDSGetWeakForm(ds, &wf));
9365     PetscCall(PetscWeakFormClear(wf));
9366     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9367     PetscCall(DMCreateMatrix(dmc, mass));
9368     PetscCall(DMGetLocalVector(dmc, &u));
9369     PetscCall(DMPlexGetDepth(dmc, &depth));
9370     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9371     PetscCall(MatZeroEntries(*mass));
9372     key.label = NULL;
9373     key.value = 0;
9374     key.field = 0;
9375     key.part  = 0;
9376     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9377     PetscCall(ISDestroy(&cellIS));
9378     PetscCall(DMRestoreLocalVector(dmc, &u));
9379     PetscCall(DMDestroy(&dmc));
9380   } else {
9381     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9382     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9383     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9384     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9385 
9386     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9387     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9388     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9389     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9390 
9391     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9392     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9393     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9394     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9395   }
9396   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9397   PetscFunctionReturn(0);
9398 }
9399 
9400 /*@
9401   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9402 
9403   Input Parameter:
9404 . dm - The DMPlex object
9405 
9406   Output Parameter:
9407 . regular - The flag
9408 
9409   Level: intermediate
9410 
9411 .seealso: `DMPlexSetRegularRefinement()`
9412 @*/
9413 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9414 {
9415   PetscFunctionBegin;
9416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9417   PetscValidBoolPointer(regular, 2);
9418   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9419   PetscFunctionReturn(0);
9420 }
9421 
9422 /*@
9423   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9424 
9425   Input Parameters:
9426 + dm - The DMPlex object
9427 - regular - The flag
9428 
9429   Level: intermediate
9430 
9431 .seealso: `DMPlexGetRegularRefinement()`
9432 @*/
9433 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9434 {
9435   PetscFunctionBegin;
9436   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9437   ((DM_Plex *) dm->data)->regularRefinement = regular;
9438   PetscFunctionReturn(0);
9439 }
9440 
9441 /* anchors */
9442 /*@
9443   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9444   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9445 
9446   not collective
9447 
9448   Input Parameter:
9449 . dm - The DMPlex object
9450 
9451   Output Parameters:
9452 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9453 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9454 
9455   Level: intermediate
9456 
9457 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9458 @*/
9459 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9460 {
9461   DM_Plex *plex = (DM_Plex *)dm->data;
9462 
9463   PetscFunctionBegin;
9464   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9465   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9466   if (anchorSection) *anchorSection = plex->anchorSection;
9467   if (anchorIS) *anchorIS = plex->anchorIS;
9468   PetscFunctionReturn(0);
9469 }
9470 
9471 /*@
9472   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9473   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9474   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9475 
9476   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9477   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9478 
9479   collective on dm
9480 
9481   Input Parameters:
9482 + dm - The DMPlex object
9483 . 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).
9484 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9485 
9486   The reference counts of anchorSection and anchorIS are incremented.
9487 
9488   Level: intermediate
9489 
9490 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9491 @*/
9492 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9493 {
9494   DM_Plex        *plex = (DM_Plex *)dm->data;
9495   PetscMPIInt    result;
9496 
9497   PetscFunctionBegin;
9498   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9499   if (anchorSection) {
9500     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9501     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9502     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9503   }
9504   if (anchorIS) {
9505     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9506     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9507     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9508   }
9509 
9510   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9511   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9512   plex->anchorSection = anchorSection;
9513 
9514   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9515   PetscCall(ISDestroy(&plex->anchorIS));
9516   plex->anchorIS = anchorIS;
9517 
9518   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9519     PetscInt size, a, pStart, pEnd;
9520     const PetscInt *anchors;
9521 
9522     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9523     PetscCall(ISGetLocalSize(anchorIS,&size));
9524     PetscCall(ISGetIndices(anchorIS,&anchors));
9525     for (a = 0; a < size; a++) {
9526       PetscInt p;
9527 
9528       p = anchors[a];
9529       if (p >= pStart && p < pEnd) {
9530         PetscInt dof;
9531 
9532         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9533         if (dof) {
9534 
9535           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9536           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %" PetscInt_FMT " cannot be constrained and an anchor",p);
9537         }
9538       }
9539     }
9540     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9541   }
9542   /* reset the generic constraints */
9543   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9544   PetscFunctionReturn(0);
9545 }
9546 
9547 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9548 {
9549   PetscSection anchorSection;
9550   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9551 
9552   PetscFunctionBegin;
9553   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9554   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9555   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9556   PetscCall(PetscSectionGetNumFields(section,&numFields));
9557   if (numFields) {
9558     PetscInt f;
9559     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9560 
9561     for (f = 0; f < numFields; f++) {
9562       PetscInt numComp;
9563 
9564       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9565       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9566     }
9567   }
9568   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9569   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9570   pStart = PetscMax(pStart,sStart);
9571   pEnd   = PetscMin(pEnd,sEnd);
9572   pEnd   = PetscMax(pStart,pEnd);
9573   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9574   for (p = pStart; p < pEnd; p++) {
9575     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9576     if (dof) {
9577       PetscCall(PetscSectionGetDof(section,p,&dof));
9578       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9579       for (f = 0; f < numFields; f++) {
9580         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9581         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9582       }
9583     }
9584   }
9585   PetscCall(PetscSectionSetUp(*cSec));
9586   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9587   PetscFunctionReturn(0);
9588 }
9589 
9590 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9591 {
9592   PetscSection   aSec;
9593   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9594   const PetscInt *anchors;
9595   PetscInt       numFields, f;
9596   IS             aIS;
9597   MatType        mtype;
9598   PetscBool      iscuda,iskokkos;
9599 
9600   PetscFunctionBegin;
9601   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9602   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9603   PetscCall(PetscSectionGetStorageSize(section, &n));
9604   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9605   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9606   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9607   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9608   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9609   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9610   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9611   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9612   else mtype = MATSEQAIJ;
9613   PetscCall(MatSetType(*cMat,mtype));
9614   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9615   PetscCall(ISGetIndices(aIS,&anchors));
9616   /* cSec will be a subset of aSec and section */
9617   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9618   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9619   PetscCall(PetscMalloc1(m+1,&i));
9620   i[0] = 0;
9621   PetscCall(PetscSectionGetNumFields(section,&numFields));
9622   for (p = pStart; p < pEnd; p++) {
9623     PetscInt rDof, rOff, r;
9624 
9625     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9626     if (!rDof) continue;
9627     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9628     if (numFields) {
9629       for (f = 0; f < numFields; f++) {
9630         annz = 0;
9631         for (r = 0; r < rDof; r++) {
9632           a = anchors[rOff + r];
9633           if (a < sStart || a >= sEnd) continue;
9634           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9635           annz += aDof;
9636         }
9637         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9638         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9639         for (q = 0; q < dof; q++) {
9640           i[off + q + 1] = i[off + q] + annz;
9641         }
9642       }
9643     } else {
9644       annz = 0;
9645       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9646       for (q = 0; q < dof; q++) {
9647         a = anchors[rOff + q];
9648         if (a < sStart || a >= sEnd) continue;
9649         PetscCall(PetscSectionGetDof(section,a,&aDof));
9650         annz += aDof;
9651       }
9652       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9653       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9654       for (q = 0; q < dof; q++) {
9655         i[off + q + 1] = i[off + q] + annz;
9656       }
9657     }
9658   }
9659   nnz = i[m];
9660   PetscCall(PetscMalloc1(nnz,&j));
9661   offset = 0;
9662   for (p = pStart; p < pEnd; p++) {
9663     if (numFields) {
9664       for (f = 0; f < numFields; f++) {
9665         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9666         for (q = 0; q < dof; q++) {
9667           PetscInt rDof, rOff, r;
9668           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9669           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9670           for (r = 0; r < rDof; r++) {
9671             PetscInt s;
9672 
9673             a = anchors[rOff + r];
9674             if (a < sStart || a >= sEnd) continue;
9675             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9676             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9677             for (s = 0; s < aDof; s++) {
9678               j[offset++] = aOff + s;
9679             }
9680           }
9681         }
9682       }
9683     } else {
9684       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9685       for (q = 0; q < dof; q++) {
9686         PetscInt rDof, rOff, r;
9687         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9688         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9689         for (r = 0; r < rDof; r++) {
9690           PetscInt s;
9691 
9692           a = anchors[rOff + r];
9693           if (a < sStart || a >= sEnd) continue;
9694           PetscCall(PetscSectionGetDof(section,a,&aDof));
9695           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9696           for (s = 0; s < aDof; s++) {
9697             j[offset++] = aOff + s;
9698           }
9699         }
9700       }
9701     }
9702   }
9703   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9704   PetscCall(PetscFree(i));
9705   PetscCall(PetscFree(j));
9706   PetscCall(ISRestoreIndices(aIS,&anchors));
9707   PetscFunctionReturn(0);
9708 }
9709 
9710 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9711 {
9712   DM_Plex        *plex = (DM_Plex *)dm->data;
9713   PetscSection   anchorSection, section, cSec;
9714   Mat            cMat;
9715 
9716   PetscFunctionBegin;
9717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9718   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9719   if (anchorSection) {
9720     PetscInt Nf;
9721 
9722     PetscCall(DMGetLocalSection(dm,&section));
9723     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9724     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9725     PetscCall(DMGetNumFields(dm,&Nf));
9726     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9727     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9728     PetscCall(PetscSectionDestroy(&cSec));
9729     PetscCall(MatDestroy(&cMat));
9730   }
9731   PetscFunctionReturn(0);
9732 }
9733 
9734 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9735 {
9736   IS             subis;
9737   PetscSection   section, subsection;
9738 
9739   PetscFunctionBegin;
9740   PetscCall(DMGetLocalSection(dm, &section));
9741   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9742   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9743   /* Create subdomain */
9744   PetscCall(DMPlexFilter(dm, label, value, subdm));
9745   /* Create submodel */
9746   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9747   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9748   PetscCall(DMSetLocalSection(*subdm, subsection));
9749   PetscCall(PetscSectionDestroy(&subsection));
9750   PetscCall(DMCopyDisc(dm, *subdm));
9751   /* Create map from submodel to global model */
9752   if (is) {
9753     PetscSection    sectionGlobal, subsectionGlobal;
9754     IS              spIS;
9755     const PetscInt *spmap;
9756     PetscInt       *subIndices;
9757     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9758     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9759 
9760     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9761     PetscCall(ISGetIndices(spIS, &spmap));
9762     PetscCall(PetscSectionGetNumFields(section, &Nf));
9763     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9764     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9765     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9766     for (p = pStart; p < pEnd; ++p) {
9767       PetscInt gdof, pSubSize  = 0;
9768 
9769       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9770       if (gdof > 0) {
9771         for (f = 0; f < Nf; ++f) {
9772           PetscInt fdof, fcdof;
9773 
9774           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9775           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9776           pSubSize += fdof-fcdof;
9777         }
9778         subSize += pSubSize;
9779         if (pSubSize) {
9780           if (bs < 0) {
9781             bs = pSubSize;
9782           } else if (bs != pSubSize) {
9783             /* Layout does not admit a pointwise block size */
9784             bs = 1;
9785           }
9786         }
9787       }
9788     }
9789     /* Must have same blocksize on all procs (some might have no points) */
9790     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9791     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9792     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9793     else                            {bs = bsMinMax[0];}
9794     PetscCall(PetscMalloc1(subSize, &subIndices));
9795     for (p = pStart; p < pEnd; ++p) {
9796       PetscInt gdof, goff;
9797 
9798       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9799       if (gdof > 0) {
9800         const PetscInt point = spmap[p];
9801 
9802         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9803         for (f = 0; f < Nf; ++f) {
9804           PetscInt fdof, fcdof, fc, f2, poff = 0;
9805 
9806           /* Can get rid of this loop by storing field information in the global section */
9807           for (f2 = 0; f2 < f; ++f2) {
9808             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9809             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9810             poff += fdof-fcdof;
9811           }
9812           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9813           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9814           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9815             subIndices[subOff] = goff+poff+fc;
9816           }
9817         }
9818       }
9819     }
9820     PetscCall(ISRestoreIndices(spIS, &spmap));
9821     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9822     if (bs > 1) {
9823       /* We need to check that the block size does not come from non-contiguous fields */
9824       PetscInt i, j, set = 1;
9825       for (i = 0; i < subSize; i += bs) {
9826         for (j = 0; j < bs; ++j) {
9827           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9828         }
9829       }
9830       if (set) PetscCall(ISSetBlockSize(*is, bs));
9831     }
9832     /* Attach nullspace */
9833     for (f = 0; f < Nf; ++f) {
9834       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9835       if ((*subdm)->nullspaceConstructors[f]) break;
9836     }
9837     if (f < Nf) {
9838       MatNullSpace nullSpace;
9839       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9840 
9841       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9842       PetscCall(MatNullSpaceDestroy(&nullSpace));
9843     }
9844   }
9845   PetscFunctionReturn(0);
9846 }
9847 
9848 /*@
9849   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9850 
9851   Input Parameter:
9852 - dm - The DM
9853 
9854   Level: developer
9855 
9856   Options Database Keys:
9857 . -dm_plex_monitor_throughput - Activate the monitor
9858 
9859 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9860 @*/
9861 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9862 {
9863 #if defined(PETSC_USE_LOG)
9864   PetscStageLog      stageLog;
9865   PetscLogEvent      event;
9866   PetscLogStage      stage;
9867   PetscEventPerfInfo eventInfo;
9868   PetscReal          cellRate, flopRate;
9869   PetscInt           cStart, cEnd, Nf, N;
9870   const char        *name;
9871 #endif
9872 
9873   PetscFunctionBegin;
9874   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9875 #if defined(PETSC_USE_LOG)
9876   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9877   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9878   PetscCall(DMGetNumFields(dm, &Nf));
9879   PetscCall(PetscLogGetStageLog(&stageLog));
9880   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9881   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9882   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9883   N        = (cEnd - cStart)*Nf*eventInfo.count;
9884   flopRate = eventInfo.flops/eventInfo.time;
9885   cellRate = N/eventInfo.time;
9886   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)));
9887 #else
9888   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9889 #endif
9890   PetscFunctionReturn(0);
9891 }
9892