xref: /petsc/src/dm/impls/plex/plex.c (revision 792fecdfe9134cce4d631112660ddd34f063bc17)
1 #include <petsc/private/dmpleximpl.h>   /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF,DMPLEX_LocatePoints,DMPLEX_TopologyView,DMPLEX_LabelsView,DMPLEX_CoordinatesView,DMPLEX_SectionView,DMPLEX_GlobalVectorView,DMPLEX_LocalVectorView,DMPLEX_TopologyLoad,DMPLEX_LabelsLoad,DMPLEX_CoordinatesLoad,DMPLEX_SectionLoad,DMPLEX_GlobalVectorLoad,DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph,DMPLEX_RebalRewriteSF,DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm      - The DMPlex object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
28   If the mesh has no cells, this returns PETSC_FALSE.
29 
30   Level: intermediate
31 
32 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
33 @*/
34 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
35 {
36   DMPolytopeType ct;
37   PetscInt       cStart, cEnd;
38 
39   PetscFunctionBegin;
40   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
41   if (cEnd <= cStart) {*simplex = PETSC_FALSE; PetscFunctionReturn(0);}
42   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
43   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
44   PetscFunctionReturn(0);
45 }
46 
47 /*@
48   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
49 
50   Input Parameters:
51 + dm     - The DMPlex object
52 - height - The cell height in the Plex, 0 is the default
53 
54   Output Parameters:
55 + cStart - The first "normal" cell
56 - cEnd   - The upper bound on "normal"" cells
57 
58   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
59 
60   Level: developer
61 
62 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
63 @*/
64 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
65 {
66   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
67   PetscInt       cS, cE, c;
68 
69   PetscFunctionBegin;
70   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
71   for (c = cS; c < cE; ++c) {
72     DMPolytopeType cct;
73 
74     PetscCall(DMPlexGetCellType(dm, c, &cct));
75     if ((PetscInt) cct < 0) break;
76     switch (cct) {
77       case DM_POLYTOPE_POINT:
78       case DM_POLYTOPE_SEGMENT:
79       case DM_POLYTOPE_TRIANGLE:
80       case DM_POLYTOPE_QUADRILATERAL:
81       case DM_POLYTOPE_TETRAHEDRON:
82       case DM_POLYTOPE_HEXAHEDRON:
83         ct = cct;
84         break;
85       default: break;
86     }
87     if (ct != DM_POLYTOPE_UNKNOWN) break;
88   }
89   if (ct != DM_POLYTOPE_UNKNOWN) {
90     DMLabel ctLabel;
91 
92     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
93     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
94     // Reset label for fast lookup
95     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
96   }
97   if (cStart) *cStart = cS;
98   if (cEnd)   *cEnd   = cE;
99   PetscFunctionReturn(0);
100 }
101 
102 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
103 {
104   PetscInt       cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
105   PetscInt       vcdof[2] = {0,0}, globalvcdof[2];
106 
107   PetscFunctionBegin;
108   *ft  = PETSC_VTK_INVALID;
109   PetscCall(DMGetCoordinateDim(dm, &cdim));
110   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
111   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
112   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
113   if (field >= 0) {
114     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
115     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
116   } else {
117     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
118     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
119   }
120   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
121   if (globalvcdof[0]) {
122     *sStart = vStart;
123     *sEnd   = vEnd;
124     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
125     else                        *ft = PETSC_VTK_POINT_FIELD;
126   } else if (globalvcdof[1]) {
127     *sStart = cStart;
128     *sEnd   = cEnd;
129     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
130     else                        *ft = PETSC_VTK_CELL_FIELD;
131   } else {
132     if (field >= 0) {
133       const char *fieldname;
134 
135       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
136       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
137     } else {
138       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section\n"));
139     }
140   }
141   PetscFunctionReturn(0);
142 }
143 
144 /*@
145   DMPlexVecView1D - Plot many 1D solutions on the same line graph
146 
147   Collective on dm
148 
149   Input Parameters:
150 + dm - The DMPlex
151 . n  - The number of vectors
152 . u  - The array of local vectors
153 - viewer - The Draw viewer
154 
155   Level: advanced
156 
157 .seealso: `VecViewFromOptions()`, `VecView()`
158 @*/
159 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
160 {
161   PetscDS            ds;
162   PetscDraw          draw = NULL;
163   PetscDrawLG        lg;
164   Vec                coordinates;
165   const PetscScalar *coords, **sol;
166   PetscReal         *vals;
167   PetscInt          *Nc;
168   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
169   char             **names;
170 
171   PetscFunctionBegin;
172   PetscCall(DMGetDS(dm, &ds));
173   PetscCall(PetscDSGetNumFields(ds, &Nf));
174   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
175   PetscCall(PetscDSGetComponents(ds, &Nc));
176 
177   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
178   if (!draw) PetscFunctionReturn(0);
179   PetscCall(PetscDrawLGCreate(draw, n*Nl, &lg));
180 
181   PetscCall(PetscMalloc3(n, &sol, n*Nl, &names, n*Nl, &vals));
182   for (i = 0, l = 0; i < n; ++i) {
183     const char *vname;
184 
185     PetscCall(PetscObjectGetName((PetscObject) u[i], &vname));
186     for (f = 0; f < Nf; ++f) {
187       PetscObject disc;
188       const char *fname;
189       char        tmpname[PETSC_MAX_PATH_LEN];
190 
191       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
192       /* TODO Create names for components */
193       for (c = 0; c < Nc[f]; ++c, ++l) {
194         PetscCall(PetscObjectGetName(disc, &fname));
195         PetscCall(PetscStrcpy(tmpname, vname));
196         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
197         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
198         PetscCall(PetscStrallocpy(tmpname, &names[l]));
199       }
200     }
201   }
202   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *) names));
203   /* Just add P_1 support for now */
204   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
205   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
206   PetscCall(VecGetArrayRead(coordinates, &coords));
207   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
208   for (v = vStart; v < vEnd; ++v) {
209     PetscScalar *x, *svals;
210 
211     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
212     for (i = 0; i < n; ++i) {
213       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
214       for (l = 0; l < Nl; ++l) vals[i*Nl + l] = PetscRealPart(svals[l]);
215     }
216     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
217   }
218   PetscCall(VecRestoreArrayRead(coordinates, &coords));
219   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
220   for (l = 0; l < n*Nl; ++l) PetscCall(PetscFree(names[l]));
221   PetscCall(PetscFree3(sol, names, vals));
222 
223   PetscCall(PetscDrawLGDraw(lg));
224   PetscCall(PetscDrawLGDestroy(&lg));
225   PetscFunctionReturn(0);
226 }
227 
228 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
229 {
230   DM             dm;
231 
232   PetscFunctionBegin;
233   PetscCall(VecGetDM(u, &dm));
234   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
235   PetscFunctionReturn(0);
236 }
237 
238 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
239 {
240   DM                 dm;
241   PetscSection       s;
242   PetscDraw          draw, popup;
243   DM                 cdm;
244   PetscSection       coordSection;
245   Vec                coordinates;
246   const PetscScalar *coords, *array;
247   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
248   PetscReal          vbound[2], time;
249   PetscBool          flg;
250   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
251   const char        *name;
252   char               title[PETSC_MAX_PATH_LEN];
253 
254   PetscFunctionBegin;
255   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
256   PetscCall(VecGetDM(v, &dm));
257   PetscCall(DMGetCoordinateDim(dm, &dim));
258   PetscCall(DMGetLocalSection(dm, &s));
259   PetscCall(PetscSectionGetNumFields(s, &Nf));
260   PetscCall(DMGetCoarsenLevel(dm, &level));
261   PetscCall(DMGetCoordinateDM(dm, &cdm));
262   PetscCall(DMGetLocalSection(cdm, &coordSection));
263   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
264   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
265   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
266 
267   PetscCall(PetscObjectGetName((PetscObject) v, &name));
268   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
269 
270   PetscCall(VecGetLocalSize(coordinates, &N));
271   PetscCall(VecGetArrayRead(coordinates, &coords));
272   for (c = 0; c < N; c += dim) {
273     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
274     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
275   }
276   PetscCall(VecRestoreArrayRead(coordinates, &coords));
277   PetscCall(PetscDrawClear(draw));
278 
279   /* Could implement something like DMDASelectFields() */
280   for (f = 0; f < Nf; ++f) {
281     DM   fdm = dm;
282     Vec  fv  = v;
283     IS   fis;
284     char prefix[PETSC_MAX_PATH_LEN];
285     const char *fname;
286 
287     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
288     PetscCall(PetscSectionGetFieldName(s, f, &fname));
289 
290     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix,sizeof(prefix)));
291     else               {prefix[0] = '\0';}
292     if (Nf > 1) {
293       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
294       PetscCall(VecGetSubVector(v, fis, &fv));
295       PetscCall(PetscStrlcat(prefix, fname,sizeof(prefix)));
296       PetscCall(PetscStrlcat(prefix, "_",sizeof(prefix)));
297     }
298     for (comp = 0; comp < Nc; ++comp, ++w) {
299       PetscInt nmax = 2;
300 
301       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
302       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
303       else        PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
304       PetscCall(PetscDrawSetTitle(draw, title));
305 
306       /* TODO Get max and min only for this component */
307       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
308       if (!flg) {
309         PetscCall(VecMin(fv, NULL, &vbound[0]));
310         PetscCall(VecMax(fv, NULL, &vbound[1]));
311         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
312       }
313       PetscCall(PetscDrawGetPopup(draw, &popup));
314       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
315       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
316 
317       PetscCall(VecGetArrayRead(fv, &array));
318       for (c = cStart; c < cEnd; ++c) {
319         PetscScalar *coords = NULL, *a = NULL;
320         PetscInt     numCoords, color[4] = {-1,-1,-1,-1};
321 
322         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
323         if (a) {
324           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
325           color[1] = color[2] = color[3] = color[0];
326         } else {
327           PetscScalar *vals = NULL;
328           PetscInt     numVals, va;
329 
330           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
331           PetscCheck(numVals % Nc == 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
332           switch (numVals/Nc) {
333           case 3: /* P1 Triangle */
334           case 4: /* P1 Quadrangle */
335             for (va = 0; va < numVals/Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp]), vbound[0], vbound[1]);
336             break;
337           case 6: /* P2 Triangle */
338           case 8: /* P2 Quadrangle */
339             for (va = 0; va < numVals/(Nc*2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp + numVals/(Nc*2)]), vbound[0], vbound[1]);
340             break;
341           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals/Nc);
342           }
343           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
344         }
345         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
346         switch (numCoords) {
347         case 6:
348         case 12: /* Localized triangle */
349           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
350           break;
351         case 8:
352         case 16: /* Localized quadrilateral */
353           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
354           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
355           break;
356         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
357         }
358         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
359       }
360       PetscCall(VecRestoreArrayRead(fv, &array));
361       PetscCall(PetscDrawFlush(draw));
362       PetscCall(PetscDrawPause(draw));
363       PetscCall(PetscDrawSave(draw));
364     }
365     if (Nf > 1) {
366       PetscCall(VecRestoreSubVector(v, fis, &fv));
367       PetscCall(ISDestroy(&fis));
368       PetscCall(DMDestroy(&fdm));
369     }
370   }
371   PetscFunctionReturn(0);
372 }
373 
374 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
375 {
376   DM        dm;
377   PetscDraw draw;
378   PetscInt  dim;
379   PetscBool isnull;
380 
381   PetscFunctionBegin;
382   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
383   PetscCall(PetscDrawIsNull(draw, &isnull));
384   if (isnull) PetscFunctionReturn(0);
385 
386   PetscCall(VecGetDM(v, &dm));
387   PetscCall(DMGetCoordinateDim(dm, &dim));
388   switch (dim) {
389   case 1: PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));break;
390   case 2: PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));break;
391   default: SETERRQ(PetscObjectComm((PetscObject) v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
392   }
393   PetscFunctionReturn(0);
394 }
395 
396 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
397 {
398   DM                      dm;
399   Vec                     locv;
400   const char              *name;
401   PetscSection            section;
402   PetscInt                pStart, pEnd;
403   PetscInt                numFields;
404   PetscViewerVTKFieldType ft;
405 
406   PetscFunctionBegin;
407   PetscCall(VecGetDM(v, &dm));
408   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
409   PetscCall(PetscObjectGetName((PetscObject) v, &name));
410   PetscCall(PetscObjectSetName((PetscObject) locv, name));
411   PetscCall(VecCopy(v, locv));
412   PetscCall(DMGetLocalSection(dm, &section));
413   PetscCall(PetscSectionGetNumFields(section, &numFields));
414   if (!numFields) {
415     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
416     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE,(PetscObject) locv));
417   } else {
418     PetscInt f;
419 
420     for (f = 0; f < numFields; f++) {
421       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
422       if (ft == PETSC_VTK_INVALID) continue;
423       PetscCall(PetscObjectReference((PetscObject)locv));
424       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE,(PetscObject) locv));
425     }
426     PetscCall(VecDestroy(&locv));
427   }
428   PetscFunctionReturn(0);
429 }
430 
431 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
432 {
433   DM             dm;
434   PetscBool      isvtk, ishdf5, isdraw, isglvis;
435 
436   PetscFunctionBegin;
437   PetscCall(VecGetDM(v, &dm));
438   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
439   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk));
440   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5));
441   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw));
442   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis));
443   if (isvtk || ishdf5 || isdraw || isglvis) {
444     PetscInt    i,numFields;
445     PetscObject fe;
446     PetscBool   fem = PETSC_FALSE;
447     Vec         locv = v;
448     const char  *name;
449     PetscInt    step;
450     PetscReal   time;
451 
452     PetscCall(DMGetNumFields(dm, &numFields));
453     for (i=0; i<numFields; i++) {
454       PetscCall(DMGetField(dm, i, NULL, &fe));
455       if (fe->classid == PETSCFE_CLASSID) { fem = PETSC_TRUE; break; }
456     }
457     if (fem) {
458       PetscObject isZero;
459 
460       PetscCall(DMGetLocalVector(dm, &locv));
461       PetscCall(PetscObjectGetName((PetscObject) v, &name));
462       PetscCall(PetscObjectSetName((PetscObject) locv, name));
463       PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
464       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
465       PetscCall(VecCopy(v, locv));
466       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
467       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
468     }
469     if (isvtk) {
470       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
471     } else if (ishdf5) {
472 #if defined(PETSC_HAVE_HDF5)
473       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
474 #else
475       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
476 #endif
477     } else if (isdraw) {
478       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
479     } else if (isglvis) {
480       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
481       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
482       PetscCall(VecView_GLVis(locv, viewer));
483     }
484     if (fem) {
485       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
486       PetscCall(DMRestoreLocalVector(dm, &locv));
487     }
488   } else {
489     PetscBool isseq;
490 
491     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
492     if (isseq) PetscCall(VecView_Seq(v, viewer));
493     else       PetscCall(VecView_MPI(v, viewer));
494   }
495   PetscFunctionReturn(0);
496 }
497 
498 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
499 {
500   DM        dm;
501   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii;
502 
503   PetscFunctionBegin;
504   PetscCall(VecGetDM(v, &dm));
505   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
506   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
507   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
508   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
509   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
510   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
511   if (isvtk || isdraw || isglvis) {
512     Vec         locv;
513     PetscObject isZero;
514     const char *name;
515 
516     PetscCall(DMGetLocalVector(dm, &locv));
517     PetscCall(PetscObjectGetName((PetscObject) v, &name));
518     PetscCall(PetscObjectSetName((PetscObject) locv, name));
519     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
520     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
521     PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
522     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
523     PetscCall(VecView_Plex_Local(locv, viewer));
524     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
525     PetscCall(DMRestoreLocalVector(dm, &locv));
526   } else if (ishdf5) {
527 #if defined(PETSC_HAVE_HDF5)
528     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
529 #else
530     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
531 #endif
532   } else if (isexodusii) {
533 #if defined(PETSC_HAVE_EXODUSII)
534     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
535 #else
536     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
537 #endif
538   } else {
539     PetscBool isseq;
540 
541     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
542     if (isseq) PetscCall(VecView_Seq(v, viewer));
543     else       PetscCall(VecView_MPI(v, viewer));
544   }
545   PetscFunctionReturn(0);
546 }
547 
548 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
549 {
550   DM                dm;
551   MPI_Comm          comm;
552   PetscViewerFormat format;
553   Vec               v;
554   PetscBool         isvtk, ishdf5;
555 
556   PetscFunctionBegin;
557   PetscCall(VecGetDM(originalv, &dm));
558   PetscCall(PetscObjectGetComm((PetscObject) originalv, &comm));
559   PetscCheck(dm,comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
560   PetscCall(PetscViewerGetFormat(viewer, &format));
561   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
562   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,  &isvtk));
563   if (format == PETSC_VIEWER_NATIVE) {
564     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
565     /* this need a better fix */
566     if (dm->useNatural) {
567       if (dm->sfNatural) {
568         const char *vecname;
569         PetscInt    n, nroots;
570 
571         PetscCall(VecGetLocalSize(originalv, &n));
572         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
573         if (n == nroots) {
574           PetscCall(DMGetGlobalVector(dm, &v));
575           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
576           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
577           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
578           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
579         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
580       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
581     } else v = originalv;
582   } else v = originalv;
583 
584   if (ishdf5) {
585 #if defined(PETSC_HAVE_HDF5)
586     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
587 #else
588     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
589 #endif
590   } else if (isvtk) {
591     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
592   } else {
593     PetscBool isseq;
594 
595     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
596     if (isseq) PetscCall(VecView_Seq(v, viewer));
597     else       PetscCall(VecView_MPI(v, viewer));
598   }
599   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
600   PetscFunctionReturn(0);
601 }
602 
603 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
604 {
605   DM             dm;
606   PetscBool      ishdf5;
607 
608   PetscFunctionBegin;
609   PetscCall(VecGetDM(v, &dm));
610   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
611   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
612   if (ishdf5) {
613     DM          dmBC;
614     Vec         gv;
615     const char *name;
616 
617     PetscCall(DMGetOutputDM(dm, &dmBC));
618     PetscCall(DMGetGlobalVector(dmBC, &gv));
619     PetscCall(PetscObjectGetName((PetscObject) v, &name));
620     PetscCall(PetscObjectSetName((PetscObject) gv, name));
621     PetscCall(VecLoad_Default(gv, viewer));
622     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
623     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
624     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
625   } else PetscCall(VecLoad_Default(v, viewer));
626   PetscFunctionReturn(0);
627 }
628 
629 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
630 {
631   DM             dm;
632   PetscBool      ishdf5,isexodusii;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
638   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
639   if (ishdf5) {
640 #if defined(PETSC_HAVE_HDF5)
641     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
642 #else
643     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
644 #endif
645   } else if (isexodusii) {
646 #if defined(PETSC_HAVE_EXODUSII)
647     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
648 #else
649     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
650 #endif
651   } else PetscCall(VecLoad_Default(v, viewer));
652   PetscFunctionReturn(0);
653 }
654 
655 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
656 {
657   DM                dm;
658   PetscViewerFormat format;
659   PetscBool         ishdf5;
660 
661   PetscFunctionBegin;
662   PetscCall(VecGetDM(originalv, &dm));
663   PetscCheck(dm,PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
664   PetscCall(PetscViewerGetFormat(viewer, &format));
665   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
666   if (format == PETSC_VIEWER_NATIVE) {
667     if (dm->useNatural) {
668       if (dm->sfNatural) {
669         if (ishdf5) {
670 #if defined(PETSC_HAVE_HDF5)
671           Vec         v;
672           const char *vecname;
673 
674           PetscCall(DMGetGlobalVector(dm, &v));
675           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
676           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
677           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
678           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
679           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
680           PetscCall(DMRestoreGlobalVector(dm, &v));
681 #else
682           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
683 #endif
684         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
685       }
686     } else PetscCall(VecLoad_Default(originalv, viewer));
687   }
688   PetscFunctionReturn(0);
689 }
690 
691 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
692 {
693   PetscSection       coordSection;
694   Vec                coordinates;
695   DMLabel            depthLabel, celltypeLabel;
696   const char        *name[4];
697   const PetscScalar *a;
698   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
699 
700   PetscFunctionBegin;
701   PetscCall(DMGetDimension(dm, &dim));
702   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
703   PetscCall(DMGetCoordinateSection(dm, &coordSection));
704   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
705   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
706   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
707   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
708   PetscCall(VecGetArrayRead(coordinates, &a));
709   name[0]     = "vertex";
710   name[1]     = "edge";
711   name[dim-1] = "face";
712   name[dim]   = "cell";
713   for (c = cStart; c < cEnd; ++c) {
714     PetscInt *closure = NULL;
715     PetscInt  closureSize, cl, ct;
716 
717     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
718     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
719     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
720     PetscCall(PetscViewerASCIIPushTab(viewer));
721     for (cl = 0; cl < closureSize*2; cl += 2) {
722       PetscInt point = closure[cl], depth, dof, off, d, p;
723 
724       if ((point < pStart) || (point >= pEnd)) continue;
725       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
726       if (!dof) continue;
727       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
728       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
729       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
730       for (p = 0; p < dof/dim; ++p) {
731         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
732         for (d = 0; d < dim; ++d) {
733           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
734           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d])));
735         }
736         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
737       }
738       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
739     }
740     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
741     PetscCall(PetscViewerASCIIPopTab(viewer));
742   }
743   PetscCall(VecRestoreArrayRead(coordinates, &a));
744   PetscFunctionReturn(0);
745 }
746 
747 typedef enum {CS_CARTESIAN, CS_POLAR, CS_CYLINDRICAL, CS_SPHERICAL} CoordSystem;
748 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
749 
750 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
751 {
752   PetscInt       i;
753 
754   PetscFunctionBegin;
755   if (dim > 3) {
756     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) PetscRealPart(x[i])));
757   } else {
758     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
759 
760     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
761     switch (cs) {
762       case CS_CARTESIAN: for (i = 0; i < dim; ++i) trcoords[i] = coords[i];break;
763       case CS_POLAR:
764         PetscCheck(dim == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
765         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
766         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
767         break;
768       case CS_CYLINDRICAL:
769         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
770         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
771         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
772         trcoords[2] = coords[2];
773         break;
774       case CS_SPHERICAL:
775         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
776         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
777         trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
778         trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
779         break;
780     }
781     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) trcoords[i]));
782   }
783   PetscFunctionReturn(0);
784 }
785 
786 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
787 {
788   DM_Plex          *mesh = (DM_Plex*) dm->data;
789   DM                cdm, cdmCell;
790   PetscSection      coordSection, coordSectionCell;
791   Vec               coordinates, coordinatesCell;
792   PetscViewerFormat format;
793 
794   PetscFunctionBegin;
795   PetscCall(DMGetCoordinateDM(dm, &cdm));
796   PetscCall(DMGetCoordinateSection(dm, &coordSection));
797   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
798   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
799   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
800   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
801   PetscCall(PetscViewerGetFormat(viewer, &format));
802   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
803     const char *name;
804     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
805     PetscInt    pStart, pEnd, p, numLabels, l;
806     PetscMPIInt rank, size;
807 
808     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
809     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
810     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
811     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
812     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
813     PetscCall(DMGetDimension(dm, &dim));
814     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
815     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
816     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
817     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
818     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
819     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
820     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
821     for (p = pStart; p < pEnd; ++p) {
822       PetscInt dof, off, s;
823 
824       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
825       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
826       for (s = off; s < off+dof; ++s) {
827         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
828       }
829     }
830     PetscCall(PetscViewerFlush(viewer));
831     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
832     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
833     for (p = pStart; p < pEnd; ++p) {
834       PetscInt dof, off, c;
835 
836       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
837       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
838       for (c = off; c < off+dof; ++c) {
839         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
840       }
841     }
842     PetscCall(PetscViewerFlush(viewer));
843     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
844     if (coordSection && coordinates) {
845       CoordSystem        cs = CS_CARTESIAN;
846       const PetscScalar *array, *arrayCell = NULL;
847       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
848       PetscMPIInt        rank;
849       const char        *name;
850 
851       PetscCall(PetscOptionsGetEnum(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *) &cs, NULL));
852       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
853       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
854       PetscCheck(Nf == 1,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
855       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
856       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
857       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
858       pStart =  PetscMin(pvStart, pcStart);
859       pEnd   =  PetscMax(pvEnd,   pcEnd);
860       PetscCall(PetscObjectGetName((PetscObject) coordinates, &name));
861       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
862       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
863       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
864 
865       PetscCall(VecGetArrayRead(coordinates, &array));
866       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
867       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
868       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
869       for (p = pStart; p < pEnd; ++p) {
870         PetscInt dof, off;
871 
872         if (p >= pvStart && p < pvEnd) {
873           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
874           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
875           if (dof) {
876             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
877             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
878             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
879           }
880         }
881         if (cdmCell && p >= pcStart && p < pcEnd) {
882           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
883           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
884           if (dof) {
885             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
886             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
887             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
888           }
889         }
890       }
891       PetscCall(PetscViewerFlush(viewer));
892       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
893       PetscCall(VecRestoreArrayRead(coordinates, &array));
894       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
895     }
896     PetscCall(DMGetNumLabels(dm, &numLabels));
897     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
898     for (l = 0; l < numLabels; ++l) {
899       DMLabel     label;
900       PetscBool   isdepth;
901       const char *name;
902 
903       PetscCall(DMGetLabelName(dm, l, &name));
904       PetscCall(PetscStrcmp(name, "depth", &isdepth));
905       if (isdepth) continue;
906       PetscCall(DMGetLabel(dm, name, &label));
907       PetscCall(DMLabelView(label, viewer));
908     }
909     if (size > 1) {
910       PetscSF sf;
911 
912       PetscCall(DMGetPointSF(dm, &sf));
913       PetscCall(PetscSFView(sf, viewer));
914     }
915     PetscCall(PetscViewerFlush(viewer));
916   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
917     const char  *name, *color;
918     const char  *defcolors[3]  = {"gray", "orange", "green"};
919     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
920     char         lname[PETSC_MAX_PATH_LEN];
921     PetscReal    scale         = 2.0;
922     PetscReal    tikzscale     = 1.0;
923     PetscBool    useNumbers    = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
924     double       tcoords[3];
925     PetscScalar *coords;
926     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
927     PetscMPIInt  rank, size;
928     char         **names, **colors, **lcolors;
929     PetscBool    flg, lflg;
930     PetscBT      wp = NULL;
931     PetscInt     pEnd, pStart;
932 
933     PetscCall(DMGetDimension(dm, &dim));
934     PetscCall(DMPlexGetDepth(dm, &depth));
935     PetscCall(DMGetNumLabels(dm, &numLabels));
936     numLabels  = PetscMax(numLabels, 10);
937     numColors  = 10;
938     numLColors = 10;
939     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
940     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
941     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
942     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
943     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
944     for (d = 0; d < 4; ++d) drawColors[d]  = PETSC_TRUE;
945     n = 4;
946     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
947     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
948     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
949     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
950     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
951     if (!useLabels) numLabels = 0;
952     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
953     if (!useColors) {
954       numColors = 3;
955       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
956     }
957     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
958     if (!useColors) {
959       numLColors = 4;
960       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
961     }
962     PetscCall(PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
963     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
964     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
965     PetscCheck(!flg || !plotEdges || depth >= dim,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
966     if (depth < dim) plotEdges = PETSC_FALSE;
967     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
968 
969     /* filter points with labelvalue != labeldefaultvalue */
970     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
971     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
972     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
973     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
974     if (lflg) {
975       DMLabel lbl;
976 
977       PetscCall(DMGetLabel(dm, lname, &lbl));
978       if (lbl) {
979         PetscInt val, defval;
980 
981         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
982         PetscCall(PetscBTCreate(pEnd-pStart, &wp));
983         for (c = pStart;  c < pEnd; c++) {
984           PetscInt *closure = NULL;
985           PetscInt  closureSize;
986 
987           PetscCall(DMLabelGetValue(lbl, c, &val));
988           if (val == defval) continue;
989 
990           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
991           for (p = 0; p < closureSize*2; p += 2) {
992             PetscCall(PetscBTSet(wp, closure[p] - pStart));
993           }
994           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
995         }
996       }
997     }
998 
999     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1000     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1001     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1002     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1003 \\documentclass[tikz]{standalone}\n\n\
1004 \\usepackage{pgflibraryshapes}\n\
1005 \\usetikzlibrary{backgrounds}\n\
1006 \\usetikzlibrary{arrows}\n\
1007 \\begin{document}\n"));
1008     if (size > 1) {
1009       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1010       for (p = 0; p < size; ++p) {
1011         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size-1) ? ", and " :  ", "));
1012         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p%numColors], p));
1013       }
1014       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1015     }
1016     if (drawHasse) {
1017       PetscInt maxStratum = PetscMax(vEnd-vStart, PetscMax(eEnd-eStart, cEnd-cStart));
1018 
1019       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1020       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd-1));
1021       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd-vStart));
1022       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum-(vEnd-vStart))/2.));
1023       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1024       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd-1));
1025       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum-(eEnd-eStart))/2.));
1026       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd-eStart));
1027       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1028       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd-1));
1029       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd-cStart));
1030       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum-(cEnd-cStart))/2.));
1031     }
1032     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale));
1033 
1034     /* Plot vertices */
1035     PetscCall(VecGetArray(coordinates, &coords));
1036     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1037     for (v = vStart; v < vEnd; ++v) {
1038       PetscInt  off, dof, d;
1039       PetscBool isLabeled = PETSC_FALSE;
1040 
1041       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
1042       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1043       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1044       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1045       PetscCheck(dof <= 3,PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3",v,dof);
1046       for (d = 0; d < dof; ++d) {
1047         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1048         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1049       }
1050       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1051       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1052       for (d = 0; d < dof; ++d) {
1053         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1054         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]));
1055       }
1056       if (drawHasse) color = colors[0%numColors];
1057       else           color = colors[rank%numColors];
1058       for (l = 0; l < numLabels; ++l) {
1059         PetscInt val;
1060         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1061         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1062       }
1063       if (drawNumbers[0]) {
1064         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1065       } else if (drawColors[0]) {
1066         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1067       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1068     }
1069     PetscCall(VecRestoreArray(coordinates, &coords));
1070     PetscCall(PetscViewerFlush(viewer));
1071     /* Plot edges */
1072     if (plotEdges) {
1073       PetscCall(VecGetArray(coordinates, &coords));
1074       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1075       for (e = eStart; e < eEnd; ++e) {
1076         const PetscInt *cone;
1077         PetscInt        coneSize, offA, offB, dof, d;
1078 
1079         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1080         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1081         PetscCheck(coneSize == 2,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1082         PetscCall(DMPlexGetCone(dm, e, &cone));
1083         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1084         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1085         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1086         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1087         for (d = 0; d < dof; ++d) {
1088           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
1089           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1090         }
1091         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1092         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1093         for (d = 0; d < dof; ++d) {
1094           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1095           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1096         }
1097         if (drawHasse) color = colors[1%numColors];
1098         else           color = colors[rank%numColors];
1099         for (l = 0; l < numLabels; ++l) {
1100           PetscInt val;
1101           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1102           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1103         }
1104         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1105       }
1106       PetscCall(VecRestoreArray(coordinates, &coords));
1107       PetscCall(PetscViewerFlush(viewer));
1108       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1109     }
1110     /* Plot cells */
1111     if (dim == 3 || !drawNumbers[1]) {
1112       for (e = eStart; e < eEnd; ++e) {
1113         const PetscInt *cone;
1114 
1115         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1116         color = colors[rank%numColors];
1117         for (l = 0; l < numLabels; ++l) {
1118           PetscInt val;
1119           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1120           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1121         }
1122         PetscCall(DMPlexGetCone(dm, e, &cone));
1123         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1124       }
1125     } else {
1126        DMPolytopeType ct;
1127 
1128       /* Drawing a 2D polygon */
1129       for (c = cStart; c < cEnd; ++c) {
1130         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1131         PetscCall(DMPlexGetCellType(dm, c, &ct));
1132         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR ||
1133             ct == DM_POLYTOPE_TRI_PRISM_TENSOR ||
1134             ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1135           const PetscInt *cone;
1136           PetscInt        coneSize, e;
1137 
1138           PetscCall(DMPlexGetCone(dm, c, &cone));
1139           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1140           for (e = 0; e < coneSize; ++e) {
1141             const PetscInt *econe;
1142 
1143             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1144             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank%numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1145           }
1146         } else {
1147           PetscInt *closure = NULL;
1148           PetscInt  closureSize, Nv = 0, v;
1149 
1150           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1151           for (p = 0; p < closureSize*2; p += 2) {
1152             const PetscInt point = closure[p];
1153 
1154             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1155           }
1156           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]));
1157           for (v = 0; v <= Nv; ++v) {
1158             const PetscInt vertex = closure[v%Nv];
1159 
1160             if (v > 0) {
1161               if (plotEdges) {
1162                 const PetscInt *edge;
1163                 PetscInt        endpoints[2], ne;
1164 
1165                 endpoints[0] = closure[v-1]; endpoints[1] = vertex;
1166                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1167                 PetscCheck(ne == 1,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1168                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1169                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1170               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1171             }
1172             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1173           }
1174           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1175           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1176         }
1177       }
1178     }
1179     PetscCall(VecGetArray(coordinates, &coords));
1180     for (c = cStart; c < cEnd; ++c) {
1181       double    ccoords[3] = {0.0, 0.0, 0.0};
1182       PetscBool isLabeled  = PETSC_FALSE;
1183       PetscInt *closure    = NULL;
1184       PetscInt  closureSize, dof, d, n = 0;
1185 
1186       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
1187       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1188       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1189       for (p = 0; p < closureSize*2; p += 2) {
1190         const PetscInt point = closure[p];
1191         PetscInt       off;
1192 
1193         if ((point < vStart) || (point >= vEnd)) continue;
1194         PetscCall(PetscSectionGetDof(coordSection, point, &dof));
1195         PetscCall(PetscSectionGetOffset(coordSection, point, &off));
1196         for (d = 0; d < dof; ++d) {
1197           tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1198           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1199         }
1200         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1201         if (dof == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1202         for (d = 0; d < dof; ++d) {ccoords[d] += tcoords[d];}
1203         ++n;
1204       }
1205       for (d = 0; d < dof; ++d) {ccoords[d] /= n;}
1206       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1207       for (d = 0; d < dof; ++d) {
1208         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1209         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]));
1210       }
1211       if (drawHasse) color = colors[depth%numColors];
1212       else           color = colors[rank%numColors];
1213       for (l = 0; l < numLabels; ++l) {
1214         PetscInt val;
1215         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1216         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1217       }
1218       if (drawNumbers[dim]) {
1219         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1220       } else if (drawColors[dim]) {
1221         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1222       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1223     }
1224     PetscCall(VecRestoreArray(coordinates, &coords));
1225     if (drawHasse) {
1226       color = colors[depth%numColors];
1227       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1228       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1229       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1230       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1231       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1232 
1233       color = colors[1%numColors];
1234       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1235       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1236       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1237       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1238       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1239 
1240       color = colors[0%numColors];
1241       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1242       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1243       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1244       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1245       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1246 
1247       for (p = pStart; p < pEnd; ++p) {
1248         const PetscInt *cone;
1249         PetscInt        coneSize, cp;
1250 
1251         PetscCall(DMPlexGetCone(dm, p, &cone));
1252         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1253         for (cp = 0; cp < coneSize; ++cp) {
1254           PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1255         }
1256       }
1257     }
1258     PetscCall(PetscViewerFlush(viewer));
1259     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1260     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1261     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1262     for (l = 0; l < numLabels;  ++l) PetscCall(PetscFree(names[l]));
1263     for (c = 0; c < numColors;  ++c) PetscCall(PetscFree(colors[c]));
1264     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1265     PetscCall(PetscFree3(names, colors, lcolors));
1266     PetscCall(PetscBTDestroy(&wp));
1267   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1268     Vec                    cown,acown;
1269     VecScatter             sct;
1270     ISLocalToGlobalMapping g2l;
1271     IS                     gid,acis;
1272     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
1273     MPI_Group              ggroup,ngroup;
1274     PetscScalar            *array,nid;
1275     const PetscInt         *idxs;
1276     PetscInt               *idxs2,*start,*adjacency,*work;
1277     PetscInt64             lm[3],gm[3];
1278     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
1279     PetscMPIInt            d1,d2,rank;
1280 
1281     PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
1282     PetscCallMPI(MPI_Comm_rank(comm,&rank));
1283 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1284     PetscCallMPI(MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm));
1285 #endif
1286     if (ncomm != MPI_COMM_NULL) {
1287       PetscCallMPI(MPI_Comm_group(comm,&ggroup));
1288       PetscCallMPI(MPI_Comm_group(ncomm,&ngroup));
1289       d1   = 0;
1290       PetscCallMPI(MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2));
1291       nid  = d2;
1292       PetscCallMPI(MPI_Group_free(&ggroup));
1293       PetscCallMPI(MPI_Group_free(&ngroup));
1294       PetscCallMPI(MPI_Comm_free(&ncomm));
1295     } else nid = 0.0;
1296 
1297     /* Get connectivity */
1298     PetscCall(DMPlexGetVTKCellHeight(dm,&cellHeight));
1299     PetscCall(DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid));
1300 
1301     /* filter overlapped local cells */
1302     PetscCall(DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd));
1303     PetscCall(ISGetIndices(gid,&idxs));
1304     PetscCall(ISGetLocalSize(gid,&cum));
1305     PetscCall(PetscMalloc1(cum,&idxs2));
1306     for (c = cStart, cum = 0; c < cEnd; c++) {
1307       if (idxs[c-cStart] < 0) continue;
1308       idxs2[cum++] = idxs[c-cStart];
1309     }
1310     PetscCall(ISRestoreIndices(gid,&idxs));
1311     PetscCheck(numVertices == cum,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %" PetscInt_FMT " != %" PetscInt_FMT,numVertices,cum);
1312     PetscCall(ISDestroy(&gid));
1313     PetscCall(ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid));
1314 
1315     /* support for node-aware cell locality */
1316     PetscCall(ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis));
1317     PetscCall(VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown));
1318     PetscCall(VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown));
1319     PetscCall(VecGetArray(cown,&array));
1320     for (c = 0; c < numVertices; c++) array[c] = nid;
1321     PetscCall(VecRestoreArray(cown,&array));
1322     PetscCall(VecScatterCreate(cown,acis,acown,NULL,&sct));
1323     PetscCall(VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1324     PetscCall(VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1325     PetscCall(ISDestroy(&acis));
1326     PetscCall(VecScatterDestroy(&sct));
1327     PetscCall(VecDestroy(&cown));
1328 
1329     /* compute edgeCut */
1330     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
1331     PetscCall(PetscMalloc1(cum,&work));
1332     PetscCall(ISLocalToGlobalMappingCreateIS(gid,&g2l));
1333     PetscCall(ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH));
1334     PetscCall(ISDestroy(&gid));
1335     PetscCall(VecGetArray(acown,&array));
1336     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1337       PetscInt totl;
1338 
1339       totl = start[c+1]-start[c];
1340       PetscCall(ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work));
1341       for (i = 0; i < totl; i++) {
1342         if (work[i] < 0) {
1343           ect  += 1;
1344           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1345         }
1346       }
1347     }
1348     PetscCall(PetscFree(work));
1349     PetscCall(VecRestoreArray(acown,&array));
1350     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1351     lm[1] = -numVertices;
1352     PetscCall(MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm));
1353     PetscCall(PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT,-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]));
1354     lm[0] = ect; /* edgeCut */
1355     lm[1] = ectn; /* node-aware edgeCut */
1356     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1357     PetscCall(MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm));
1358     PetscCall(PetscViewerASCIIPrintf(viewer,", empty %" PetscInt_FMT ")\n",(PetscInt)gm[2]));
1359 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1360     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),gm[0] ? ((double)(gm[1]))/((double)gm[0]) : 1.));
1361 #else
1362     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0));
1363 #endif
1364     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1365     PetscCall(PetscFree(start));
1366     PetscCall(PetscFree(adjacency));
1367     PetscCall(VecDestroy(&acown));
1368   } else {
1369     const char    *name;
1370     PetscInt      *sizes, *hybsizes, *ghostsizes;
1371     PetscInt       locDepth, depth, cellHeight, dim, d;
1372     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1373     PetscInt       numLabels, l, maxSize = 17;
1374     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1375     MPI_Comm       comm;
1376     PetscMPIInt    size, rank;
1377 
1378     PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
1379     PetscCallMPI(MPI_Comm_size(comm, &size));
1380     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1381     PetscCall(DMGetDimension(dm, &dim));
1382     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1383     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1384     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1385     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1386     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1387     PetscCall(DMPlexGetDepth(dm, &locDepth));
1388     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1389     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1390     gcNum = gcEnd - gcStart;
1391     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1392     else                PetscCall(PetscCalloc3(3,    &sizes, 3,    &hybsizes, 3,    &ghostsizes));
1393     for (d = 0; d <= depth; d++) {
1394       PetscInt Nc[2] = {0, 0}, ict;
1395 
1396       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1397       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1398       ict  = ct0;
1399       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1400       ct0  = (DMPolytopeType) ict;
1401       for (p = pStart; p < pEnd; ++p) {
1402         DMPolytopeType ct;
1403 
1404         PetscCall(DMPlexGetCellType(dm, p, &ct));
1405         if (ct == ct0) ++Nc[0];
1406         else           ++Nc[1];
1407       }
1408       if (size < maxSize) {
1409         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm));
1410         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1411         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1412         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1413         for (p = 0; p < size; ++p) {
1414           if (rank == 0) {
1415             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p]+hybsizes[p]));
1416             if (hybsizes[p]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1417             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1418           }
1419         }
1420       } else {
1421         PetscInt locMinMax[2];
1422 
1423         locMinMax[0] = Nc[0]+Nc[1]; locMinMax[1] = Nc[0]+Nc[1];
1424         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1425         locMinMax[0] = Nc[1]; locMinMax[1] = Nc[1];
1426         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1427         if (d == depth) {
1428           locMinMax[0] = gcNum; locMinMax[1] = gcNum;
1429           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1430         }
1431         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1432         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1433         if (hybsizes[0]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1434         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1435       }
1436       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1437     }
1438     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1439     {
1440       const PetscReal *maxCell;
1441       const PetscReal *L;
1442       PetscBool        localized;
1443 
1444       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1445       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1446       if (L || localized) {
1447         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1448         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1449         if (L) {
1450           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1451           for (d = 0; d < dim; ++d) {
1452             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1453             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1454           }
1455           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1456         }
1457         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1458         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1459       }
1460     }
1461     PetscCall(DMGetNumLabels(dm, &numLabels));
1462     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1463     for (l = 0; l < numLabels; ++l) {
1464       DMLabel         label;
1465       const char     *name;
1466       IS              valueIS;
1467       const PetscInt *values;
1468       PetscInt        numValues, v;
1469 
1470       PetscCall(DMGetLabelName(dm, l, &name));
1471       PetscCall(DMGetLabel(dm, name, &label));
1472       PetscCall(DMLabelGetNumValues(label, &numValues));
1473       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1474       PetscCall(DMLabelGetValueIS(label, &valueIS));
1475       PetscCall(ISGetIndices(valueIS, &values));
1476       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1477       for (v = 0; v < numValues; ++v) {
1478         PetscInt size;
1479 
1480         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1481         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1482         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1483       }
1484       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1485       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1486       PetscCall(ISRestoreIndices(valueIS, &values));
1487       PetscCall(ISDestroy(&valueIS));
1488     }
1489     {
1490       char    **labelNames;
1491       PetscInt  Nl = numLabels;
1492       PetscBool flg;
1493 
1494       PetscCall(PetscMalloc1(Nl, &labelNames));
1495       PetscCall(PetscOptionsGetStringArray(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1496       for (l = 0; l < Nl; ++l) {
1497         DMLabel label;
1498 
1499         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1500         if (flg) {
1501           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1502           PetscCall(DMLabelView(label, viewer));
1503         }
1504         PetscCall(PetscFree(labelNames[l]));
1505       }
1506       PetscCall(PetscFree(labelNames));
1507     }
1508     /* If no fields are specified, people do not want to see adjacency */
1509     if (dm->Nf) {
1510       PetscInt f;
1511 
1512       for (f = 0; f < dm->Nf; ++f) {
1513         const char *name;
1514 
1515         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1516         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1517         PetscCall(PetscViewerASCIIPushTab(viewer));
1518         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1519         if (dm->fields[f].adjacency[0]) {
1520           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1521           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1522         } else {
1523           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1524           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1525         }
1526         PetscCall(PetscViewerASCIIPopTab(viewer));
1527       }
1528     }
1529     PetscCall(DMGetCoarseDM(dm, &cdm));
1530     if (cdm) {
1531       PetscCall(PetscViewerASCIIPushTab(viewer));
1532       PetscCall(DMPlexView_Ascii(cdm, viewer));
1533       PetscCall(PetscViewerASCIIPopTab(viewer));
1534     }
1535   }
1536   PetscFunctionReturn(0);
1537 }
1538 
1539 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1540 {
1541   DMPolytopeType ct;
1542   PetscMPIInt    rank;
1543   PetscInt       cdim;
1544 
1545   PetscFunctionBegin;
1546   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1547   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1548   PetscCall(DMGetCoordinateDim(dm, &cdim));
1549   switch (ct) {
1550   case DM_POLYTOPE_SEGMENT:
1551   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1552     switch (cdim) {
1553     case 1:
1554     {
1555       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1556       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1557 
1558       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y,    PetscRealPart(coords[1]), y,    PETSC_DRAW_BLACK));
1559       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y+dy, PetscRealPart(coords[0]), y-dy, PETSC_DRAW_BLACK));
1560       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y+dy, PetscRealPart(coords[1]), y-dy, PETSC_DRAW_BLACK));
1561     }
1562     break;
1563     case 2:
1564     {
1565       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1566       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1567       const PetscReal l  = 0.1/PetscSqrtReal(dx*dx + dy*dy);
1568 
1569       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1570       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0])+l*dx, PetscRealPart(coords[1])+l*dy, PetscRealPart(coords[0])-l*dx, PetscRealPart(coords[1])-l*dy, PETSC_DRAW_BLACK));
1571       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2])+l*dx, PetscRealPart(coords[3])+l*dy, PetscRealPart(coords[2])-l*dx, PetscRealPart(coords[3])-l*dy, PETSC_DRAW_BLACK));
1572     }
1573     break;
1574     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1575     }
1576     break;
1577   case DM_POLYTOPE_TRIANGLE:
1578     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1579                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1580                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1581                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1582     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1583     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1584     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1585     break;
1586   case DM_POLYTOPE_QUADRILATERAL:
1587     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1588                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1589                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1590                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1591     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1592                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1593                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1594                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1595     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1596     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1597     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1598     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1599     break;
1600   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1601   }
1602   PetscFunctionReturn(0);
1603 }
1604 
1605 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1606 {
1607   DMPolytopeType ct;
1608   PetscReal      centroid[2] = {0., 0.};
1609   PetscMPIInt    rank;
1610   PetscInt       fillColor, v, e, d;
1611 
1612   PetscFunctionBegin;
1613   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1614   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1615   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2;
1616   switch (ct) {
1617   case DM_POLYTOPE_TRIANGLE:
1618     {
1619       PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1620 
1621       for (v = 0; v < 3; ++v) {centroid[0] += PetscRealPart(coords[v*2+0])/3.;centroid[1] += PetscRealPart(coords[v*2+1])/3.;}
1622       for (e = 0; e < 3; ++e) {
1623         refCoords[0] = refVertices[e*2+0];
1624         refCoords[1] = refVertices[e*2+1];
1625         for (d = 1; d <= edgeDiv; ++d) {
1626           refCoords[d*2+0] = refCoords[0] + (refVertices[(e+1)%3 * 2 + 0] - refCoords[0])*d/edgeDiv;
1627           refCoords[d*2+1] = refCoords[1] + (refVertices[(e+1)%3 * 2 + 1] - refCoords[1])*d/edgeDiv;
1628         }
1629         PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv+1, refCoords, edgeCoords));
1630         for (d = 0; d < edgeDiv; ++d) {
1631           PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], fillColor, fillColor, fillColor));
1632           PetscCall(PetscDrawLine(draw, edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], PETSC_DRAW_BLACK));
1633         }
1634       }
1635     }
1636     break;
1637   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1638   }
1639   PetscFunctionReturn(0);
1640 }
1641 
1642 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1643 {
1644   PetscDraw          draw;
1645   DM                 cdm;
1646   PetscSection       coordSection;
1647   Vec                coordinates;
1648   const PetscScalar *coords;
1649   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1650   PetscReal         *refCoords, *edgeCoords;
1651   PetscBool          isnull, drawAffine = PETSC_TRUE;
1652   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1653 
1654   PetscFunctionBegin;
1655   PetscCall(DMGetCoordinateDim(dm, &dim));
1656   PetscCheck(dim <= 2,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1657   PetscCall(PetscOptionsGetBool(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1658   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv+1)*dim, &refCoords, (edgeDiv+1)*dim, &edgeCoords));
1659   PetscCall(DMGetCoordinateDM(dm, &cdm));
1660   PetscCall(DMGetLocalSection(cdm, &coordSection));
1661   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1662   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1663   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1664 
1665   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1666   PetscCall(PetscDrawIsNull(draw, &isnull));
1667   if (isnull) PetscFunctionReturn(0);
1668   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1669 
1670   PetscCall(VecGetLocalSize(coordinates, &N));
1671   PetscCall(VecGetArrayRead(coordinates, &coords));
1672   for (c = 0; c < N; c += dim) {
1673     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1674     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1675   }
1676   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1677   PetscCall(MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm)));
1678   PetscCall(MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm)));
1679   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1680   PetscCall(PetscDrawClear(draw));
1681 
1682   for (c = cStart; c < cEnd; ++c) {
1683     PetscScalar *coords = NULL;
1684     PetscInt     numCoords;
1685 
1686     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1687     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1688     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1689     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1690   }
1691   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1692   PetscCall(PetscDrawFlush(draw));
1693   PetscCall(PetscDrawPause(draw));
1694   PetscCall(PetscDrawSave(draw));
1695   PetscFunctionReturn(0);
1696 }
1697 
1698 #if defined(PETSC_HAVE_EXODUSII)
1699 #include <exodusII.h>
1700 #include <petscviewerexodusii.h>
1701 #endif
1702 
1703 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1704 {
1705   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus;
1706   char           name[PETSC_MAX_PATH_LEN];
1707 
1708   PetscFunctionBegin;
1709   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1710   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1711   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii));
1712   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
1713   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
1714   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
1715   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
1716   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus));
1717   if (iascii) {
1718     PetscViewerFormat format;
1719     PetscCall(PetscViewerGetFormat(viewer, &format));
1720     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1721     else PetscCall(DMPlexView_Ascii(dm, viewer));
1722   } else if (ishdf5) {
1723 #if defined(PETSC_HAVE_HDF5)
1724     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1725 #else
1726     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1727 #endif
1728   } else if (isvtk) {
1729     PetscCall(DMPlexVTKWriteAll((PetscObject) dm,viewer));
1730   } else if (isdraw) {
1731     PetscCall(DMPlexView_Draw(dm, viewer));
1732   } else if (isglvis) {
1733     PetscCall(DMPlexView_GLVis(dm, viewer));
1734 #if defined(PETSC_HAVE_EXODUSII)
1735   } else if (isexodus) {
1736 /*
1737       exodusII requires that all sets be part of exactly one cell set.
1738       If the dm does not have a "Cell Sets" label defined, we create one
1739       with ID 1, containig all cells.
1740       Note that if the Cell Sets label is defined but does not cover all cells,
1741       we may still have a problem. This should probably be checked here or in the viewer;
1742     */
1743     PetscInt numCS;
1744     PetscCall(DMGetLabelSize(dm,"Cell Sets",&numCS));
1745     if (!numCS) {
1746       PetscInt cStart, cEnd, c;
1747       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1748       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1749       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1750     }
1751     PetscCall(DMView_PlexExodusII(dm, viewer));
1752 #endif
1753   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1754 
1755   /* Optionally view the partition */
1756   PetscCall(PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg));
1757   if (flg) {
1758     Vec ranks;
1759     PetscCall(DMPlexCreateRankField(dm, &ranks));
1760     PetscCall(VecView(ranks, viewer));
1761     PetscCall(VecDestroy(&ranks));
1762   }
1763   /* Optionally view a label */
1764   PetscCall(PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1765   if (flg) {
1766     DMLabel label;
1767     Vec     val;
1768 
1769     PetscCall(DMGetLabel(dm, name, &label));
1770     PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1771     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1772     PetscCall(VecView(val, viewer));
1773     PetscCall(VecDestroy(&val));
1774   }
1775   PetscFunctionReturn(0);
1776 }
1777 
1778 /*@
1779   DMPlexTopologyView - Saves a DMPlex topology into a file
1780 
1781   Collective on DM
1782 
1783   Input Parameters:
1784 + dm     - The DM whose topology is to be saved
1785 - viewer - The PetscViewer for saving
1786 
1787   Level: advanced
1788 
1789 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1790 @*/
1791 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1792 {
1793   PetscBool      ishdf5;
1794 
1795   PetscFunctionBegin;
1796   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1797   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1798   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1799   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView,viewer,0,0,0));
1800   if (ishdf5) {
1801 #if defined(PETSC_HAVE_HDF5)
1802     PetscViewerFormat format;
1803     PetscCall(PetscViewerGetFormat(viewer, &format));
1804     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1805       IS globalPointNumbering;
1806 
1807       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1808       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1809       PetscCall(ISDestroy(&globalPointNumbering));
1810     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1811 #else
1812     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1813 #endif
1814   }
1815   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView,viewer,0,0,0));
1816   PetscFunctionReturn(0);
1817 }
1818 
1819 /*@
1820   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1821 
1822   Collective on DM
1823 
1824   Input Parameters:
1825 + dm     - The DM whose coordinates are to be saved
1826 - viewer - The PetscViewer for saving
1827 
1828   Level: advanced
1829 
1830 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1831 @*/
1832 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1833 {
1834   PetscBool      ishdf5;
1835 
1836   PetscFunctionBegin;
1837   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1838   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1839   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1840   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView,viewer,0,0,0));
1841   if (ishdf5) {
1842 #if defined(PETSC_HAVE_HDF5)
1843     PetscViewerFormat format;
1844     PetscCall(PetscViewerGetFormat(viewer, &format));
1845     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1846       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1847     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1848 #else
1849     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1850 #endif
1851   }
1852   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView,viewer,0,0,0));
1853   PetscFunctionReturn(0);
1854 }
1855 
1856 /*@
1857   DMPlexLabelsView - Saves DMPlex labels into a file
1858 
1859   Collective on DM
1860 
1861   Input Parameters:
1862 + dm     - The DM whose labels are to be saved
1863 - viewer - The PetscViewer for saving
1864 
1865   Level: advanced
1866 
1867 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1868 @*/
1869 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1870 {
1871   PetscBool      ishdf5;
1872 
1873   PetscFunctionBegin;
1874   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1875   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1876   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1877   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView,viewer,0,0,0));
1878   if (ishdf5) {
1879 #if defined(PETSC_HAVE_HDF5)
1880     IS                globalPointNumbering;
1881     PetscViewerFormat format;
1882 
1883     PetscCall(PetscViewerGetFormat(viewer, &format));
1884     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1885       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1886       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1887       PetscCall(ISDestroy(&globalPointNumbering));
1888     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1889 #else
1890     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1891 #endif
1892   }
1893   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView,viewer,0,0,0));
1894   PetscFunctionReturn(0);
1895 }
1896 
1897 /*@
1898   DMPlexSectionView - Saves a section associated with a DMPlex
1899 
1900   Collective on DM
1901 
1902   Input Parameters:
1903 + dm         - The DM that contains the topology on which the section to be saved is defined
1904 . viewer     - The PetscViewer for saving
1905 - sectiondm  - The DM that contains the section to be saved
1906 
1907   Level: advanced
1908 
1909   Notes:
1910   This function is a wrapper around PetscSectionView(); in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with DMPlexTopologyLoad() and DMPlexSectionLoad(), respectively, this information is used to match section points with topology points.
1911 
1912   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1913 
1914 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1915 @*/
1916 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1917 {
1918   PetscBool      ishdf5;
1919 
1920   PetscFunctionBegin;
1921   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1922   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1923   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1924   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
1925   PetscCall(PetscLogEventBegin(DMPLEX_SectionView,viewer,0,0,0));
1926   if (ishdf5) {
1927 #if defined(PETSC_HAVE_HDF5)
1928     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1929 #else
1930     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1931 #endif
1932   }
1933   PetscCall(PetscLogEventEnd(DMPLEX_SectionView,viewer,0,0,0));
1934   PetscFunctionReturn(0);
1935 }
1936 
1937 /*@
1938   DMPlexGlobalVectorView - Saves a global vector
1939 
1940   Collective on DM
1941 
1942   Input Parameters:
1943 + dm        - The DM that represents the topology
1944 . viewer    - The PetscViewer to save data with
1945 . sectiondm - The DM that contains the global section on which vec is defined
1946 - vec       - The global vector to be saved
1947 
1948   Level: advanced
1949 
1950   Notes:
1951   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1952 
1953   Typical calling sequence
1954 $       DMCreate(PETSC_COMM_WORLD, &dm);
1955 $       DMSetType(dm, DMPLEX);
1956 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1957 $       DMClone(dm, &sectiondm);
1958 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1959 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1960 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1961 $       PetscSectionSetChart(section, pStart, pEnd);
1962 $       PetscSectionSetUp(section);
1963 $       DMSetLocalSection(sectiondm, section);
1964 $       PetscSectionDestroy(&section);
1965 $       DMGetGlobalVector(sectiondm, &vec);
1966 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1967 $       DMPlexTopologyView(dm, viewer);
1968 $       DMPlexSectionView(dm, viewer, sectiondm);
1969 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1970 $       DMRestoreGlobalVector(sectiondm, &vec);
1971 $       DMDestroy(&sectiondm);
1972 $       DMDestroy(&dm);
1973 
1974 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1975 @*/
1976 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
1977 {
1978   PetscBool       ishdf5;
1979 
1980   PetscFunctionBegin;
1981   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1982   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1983   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1984   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1985   /* Check consistency */
1986   {
1987     PetscSection  section;
1988     PetscBool     includesConstraints;
1989     PetscInt      m, m1;
1990 
1991     PetscCall(VecGetLocalSize(vec, &m1));
1992     PetscCall(DMGetGlobalSection(sectiondm, &section));
1993     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1994     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
1995     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
1996     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
1997   }
1998   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1999   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView,viewer,0,0,0));
2000   if (ishdf5) {
2001 #if defined(PETSC_HAVE_HDF5)
2002     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2003 #else
2004     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2005 #endif
2006   }
2007   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView,viewer,0,0,0));
2008   PetscFunctionReturn(0);
2009 }
2010 
2011 /*@
2012   DMPlexLocalVectorView - Saves a local vector
2013 
2014   Collective on DM
2015 
2016   Input Parameters:
2017 + dm        - The DM that represents the topology
2018 . viewer    - The PetscViewer to save data with
2019 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2020 - vec       - The local vector to be saved
2021 
2022   Level: advanced
2023 
2024   Notes:
2025   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2026 
2027   Typical calling sequence
2028 $       DMCreate(PETSC_COMM_WORLD, &dm);
2029 $       DMSetType(dm, DMPLEX);
2030 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2031 $       DMClone(dm, &sectiondm);
2032 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2033 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2034 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2035 $       PetscSectionSetChart(section, pStart, pEnd);
2036 $       PetscSectionSetUp(section);
2037 $       DMSetLocalSection(sectiondm, section);
2038 $       DMGetLocalVector(sectiondm, &vec);
2039 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2040 $       DMPlexTopologyView(dm, viewer);
2041 $       DMPlexSectionView(dm, viewer, sectiondm);
2042 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2043 $       DMRestoreLocalVector(sectiondm, &vec);
2044 $       DMDestroy(&sectiondm);
2045 $       DMDestroy(&dm);
2046 
2047 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2048 @*/
2049 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2050 {
2051   PetscBool       ishdf5;
2052 
2053   PetscFunctionBegin;
2054   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2055   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2056   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2057   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2058   /* Check consistency */
2059   {
2060     PetscSection  section;
2061     PetscBool     includesConstraints;
2062     PetscInt      m, m1;
2063 
2064     PetscCall(VecGetLocalSize(vec, &m1));
2065     PetscCall(DMGetLocalSection(sectiondm, &section));
2066     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2067     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2068     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2069     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2070   }
2071   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2072   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView,viewer,0,0,0));
2073   if (ishdf5) {
2074 #if defined(PETSC_HAVE_HDF5)
2075     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2076 #else
2077     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2078 #endif
2079   }
2080   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView,viewer,0,0,0));
2081   PetscFunctionReturn(0);
2082 }
2083 
2084 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2085 {
2086   PetscBool      ishdf5;
2087 
2088   PetscFunctionBegin;
2089   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2090   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2091   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5));
2092   if (ishdf5) {
2093 #if defined(PETSC_HAVE_HDF5)
2094     PetscViewerFormat format;
2095     PetscCall(PetscViewerGetFormat(viewer, &format));
2096     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2097       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2098     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2099       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2100     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2101     PetscFunctionReturn(0);
2102 #else
2103     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2104 #endif
2105   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2106 }
2107 
2108 /*@
2109   DMPlexTopologyLoad - Loads a topology into a DMPlex
2110 
2111   Collective on DM
2112 
2113   Input Parameters:
2114 + dm     - The DM into which the topology is loaded
2115 - viewer - The PetscViewer for the saved topology
2116 
2117   Output Parameters:
2118 . globalToLocalPointSF - The PetscSF that pushes points in [0, N) to the associated points in the loaded plex, where N is the global number of points; NULL if unneeded
2119 
2120   Level: advanced
2121 
2122 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2123 @*/
2124 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2125 {
2126   PetscBool      ishdf5;
2127 
2128   PetscFunctionBegin;
2129   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2130   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2131   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2132   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2133   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad,viewer,0,0,0));
2134   if (ishdf5) {
2135 #if defined(PETSC_HAVE_HDF5)
2136     PetscViewerFormat format;
2137     PetscCall(PetscViewerGetFormat(viewer, &format));
2138     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2139       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2140     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2141 #else
2142     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2143 #endif
2144   }
2145   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad,viewer,0,0,0));
2146   PetscFunctionReturn(0);
2147 }
2148 
2149 /*@
2150   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2151 
2152   Collective on DM
2153 
2154   Input Parameters:
2155 + dm     - The DM into which the coordinates are loaded
2156 . viewer - The PetscViewer for the saved coordinates
2157 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2158 
2159   Level: advanced
2160 
2161 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2162 @*/
2163 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2164 {
2165   PetscBool      ishdf5;
2166 
2167   PetscFunctionBegin;
2168   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2169   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2170   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2171   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2172   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2173   if (ishdf5) {
2174 #if defined(PETSC_HAVE_HDF5)
2175     PetscViewerFormat format;
2176     PetscCall(PetscViewerGetFormat(viewer, &format));
2177     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2178       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2179     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2180 #else
2181     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2182 #endif
2183   }
2184   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2185   PetscFunctionReturn(0);
2186 }
2187 
2188 /*@
2189   DMPlexLabelsLoad - Loads labels into a DMPlex
2190 
2191   Collective on DM
2192 
2193   Input Parameters:
2194 + dm     - The DM into which the labels are loaded
2195 . viewer - The PetscViewer for the saved labels
2196 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2197 
2198   Level: advanced
2199 
2200   Notes:
2201   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2202 
2203 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2204 @*/
2205 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2206 {
2207   PetscBool      ishdf5;
2208 
2209   PetscFunctionBegin;
2210   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2211   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2212   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2213   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2214   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad,viewer,0,0,0));
2215   if (ishdf5) {
2216 #if defined(PETSC_HAVE_HDF5)
2217     PetscViewerFormat format;
2218 
2219     PetscCall(PetscViewerGetFormat(viewer, &format));
2220     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2221       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2222     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2223 #else
2224     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2225 #endif
2226   }
2227   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad,viewer,0,0,0));
2228   PetscFunctionReturn(0);
2229 }
2230 
2231 /*@
2232   DMPlexSectionLoad - Loads section into a DMPlex
2233 
2234   Collective on DM
2235 
2236   Input Parameters:
2237 + dm          - The DM that represents the topology
2238 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2239 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2240 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2241 
2242   Output Parameters
2243 + globalDofSF - The SF that migrates any on-disk Vec data associated with sectionA into a global Vec associated with the sectiondm's global section (NULL if not needed)
2244 - localDofSF  - The SF that migrates any on-disk Vec data associated with sectionA into a local Vec associated with the sectiondm's local section (NULL if not needed)
2245 
2246   Level: advanced
2247 
2248   Notes:
2249   This function is a wrapper around PetscSectionLoad(); it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in dm. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2250 
2251   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2252 
2253   The output parameter, globalDofSF (localDofSF), can later be used with DMPlexGlobalVectorLoad() (DMPlexLocalVectorLoad()) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2254 
2255   Example using 2 processes:
2256 $  NX (number of points on dm): 4
2257 $  sectionA                   : the on-disk section
2258 $  vecA                       : a vector associated with sectionA
2259 $  sectionB                   : sectiondm's local section constructed in this function
2260 $  vecB (local)               : a vector associated with sectiondm's local section
2261 $  vecB (global)              : a vector associated with sectiondm's global section
2262 $
2263 $                                     rank 0    rank 1
2264 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2265 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2266 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2267 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2268 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2269 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2270 $  sectionB->atlasDof             :     1 0 1 | 1 3
2271 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2272 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2273 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2274 $
2275 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2276 
2277 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2278 @*/
2279 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2280 {
2281   PetscBool      ishdf5;
2282 
2283   PetscFunctionBegin;
2284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2285   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2286   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2287   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2288   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2289   if (localDofSF) PetscValidPointer(localDofSF, 6);
2290   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2291   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad,viewer,0,0,0));
2292   if (ishdf5) {
2293 #if defined(PETSC_HAVE_HDF5)
2294     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2295 #else
2296     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2297 #endif
2298   }
2299   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad,viewer,0,0,0));
2300   PetscFunctionReturn(0);
2301 }
2302 
2303 /*@
2304   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2305 
2306   Collective on DM
2307 
2308   Input Parameters:
2309 + dm        - The DM that represents the topology
2310 . viewer    - The PetscViewer that represents the on-disk vector data
2311 . sectiondm - The DM that contains the global section on which vec is defined
2312 . sf        - The SF that migrates the on-disk vector data into vec
2313 - vec       - The global vector to set values of
2314 
2315   Level: advanced
2316 
2317   Notes:
2318   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2319 
2320   Typical calling sequence
2321 $       DMCreate(PETSC_COMM_WORLD, &dm);
2322 $       DMSetType(dm, DMPLEX);
2323 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2324 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2325 $       DMClone(dm, &sectiondm);
2326 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2327 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2328 $       DMGetGlobalVector(sectiondm, &vec);
2329 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2330 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2331 $       DMRestoreGlobalVector(sectiondm, &vec);
2332 $       PetscSFDestroy(&gsf);
2333 $       PetscSFDestroy(&sfX);
2334 $       DMDestroy(&sectiondm);
2335 $       DMDestroy(&dm);
2336 
2337 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2338 @*/
2339 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2340 {
2341   PetscBool       ishdf5;
2342 
2343   PetscFunctionBegin;
2344   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2345   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2346   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2347   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2348   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2349   /* Check consistency */
2350   {
2351     PetscSection  section;
2352     PetscBool     includesConstraints;
2353     PetscInt      m, m1;
2354 
2355     PetscCall(VecGetLocalSize(vec, &m1));
2356     PetscCall(DMGetGlobalSection(sectiondm, &section));
2357     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2358     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2359     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2360     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2361   }
2362   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2363   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2364   if (ishdf5) {
2365 #if defined(PETSC_HAVE_HDF5)
2366     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2367 #else
2368     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2369 #endif
2370   }
2371   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2372   PetscFunctionReturn(0);
2373 }
2374 
2375 /*@
2376   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2377 
2378   Collective on DM
2379 
2380   Input Parameters:
2381 + dm        - The DM that represents the topology
2382 . viewer    - The PetscViewer that represents the on-disk vector data
2383 . sectiondm - The DM that contains the local section on which vec is defined
2384 . sf        - The SF that migrates the on-disk vector data into vec
2385 - vec       - The local vector to set values of
2386 
2387   Level: advanced
2388 
2389   Notes:
2390   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2391 
2392   Typical calling sequence
2393 $       DMCreate(PETSC_COMM_WORLD, &dm);
2394 $       DMSetType(dm, DMPLEX);
2395 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2396 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2397 $       DMClone(dm, &sectiondm);
2398 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2399 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2400 $       DMGetLocalVector(sectiondm, &vec);
2401 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2402 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2403 $       DMRestoreLocalVector(sectiondm, &vec);
2404 $       PetscSFDestroy(&lsf);
2405 $       PetscSFDestroy(&sfX);
2406 $       DMDestroy(&sectiondm);
2407 $       DMDestroy(&dm);
2408 
2409 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2410 @*/
2411 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2412 {
2413   PetscBool       ishdf5;
2414 
2415   PetscFunctionBegin;
2416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2417   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2418   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2419   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2420   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2421   /* Check consistency */
2422   {
2423     PetscSection  section;
2424     PetscBool     includesConstraints;
2425     PetscInt      m, m1;
2426 
2427     PetscCall(VecGetLocalSize(vec, &m1));
2428     PetscCall(DMGetLocalSection(sectiondm, &section));
2429     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2430     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2431     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2432     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2433   }
2434   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2435   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2436   if (ishdf5) {
2437 #if defined(PETSC_HAVE_HDF5)
2438     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2439 #else
2440     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2441 #endif
2442   }
2443   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2444   PetscFunctionReturn(0);
2445 }
2446 
2447 PetscErrorCode DMDestroy_Plex(DM dm)
2448 {
2449   DM_Plex       *mesh = (DM_Plex*) dm->data;
2450 
2451   PetscFunctionBegin;
2452   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL));
2453   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL));
2454   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL));
2455   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMInterpolateSolution_C", NULL));
2456   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2457   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C", NULL));
2458   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeGetDefault_C", NULL));
2459   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeSetDefault_C", NULL));
2460   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"MatComputeNeumannOverlap_C",NULL));
2461   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderGetDefault_C", NULL));
2462   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderSetDefault_C", NULL));
2463   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C",NULL));
2464   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexSetOverlap_C",NULL));
2465   if (--mesh->refct > 0) PetscFunctionReturn(0);
2466   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2467   PetscCall(PetscFree(mesh->cones));
2468   PetscCall(PetscFree(mesh->coneOrientations));
2469   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2470   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2471   PetscCall(PetscFree(mesh->supports));
2472   PetscCall(PetscFree(mesh->facesTmp));
2473   PetscCall(PetscFree(mesh->tetgenOpts));
2474   PetscCall(PetscFree(mesh->triangleOpts));
2475   PetscCall(PetscFree(mesh->transformType));
2476   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2477   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2478   PetscCall(ISDestroy(&mesh->subpointIS));
2479   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2480   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2481   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2482   PetscCall(ISDestroy(&mesh->anchorIS));
2483   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2484   PetscCall(PetscFree(mesh->parents));
2485   PetscCall(PetscFree(mesh->childIDs));
2486   PetscCall(PetscSectionDestroy(&mesh->childSection));
2487   PetscCall(PetscFree(mesh->children));
2488   PetscCall(DMDestroy(&mesh->referenceTree));
2489   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2490   PetscCall(PetscFree(mesh->neighbors));
2491   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2492   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2493   PetscCall(PetscFree(mesh));
2494   PetscFunctionReturn(0);
2495 }
2496 
2497 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2498 {
2499   PetscSection           sectionGlobal;
2500   PetscInt               bs = -1, mbs;
2501   PetscInt               localSize, localStart = 0;
2502   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2503   MatType                mtype;
2504   ISLocalToGlobalMapping ltog;
2505 
2506   PetscFunctionBegin;
2507   PetscCall(MatInitializePackage());
2508   mtype = dm->mattype;
2509   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2510   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2511   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2512   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject) dm)));
2513   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2514   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2515   PetscCall(MatSetType(*J, mtype));
2516   PetscCall(MatSetFromOptions(*J));
2517   PetscCall(MatGetBlockSize(*J, &mbs));
2518   if (mbs > 1) bs = mbs;
2519   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2520   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2521   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2522   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2523   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2524   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2525   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2526   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2527   if (!isShell) {
2528     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2529     PetscInt  *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2530     PetscInt  pStart, pEnd, p, dof, cdof;
2531 
2532     PetscCall(DMGetLocalToGlobalMapping(dm,&ltog));
2533 
2534     PetscCall(PetscCalloc1(localSize, &pblocks));
2535     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2536     for (p = pStart; p < pEnd; ++p) {
2537       PetscInt bdof, offset;
2538 
2539       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2540       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2541       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2542       for (PetscInt i=0; i < dof - cdof; i++)
2543         pblocks[offset - localStart + i] = dof - cdof;
2544       dof  = dof < 0 ? -(dof+1) : dof;
2545       bdof = cdof && (dof-cdof) ? 1 : dof;
2546       if (dof) {
2547         if (bs < 0)          {bs = bdof;}
2548         else if (bs != bdof) {bs = 1;}
2549       }
2550     }
2551     /* Must have same blocksize on all procs (some might have no points) */
2552     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2553     bsLocal[1] = bs;
2554     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
2555     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2556     else bs = bsMinMax[0];
2557     bs = PetscMax(1,bs);
2558     PetscCall(MatSetLocalToGlobalMapping(*J,ltog,ltog));
2559     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2560       PetscCall(MatSetBlockSize(*J, bs));
2561       PetscCall(MatSetUp(*J));
2562     } else {
2563       PetscCall(PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu));
2564       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2565       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2566     }
2567     { // Consolidate blocks
2568       PetscInt nblocks = 0;
2569       for (PetscInt i=0; i<localSize; i += PetscMax(1, pblocks[i])) {
2570         if (pblocks[i] == 0) continue;
2571         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2572         for (PetscInt j=1; j<pblocks[i]; j++) {
2573            PetscCheck(pblocks[i+j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i+j]);
2574         }
2575       }
2576       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2577     }
2578     PetscCall(PetscFree(pblocks));
2579   }
2580   PetscCall(MatSetDM(*J, dm));
2581   PetscFunctionReturn(0);
2582 }
2583 
2584 /*@
2585   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2586 
2587   Not collective
2588 
2589   Input Parameter:
2590 . mesh - The DMPlex
2591 
2592   Output Parameters:
2593 . subsection - The subdomain section
2594 
2595   Level: developer
2596 
2597 .seealso:
2598 @*/
2599 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2600 {
2601   DM_Plex       *mesh = (DM_Plex*) dm->data;
2602 
2603   PetscFunctionBegin;
2604   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2605   if (!mesh->subdomainSection) {
2606     PetscSection section;
2607     PetscSF      sf;
2608 
2609     PetscCall(PetscSFCreate(PETSC_COMM_SELF,&sf));
2610     PetscCall(DMGetLocalSection(dm,&section));
2611     PetscCall(PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection));
2612     PetscCall(PetscSFDestroy(&sf));
2613   }
2614   *subsection = mesh->subdomainSection;
2615   PetscFunctionReturn(0);
2616 }
2617 
2618 /*@
2619   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2620 
2621   Not collective
2622 
2623   Input Parameter:
2624 . mesh - The DMPlex
2625 
2626   Output Parameters:
2627 + pStart - The first mesh point
2628 - pEnd   - The upper bound for mesh points
2629 
2630   Level: beginner
2631 
2632 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2633 @*/
2634 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2635 {
2636   DM_Plex       *mesh = (DM_Plex*) dm->data;
2637 
2638   PetscFunctionBegin;
2639   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2640   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2641   PetscFunctionReturn(0);
2642 }
2643 
2644 /*@
2645   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2646 
2647   Not collective
2648 
2649   Input Parameters:
2650 + mesh - The DMPlex
2651 . pStart - The first mesh point
2652 - pEnd   - The upper bound for mesh points
2653 
2654   Output Parameters:
2655 
2656   Level: beginner
2657 
2658 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2659 @*/
2660 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2661 {
2662   DM_Plex       *mesh = (DM_Plex*) dm->data;
2663 
2664   PetscFunctionBegin;
2665   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2666   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2667   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2668   PetscFunctionReturn(0);
2669 }
2670 
2671 /*@
2672   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2673 
2674   Not collective
2675 
2676   Input Parameters:
2677 + mesh - The DMPlex
2678 - p - The point, which must lie in the chart set with DMPlexSetChart()
2679 
2680   Output Parameter:
2681 . size - The cone size for point p
2682 
2683   Level: beginner
2684 
2685 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2686 @*/
2687 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2688 {
2689   DM_Plex       *mesh = (DM_Plex*) dm->data;
2690 
2691   PetscFunctionBegin;
2692   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2693   PetscValidIntPointer(size, 3);
2694   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2695   PetscFunctionReturn(0);
2696 }
2697 
2698 /*@
2699   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2700 
2701   Not collective
2702 
2703   Input Parameters:
2704 + mesh - The DMPlex
2705 . p - The point, which must lie in the chart set with DMPlexSetChart()
2706 - size - The cone size for point p
2707 
2708   Output Parameter:
2709 
2710   Note:
2711   This should be called after DMPlexSetChart().
2712 
2713   Level: beginner
2714 
2715 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2716 @*/
2717 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2718 {
2719   DM_Plex       *mesh = (DM_Plex*) dm->data;
2720 
2721   PetscFunctionBegin;
2722   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2723   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2724   PetscFunctionReturn(0);
2725 }
2726 
2727 /*@
2728   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2729 
2730   Not collective
2731 
2732   Input Parameters:
2733 + mesh - The DMPlex
2734 . p - The point, which must lie in the chart set with DMPlexSetChart()
2735 - size - The additional cone size for point p
2736 
2737   Output Parameter:
2738 
2739   Note:
2740   This should be called after DMPlexSetChart().
2741 
2742   Level: beginner
2743 
2744 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2745 @*/
2746 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2747 {
2748   DM_Plex       *mesh = (DM_Plex*) dm->data;
2749   PetscFunctionBegin;
2750   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2751   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2752   PetscFunctionReturn(0);
2753 }
2754 
2755 /*@C
2756   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2757 
2758   Not collective
2759 
2760   Input Parameters:
2761 + dm - The DMPlex
2762 - p - The point, which must lie in the chart set with DMPlexSetChart()
2763 
2764   Output Parameter:
2765 . cone - An array of points which are on the in-edges for point p
2766 
2767   Level: beginner
2768 
2769   Fortran Notes:
2770   Since it returns an array, this routine is only available in Fortran 90, and you must
2771   include petsc.h90 in your code.
2772   You must also call DMPlexRestoreCone() after you finish using the returned array.
2773   DMPlexRestoreCone() is not needed/available in C.
2774 
2775 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2776 @*/
2777 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2778 {
2779   DM_Plex       *mesh = (DM_Plex*) dm->data;
2780   PetscInt       off;
2781 
2782   PetscFunctionBegin;
2783   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2784   PetscValidPointer(cone, 3);
2785   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2786   *cone = &mesh->cones[off];
2787   PetscFunctionReturn(0);
2788 }
2789 
2790 /*@C
2791   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2792 
2793   Not collective
2794 
2795   Input Parameters:
2796 + dm - The DMPlex
2797 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2798 
2799   Output Parameters:
2800 + pConesSection - PetscSection describing the layout of pCones
2801 - pCones - An array of points which are on the in-edges for the point set p
2802 
2803   Level: intermediate
2804 
2805 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2806 @*/
2807 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2808 {
2809   PetscSection        cs, newcs;
2810   PetscInt            *cones;
2811   PetscInt            *newarr=NULL;
2812   PetscInt            n;
2813 
2814   PetscFunctionBegin;
2815   PetscCall(DMPlexGetCones(dm, &cones));
2816   PetscCall(DMPlexGetConeSection(dm, &cs));
2817   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL));
2818   if (pConesSection) *pConesSection = newcs;
2819   if (pCones) {
2820     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2821     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2822   }
2823   PetscFunctionReturn(0);
2824 }
2825 
2826 /*@
2827   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2828 
2829   Not collective
2830 
2831   Input Parameters:
2832 + dm - The DMPlex
2833 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2834 
2835   Output Parameter:
2836 . expandedPoints - An array of vertices recursively expanded from input points
2837 
2838   Level: advanced
2839 
2840   Notes:
2841   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2842   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2843 
2844 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2845 @*/
2846 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2847 {
2848   IS                  *expandedPointsAll;
2849   PetscInt            depth;
2850 
2851   PetscFunctionBegin;
2852   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2853   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2854   PetscValidPointer(expandedPoints, 3);
2855   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2856   *expandedPoints = expandedPointsAll[0];
2857   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2858   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2859   PetscFunctionReturn(0);
2860 }
2861 
2862 /*@
2863   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
2864 
2865   Not collective
2866 
2867   Input Parameters:
2868 + dm - The DMPlex
2869 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2870 
2871   Output Parameters:
2872 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2873 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2874 - sections - (optional) An array of sections which describe mappings from points to their cone points
2875 
2876   Level: advanced
2877 
2878   Notes:
2879   Like DMPlexGetConeTuple() but recursive.
2880 
2881   Array expandedPoints has size equal to depth. Each expandedPoints[d] contains DAG points with maximum depth d, recursively cone-wise expanded from the input points.
2882   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2883 
2884   Array section has size equal to depth.  Each PetscSection sections[d] realizes mapping from expandedPoints[d+1] (section points) to expandedPoints[d] (section dofs) as follows:
2885   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2886   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2887 
2888 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2889 @*/
2890 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2891 {
2892   const PetscInt      *arr0=NULL, *cone=NULL;
2893   PetscInt            *arr=NULL, *newarr=NULL;
2894   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
2895   IS                  *expandedPoints_;
2896   PetscSection        *sections_;
2897 
2898   PetscFunctionBegin;
2899   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2900   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2901   if (depth) PetscValidIntPointer(depth, 3);
2902   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2903   if (sections) PetscValidPointer(sections, 5);
2904   PetscCall(ISGetLocalSize(points, &n));
2905   PetscCall(ISGetIndices(points, &arr0));
2906   PetscCall(DMPlexGetDepth(dm, &depth_));
2907   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2908   PetscCall(PetscCalloc1(depth_, &sections_));
2909   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
2910   for (d=depth_-1; d>=0; d--) {
2911     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2912     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2913     for (i=0; i<n; i++) {
2914       PetscCall(DMPlexGetDepthStratum(dm, d+1, &start, &end));
2915       if (arr[i] >= start && arr[i] < end) {
2916         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2917         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2918       } else {
2919         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2920       }
2921     }
2922     PetscCall(PetscSectionSetUp(sections_[d]));
2923     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2924     PetscCall(PetscMalloc1(newn, &newarr));
2925     for (i=0; i<n; i++) {
2926       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2927       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2928       if (cn > 1) {
2929         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2930         PetscCall(PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt)));
2931       } else {
2932         newarr[co] = arr[i];
2933       }
2934     }
2935     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2936     arr = newarr;
2937     n = newn;
2938   }
2939   PetscCall(ISRestoreIndices(points, &arr0));
2940   *depth = depth_;
2941   if (expandedPoints) *expandedPoints = expandedPoints_;
2942   else {
2943     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2944     PetscCall(PetscFree(expandedPoints_));
2945   }
2946   if (sections) *sections = sections_;
2947   else {
2948     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2949     PetscCall(PetscFree(sections_));
2950   }
2951   PetscFunctionReturn(0);
2952 }
2953 
2954 /*@
2955   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2956 
2957   Not collective
2958 
2959   Input Parameters:
2960 + dm - The DMPlex
2961 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2962 
2963   Output Parameters:
2964 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2965 . expandedPoints - (optional) An array of recursively expanded cones
2966 - sections - (optional) An array of sections which describe mappings from points to their cone points
2967 
2968   Level: advanced
2969 
2970   Notes:
2971   See DMPlexGetConeRecursive() for details.
2972 
2973 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2974 @*/
2975 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2976 {
2977   PetscInt            d, depth_;
2978 
2979   PetscFunctionBegin;
2980   PetscCall(DMPlexGetDepth(dm, &depth_));
2981   PetscCheck(!depth || *depth == depth_,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2982   if (depth) *depth = 0;
2983   if (expandedPoints) {
2984     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2985     PetscCall(PetscFree(*expandedPoints));
2986   }
2987   if (sections)  {
2988     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2989     PetscCall(PetscFree(*sections));
2990   }
2991   PetscFunctionReturn(0);
2992 }
2993 
2994 /*@
2995   DMPlexSetCone - Set the points on the in-edges for this point in the DAG; that is these are the points that cover the specific point
2996 
2997   Not collective
2998 
2999   Input Parameters:
3000 + mesh - The DMPlex
3001 . p - The point, which must lie in the chart set with DMPlexSetChart()
3002 - cone - An array of points which are on the in-edges for point p
3003 
3004   Output Parameter:
3005 
3006   Note:
3007   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3008 
3009   Level: beginner
3010 
3011 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3012 @*/
3013 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3014 {
3015   DM_Plex       *mesh = (DM_Plex*) dm->data;
3016   PetscInt       pStart, pEnd;
3017   PetscInt       dof, off, c;
3018 
3019   PetscFunctionBegin;
3020   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3021   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3022   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3023   if (dof) PetscValidIntPointer(cone, 3);
3024   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3025   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3026   for (c = 0; c < dof; ++c) {
3027     PetscCheck(!(cone[c] < pStart) && !(cone[c] >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", cone[c], pStart, pEnd);
3028     mesh->cones[off+c] = cone[c];
3029   }
3030   PetscFunctionReturn(0);
3031 }
3032 
3033 /*@C
3034   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3035 
3036   Not collective
3037 
3038   Input Parameters:
3039 + mesh - The DMPlex
3040 - p - The point, which must lie in the chart set with DMPlexSetChart()
3041 
3042   Output Parameter:
3043 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3044                     integer giving the prescription for cone traversal.
3045 
3046   Level: beginner
3047 
3048   Notes:
3049   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3050   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3051   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3052   with the identity.
3053 
3054   Fortran Notes:
3055   Since it returns an array, this routine is only available in Fortran 90, and you must
3056   include petsc.h90 in your code.
3057   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3058   DMPlexRestoreConeOrientation() is not needed/available in C.
3059 
3060 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3061 @*/
3062 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3063 {
3064   DM_Plex       *mesh = (DM_Plex*) dm->data;
3065   PetscInt       off;
3066 
3067   PetscFunctionBegin;
3068   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3069   if (PetscDefined(USE_DEBUG)) {
3070     PetscInt dof;
3071     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3072     if (dof) PetscValidPointer(coneOrientation, 3);
3073   }
3074   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3075 
3076   *coneOrientation = &mesh->coneOrientations[off];
3077   PetscFunctionReturn(0);
3078 }
3079 
3080 /*@
3081   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3082 
3083   Not collective
3084 
3085   Input Parameters:
3086 + mesh - The DMPlex
3087 . p - The point, which must lie in the chart set with DMPlexSetChart()
3088 - coneOrientation - An array of orientations
3089   Output Parameter:
3090 
3091   Notes:
3092   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3093 
3094   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3095 
3096   Level: beginner
3097 
3098 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3099 @*/
3100 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3101 {
3102   DM_Plex       *mesh = (DM_Plex*) dm->data;
3103   PetscInt       pStart, pEnd;
3104   PetscInt       dof, off, c;
3105 
3106   PetscFunctionBegin;
3107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3108   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3109   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3110   if (dof) PetscValidIntPointer(coneOrientation, 3);
3111   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3112   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3113   for (c = 0; c < dof; ++c) {
3114     PetscInt cdof, o = coneOrientation[c];
3115 
3116     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof));
3117     PetscCheck(!o || (o >= -(cdof+1) && o < cdof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ". %" PetscInt_FMT ")", o, -(cdof+1), cdof);
3118     mesh->coneOrientations[off+c] = o;
3119   }
3120   PetscFunctionReturn(0);
3121 }
3122 
3123 /*@
3124   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3125 
3126   Not collective
3127 
3128   Input Parameters:
3129 + mesh - The DMPlex
3130 . p - The point, which must lie in the chart set with DMPlexSetChart()
3131 . conePos - The local index in the cone where the point should be put
3132 - conePoint - The mesh point to insert
3133 
3134   Level: beginner
3135 
3136 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3137 @*/
3138 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3139 {
3140   DM_Plex       *mesh = (DM_Plex*) dm->data;
3141   PetscInt       pStart, pEnd;
3142   PetscInt       dof, off;
3143 
3144   PetscFunctionBegin;
3145   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3146   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3147   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3148   PetscCheck(!(conePoint < pStart) && !(conePoint >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", conePoint, pStart, pEnd);
3149   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3150   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3151   PetscCheck(!(conePos < 0) && !(conePos >= dof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3152   mesh->cones[off+conePos] = conePoint;
3153   PetscFunctionReturn(0);
3154 }
3155 
3156 /*@
3157   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3158 
3159   Not collective
3160 
3161   Input Parameters:
3162 + mesh - The DMPlex
3163 . p - The point, which must lie in the chart set with DMPlexSetChart()
3164 . conePos - The local index in the cone where the point should be put
3165 - coneOrientation - The point orientation to insert
3166 
3167   Level: beginner
3168 
3169   Notes:
3170   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3171 
3172 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3173 @*/
3174 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3175 {
3176   DM_Plex       *mesh = (DM_Plex*) dm->data;
3177   PetscInt       pStart, pEnd;
3178   PetscInt       dof, off;
3179 
3180   PetscFunctionBegin;
3181   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3182   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3183   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3184   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3185   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3186   PetscCheck(!(conePos < 0) && !(conePos >= dof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3187   mesh->coneOrientations[off+conePos] = coneOrientation;
3188   PetscFunctionReturn(0);
3189 }
3190 
3191 /*@
3192   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3193 
3194   Not collective
3195 
3196   Input Parameters:
3197 + mesh - The DMPlex
3198 - p - The point, which must lie in the chart set with DMPlexSetChart()
3199 
3200   Output Parameter:
3201 . size - The support size for point p
3202 
3203   Level: beginner
3204 
3205 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3206 @*/
3207 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3208 {
3209   DM_Plex       *mesh = (DM_Plex*) dm->data;
3210 
3211   PetscFunctionBegin;
3212   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3213   PetscValidIntPointer(size, 3);
3214   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3215   PetscFunctionReturn(0);
3216 }
3217 
3218 /*@
3219   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3220 
3221   Not collective
3222 
3223   Input Parameters:
3224 + mesh - The DMPlex
3225 . p - The point, which must lie in the chart set with DMPlexSetChart()
3226 - size - The support size for point p
3227 
3228   Output Parameter:
3229 
3230   Note:
3231   This should be called after DMPlexSetChart().
3232 
3233   Level: beginner
3234 
3235 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3236 @*/
3237 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3238 {
3239   DM_Plex       *mesh = (DM_Plex*) dm->data;
3240 
3241   PetscFunctionBegin;
3242   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3243   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3244   PetscFunctionReturn(0);
3245 }
3246 
3247 /*@C
3248   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3249 
3250   Not collective
3251 
3252   Input Parameters:
3253 + mesh - The DMPlex
3254 - p - The point, which must lie in the chart set with DMPlexSetChart()
3255 
3256   Output Parameter:
3257 . support - An array of points which are on the out-edges for point p
3258 
3259   Level: beginner
3260 
3261   Fortran Notes:
3262   Since it returns an array, this routine is only available in Fortran 90, and you must
3263   include petsc.h90 in your code.
3264   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3265   DMPlexRestoreSupport() is not needed/available in C.
3266 
3267 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3268 @*/
3269 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3270 {
3271   DM_Plex       *mesh = (DM_Plex*) dm->data;
3272   PetscInt       off;
3273 
3274   PetscFunctionBegin;
3275   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3276   PetscValidPointer(support, 3);
3277   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3278   *support = &mesh->supports[off];
3279   PetscFunctionReturn(0);
3280 }
3281 
3282 /*@
3283   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3284 
3285   Not collective
3286 
3287   Input Parameters:
3288 + mesh - The DMPlex
3289 . p - The point, which must lie in the chart set with DMPlexSetChart()
3290 - support - An array of points which are on the out-edges for point p
3291 
3292   Output Parameter:
3293 
3294   Note:
3295   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3296 
3297   Level: beginner
3298 
3299 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3300 @*/
3301 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3302 {
3303   DM_Plex       *mesh = (DM_Plex*) dm->data;
3304   PetscInt       pStart, pEnd;
3305   PetscInt       dof, off, c;
3306 
3307   PetscFunctionBegin;
3308   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3309   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3310   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3311   if (dof) PetscValidIntPointer(support, 3);
3312   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3313   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3314   for (c = 0; c < dof; ++c) {
3315     PetscCheck(!(support[c] < pStart) && !(support[c] >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", support[c], pStart, pEnd);
3316     mesh->supports[off+c] = support[c];
3317   }
3318   PetscFunctionReturn(0);
3319 }
3320 
3321 /*@
3322   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3323 
3324   Not collective
3325 
3326   Input Parameters:
3327 + mesh - The DMPlex
3328 . p - The point, which must lie in the chart set with DMPlexSetChart()
3329 . supportPos - The local index in the cone where the point should be put
3330 - supportPoint - The mesh point to insert
3331 
3332   Level: beginner
3333 
3334 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3335 @*/
3336 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3337 {
3338   DM_Plex       *mesh = (DM_Plex*) dm->data;
3339   PetscInt       pStart, pEnd;
3340   PetscInt       dof, off;
3341 
3342   PetscFunctionBegin;
3343   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3344   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3345   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3346   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3347   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3348   PetscCheck(!(supportPoint < pStart) && !(supportPoint >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", supportPoint, pStart, pEnd);
3349   PetscCheck(supportPos < dof,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", supportPos, p, dof);
3350   mesh->supports[off+supportPos] = supportPoint;
3351   PetscFunctionReturn(0);
3352 }
3353 
3354 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3355 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3356 {
3357   switch (ct) {
3358     case DM_POLYTOPE_SEGMENT:
3359       if (o == -1) return -2;
3360       break;
3361     case DM_POLYTOPE_TRIANGLE:
3362       if (o == -3) return -1;
3363       if (o == -2) return -3;
3364       if (o == -1) return -2;
3365       break;
3366     case DM_POLYTOPE_QUADRILATERAL:
3367       if (o == -4) return -2;
3368       if (o == -3) return -1;
3369       if (o == -2) return -4;
3370       if (o == -1) return -3;
3371       break;
3372     default: return o;
3373   }
3374   return o;
3375 }
3376 
3377 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3378 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3379 {
3380   switch (ct) {
3381     case DM_POLYTOPE_SEGMENT:
3382       if ((o == -2) || (o == 1)) return -1;
3383       if (o == -1) return 0;
3384       break;
3385     case DM_POLYTOPE_TRIANGLE:
3386       if (o == -3) return -2;
3387       if (o == -2) return -1;
3388       if (o == -1) return -3;
3389       break;
3390     case DM_POLYTOPE_QUADRILATERAL:
3391       if (o == -4) return -2;
3392       if (o == -3) return -1;
3393       if (o == -2) return -4;
3394       if (o == -1) return -3;
3395       break;
3396     default: return o;
3397   }
3398   return o;
3399 }
3400 
3401 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3402 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3403 {
3404   PetscInt       pStart, pEnd, p;
3405 
3406   PetscFunctionBegin;
3407   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3408   for (p = pStart; p < pEnd; ++p) {
3409     const PetscInt *cone, *ornt;
3410     PetscInt        coneSize, c;
3411 
3412     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3413     PetscCall(DMPlexGetCone(dm, p, &cone));
3414     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3415     for (c = 0; c < coneSize; ++c) {
3416       DMPolytopeType ct;
3417       const PetscInt o = ornt[c];
3418 
3419       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3420       switch (ct) {
3421         case DM_POLYTOPE_SEGMENT:
3422           if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3423           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3424           break;
3425         case DM_POLYTOPE_TRIANGLE:
3426           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3427           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3428           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3429           break;
3430         case DM_POLYTOPE_QUADRILATERAL:
3431           if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3432           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3433           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3434           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3435           break;
3436         default: break;
3437       }
3438     }
3439   }
3440   PetscFunctionReturn(0);
3441 }
3442 
3443 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3444 {
3445   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3446   PetscInt       *closure;
3447   const PetscInt *tmp = NULL, *tmpO = NULL;
3448   PetscInt        off = 0, tmpSize, t;
3449 
3450   PetscFunctionBeginHot;
3451   if (ornt) {
3452     PetscCall(DMPlexGetCellType(dm, p, &ct));
3453     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3454   }
3455   if (*points) {
3456     closure = *points;
3457   } else {
3458     PetscInt maxConeSize, maxSupportSize;
3459     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3460     PetscCall(DMGetWorkArray(dm, 2*(PetscMax(maxConeSize, maxSupportSize)+1), MPIU_INT, &closure));
3461   }
3462   if (useCone) {
3463     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3464     PetscCall(DMPlexGetCone(dm, p, &tmp));
3465     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3466   } else {
3467     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3468     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3469   }
3470   if (ct == DM_POLYTOPE_UNKNOWN) {
3471     closure[off++] = p;
3472     closure[off++] = 0;
3473     for (t = 0; t < tmpSize; ++t) {
3474       closure[off++] = tmp[t];
3475       closure[off++] = tmpO ? tmpO[t] : 0;
3476     }
3477   } else {
3478     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3479 
3480     /* We assume that cells with a valid type have faces with a valid type */
3481     closure[off++] = p;
3482     closure[off++] = ornt;
3483     for (t = 0; t < tmpSize; ++t) {
3484       DMPolytopeType ft;
3485 
3486       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3487       closure[off++] = tmp[arr[t]];
3488       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3489     }
3490   }
3491   if (numPoints) *numPoints = tmpSize+1;
3492   if (points)    *points    = closure;
3493   PetscFunctionReturn(0);
3494 }
3495 
3496 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3497 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3498 {
3499   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3500   const PetscInt *cone, *ornt;
3501   PetscInt       *pts,  *closure = NULL;
3502   DMPolytopeType  ft;
3503   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3504   PetscInt        dim, coneSize, c, d, clSize, cl;
3505 
3506   PetscFunctionBeginHot;
3507   PetscCall(DMGetDimension(dm, &dim));
3508   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3509   PetscCall(DMPlexGetCone(dm, point, &cone));
3510   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3511   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3512   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    dim+1)-1)/(maxConeSize-1))    : dim+1;
3513   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim+1)-1)/(maxSupportSize-1)) : dim+1;
3514   maxSize       = PetscMax(coneSeries, supportSeries);
3515   if (*points) {pts  = *points;}
3516   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &pts));
3517   c    = 0;
3518   pts[c++] = point;
3519   pts[c++] = o;
3520   PetscCall(DMPlexGetCellType(dm, cone[arr[0*2+0]], &ft));
3521   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[0*2+1], ornt[0]), useCone, &clSize, &closure));
3522   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3523   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[1*2+1], ornt[1]), useCone, &clSize, &closure));
3524   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3525   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3526   for (d = 2; d < coneSize; ++d) {
3527     PetscCall(DMPlexGetCellType(dm, cone[arr[d*2+0]], &ft));
3528     pts[c++] = cone[arr[d*2+0]];
3529     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]);
3530   }
3531   if (dim >= 3) {
3532     for (d = 2; d < coneSize; ++d) {
3533       const PetscInt  fpoint = cone[arr[d*2+0]];
3534       const PetscInt *fcone, *fornt;
3535       PetscInt        fconeSize, fc, i;
3536 
3537       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3538       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]));
3539       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3540       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3541       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3542       for (fc = 0; fc < fconeSize; ++fc) {
3543         const PetscInt cp = fcone[farr[fc*2+0]];
3544         const PetscInt co = farr[fc*2+1];
3545 
3546         for (i = 0; i < c; i += 2) if (pts[i] == cp) break;
3547         if (i == c) {
3548           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3549           pts[c++] = cp;
3550           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc*2+0]]);
3551         }
3552       }
3553     }
3554   }
3555   *numPoints = c/2;
3556   *points    = pts;
3557   PetscFunctionReturn(0);
3558 }
3559 
3560 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3561 {
3562   DMPolytopeType ct;
3563   PetscInt      *closure, *fifo;
3564   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3565   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3566   PetscInt       depth, maxSize;
3567 
3568   PetscFunctionBeginHot;
3569   PetscCall(DMPlexGetDepth(dm, &depth));
3570   if (depth == 1) {
3571     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3572     PetscFunctionReturn(0);
3573   }
3574   PetscCall(DMPlexGetCellType(dm, p, &ct));
3575   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3576   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3577     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3578     PetscFunctionReturn(0);
3579   }
3580   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3581   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    depth+1)-1)/(maxConeSize-1))    : depth+1;
3582   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth+1)-1)/(maxSupportSize-1)) : depth+1;
3583   maxSize       = PetscMax(coneSeries, supportSeries);
3584   PetscCall(DMGetWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3585   if (*points) {closure = *points;}
3586   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &closure));
3587   closure[closureSize++] = p;
3588   closure[closureSize++] = ornt;
3589   fifo[fifoSize++]       = p;
3590   fifo[fifoSize++]       = ornt;
3591   fifo[fifoSize++]       = ct;
3592   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3593   while (fifoSize - fifoStart) {
3594     const PetscInt       q    = fifo[fifoStart++];
3595     const PetscInt       o    = fifo[fifoStart++];
3596     const DMPolytopeType qt   = (DMPolytopeType) fifo[fifoStart++];
3597     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3598     const PetscInt      *tmp, *tmpO;
3599     PetscInt             tmpSize, t;
3600 
3601     if (PetscDefined(USE_DEBUG)) {
3602       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt)/2;
3603       PetscCheck(!o || !(o >= nO || o < -nO),PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid orientation %" PetscInt_FMT " not in [%" PetscInt_FMT ",%" PetscInt_FMT ") for %s %" PetscInt_FMT, o, -nO, nO, DMPolytopeTypes[qt], q);
3604     }
3605     if (useCone) {
3606       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3607       PetscCall(DMPlexGetCone(dm, q, &tmp));
3608       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3609     } else {
3610       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3611       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3612       tmpO = NULL;
3613     }
3614     for (t = 0; t < tmpSize; ++t) {
3615       const PetscInt ip = useCone && qarr ? qarr[t*2]   : t;
3616       const PetscInt io = useCone && qarr ? qarr[t*2+1] : 0;
3617       const PetscInt cp = tmp[ip];
3618       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3619       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3620       PetscInt       c;
3621 
3622       /* Check for duplicate */
3623       for (c = 0; c < closureSize; c += 2) {
3624         if (closure[c] == cp) break;
3625       }
3626       if (c == closureSize) {
3627         closure[closureSize++] = cp;
3628         closure[closureSize++] = co;
3629         fifo[fifoSize++]       = cp;
3630         fifo[fifoSize++]       = co;
3631         fifo[fifoSize++]       = ct;
3632       }
3633     }
3634   }
3635   PetscCall(DMRestoreWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3636   if (numPoints) *numPoints = closureSize/2;
3637   if (points)    *points    = closure;
3638   PetscFunctionReturn(0);
3639 }
3640 
3641 /*@C
3642   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3643 
3644   Not collective
3645 
3646   Input Parameters:
3647 + dm      - The DMPlex
3648 . p       - The mesh point
3649 - useCone - PETSC_TRUE for the closure, otherwise return the star
3650 
3651   Input/Output Parameter:
3652 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3653            if NULL on input, internal storage will be returned, otherwise the provided array is used
3654 
3655   Output Parameter:
3656 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3657 
3658   Note:
3659   If using internal storage (points is NULL on input), each call overwrites the last output.
3660 
3661   Fortran Notes:
3662   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3663 
3664   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3665 
3666   Level: beginner
3667 
3668 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3669 @*/
3670 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3671 {
3672   PetscFunctionBeginHot;
3673   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3674   if (numPoints) PetscValidIntPointer(numPoints, 4);
3675   if (points)    PetscValidPointer(points, 5);
3676   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3677   PetscFunctionReturn(0);
3678 }
3679 
3680 /*@C
3681   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3682 
3683   Not collective
3684 
3685   Input Parameters:
3686 + dm        - The DMPlex
3687 . p         - The mesh point
3688 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3689 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3690 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3691 
3692   Note:
3693   If not using internal storage (points is not NULL on input), this call is unnecessary
3694 
3695   Fortran Notes:
3696   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3697 
3698   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3699 
3700   Level: beginner
3701 
3702 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3703 @*/
3704 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3705 {
3706   PetscFunctionBeginHot;
3707   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3708   if (numPoints) *numPoints = 0;
3709   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3710   PetscFunctionReturn(0);
3711 }
3712 
3713 /*@
3714   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3715 
3716   Not collective
3717 
3718   Input Parameter:
3719 . mesh - The DMPlex
3720 
3721   Output Parameters:
3722 + maxConeSize - The maximum number of in-edges
3723 - maxSupportSize - The maximum number of out-edges
3724 
3725   Level: beginner
3726 
3727 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3728 @*/
3729 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3730 {
3731   DM_Plex *mesh = (DM_Plex*) dm->data;
3732 
3733   PetscFunctionBegin;
3734   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3735   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3736   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3737   PetscFunctionReturn(0);
3738 }
3739 
3740 PetscErrorCode DMSetUp_Plex(DM dm)
3741 {
3742   DM_Plex       *mesh = (DM_Plex*) dm->data;
3743   PetscInt       size, maxSupportSize;
3744 
3745   PetscFunctionBegin;
3746   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3747   PetscCall(PetscSectionSetUp(mesh->coneSection));
3748   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3749   PetscCall(PetscMalloc1(size, &mesh->cones));
3750   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3751   PetscCall(PetscLogObjectMemory((PetscObject) dm, size*2*sizeof(PetscInt)));
3752   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3753   if (maxSupportSize) {
3754     PetscCall(PetscSectionSetUp(mesh->supportSection));
3755     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3756     PetscCall(PetscMalloc1(size, &mesh->supports));
3757     PetscCall(PetscLogObjectMemory((PetscObject) dm, size*sizeof(PetscInt)));
3758   }
3759   PetscFunctionReturn(0);
3760 }
3761 
3762 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3763 {
3764   PetscFunctionBegin;
3765   if (subdm) PetscCall(DMClone(dm, subdm));
3766   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3767   if (subdm) {(*subdm)->useNatural = dm->useNatural;}
3768   if (dm->useNatural && dm->sfMigration) {
3769     PetscSF        sfMigrationInv,sfNatural;
3770     PetscSection   section, sectionSeq;
3771 
3772     (*subdm)->sfMigration = dm->sfMigration;
3773     PetscCall(PetscObjectReference((PetscObject) dm->sfMigration));
3774     PetscCall(DMGetLocalSection((*subdm), &section));
3775     PetscCall(PetscSFCreateInverseSF((*subdm)->sfMigration, &sfMigrationInv));
3776     PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*subdm)), &sectionSeq));
3777     PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3778 
3779     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, sectionSeq, (*subdm)->sfMigration, &sfNatural));
3780     (*subdm)->sfNatural = sfNatural;
3781     PetscCall(PetscSectionDestroy(&sectionSeq));
3782     PetscCall(PetscSFDestroy(&sfMigrationInv));
3783   }
3784   PetscFunctionReturn(0);
3785 }
3786 
3787 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3788 {
3789   PetscInt       i = 0;
3790 
3791   PetscFunctionBegin;
3792   PetscCall(DMClone(dms[0], superdm));
3793   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3794   (*superdm)->useNatural = PETSC_FALSE;
3795   for (i = 0; i < len; i++) {
3796     if (dms[i]->useNatural && dms[i]->sfMigration) {
3797       PetscSF        sfMigrationInv,sfNatural;
3798       PetscSection   section, sectionSeq;
3799 
3800       (*superdm)->sfMigration = dms[i]->sfMigration;
3801       PetscCall(PetscObjectReference((PetscObject) dms[i]->sfMigration));
3802       (*superdm)->useNatural = PETSC_TRUE;
3803       PetscCall(DMGetLocalSection((*superdm), &section));
3804       PetscCall(PetscSFCreateInverseSF((*superdm)->sfMigration, &sfMigrationInv));
3805       PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*superdm)), &sectionSeq));
3806       PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3807 
3808       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, sectionSeq, (*superdm)->sfMigration, &sfNatural));
3809       (*superdm)->sfNatural = sfNatural;
3810       PetscCall(PetscSectionDestroy(&sectionSeq));
3811       PetscCall(PetscSFDestroy(&sfMigrationInv));
3812       break;
3813     }
3814   }
3815   PetscFunctionReturn(0);
3816 }
3817 
3818 /*@
3819   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3820 
3821   Not collective
3822 
3823   Input Parameter:
3824 . mesh - The DMPlex
3825 
3826   Output Parameter:
3827 
3828   Note:
3829   This should be called after all calls to DMPlexSetCone()
3830 
3831   Level: beginner
3832 
3833 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3834 @*/
3835 PetscErrorCode DMPlexSymmetrize(DM dm)
3836 {
3837   DM_Plex       *mesh = (DM_Plex*) dm->data;
3838   PetscInt      *offsets;
3839   PetscInt       supportSize;
3840   PetscInt       pStart, pEnd, p;
3841 
3842   PetscFunctionBegin;
3843   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3844   PetscCheck(!mesh->supports,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3845   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0));
3846   /* Calculate support sizes */
3847   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3848   for (p = pStart; p < pEnd; ++p) {
3849     PetscInt dof, off, c;
3850 
3851     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3852     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3853     for (c = off; c < off+dof; ++c) {
3854       PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3855     }
3856   }
3857   PetscCall(PetscSectionSetUp(mesh->supportSection));
3858   /* Calculate supports */
3859   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3860   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3861   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3862   for (p = pStart; p < pEnd; ++p) {
3863     PetscInt dof, off, c;
3864 
3865     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3866     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3867     for (c = off; c < off+dof; ++c) {
3868       const PetscInt q = mesh->cones[c];
3869       PetscInt       offS;
3870 
3871       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3872 
3873       mesh->supports[offS+offsets[q]] = p;
3874       ++offsets[q];
3875     }
3876   }
3877   PetscCall(PetscFree(offsets));
3878   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0));
3879   PetscFunctionReturn(0);
3880 }
3881 
3882 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3883 {
3884   IS             stratumIS;
3885 
3886   PetscFunctionBegin;
3887   if (pStart >= pEnd) PetscFunctionReturn(0);
3888   if (PetscDefined(USE_DEBUG)) {
3889     PetscInt  qStart, qEnd, numLevels, level;
3890     PetscBool overlap = PETSC_FALSE;
3891     PetscCall(DMLabelGetNumValues(label, &numLevels));
3892     for (level = 0; level < numLevels; level++) {
3893       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3894       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
3895     }
3896     PetscCheck(!overlap,PETSC_COMM_SELF, PETSC_ERR_PLIB, "New depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ") overlaps with depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ")", depth, pStart, pEnd, level, qStart, qEnd);
3897   }
3898   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS));
3899   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3900   PetscCall(ISDestroy(&stratumIS));
3901   PetscFunctionReturn(0);
3902 }
3903 
3904 /*@
3905   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3906   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3907   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3908   the DAG.
3909 
3910   Collective on dm
3911 
3912   Input Parameter:
3913 . mesh - The DMPlex
3914 
3915   Output Parameter:
3916 
3917   Notes:
3918   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3919   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3920   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3921   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3922   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3923 
3924   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3925   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3926   we had a mesh consisting of one triangle (c0) and three vertices (v0, v1, v2), and only one edge is on the boundary so we choose
3927   to interpolate only that one (e0), so that
3928 $  cone(c0) = {e0, v2}
3929 $  cone(e0) = {v0, v1}
3930   If DMPlexStratify() is run on this mesh, it will give depths
3931 $  depth 0 = {v0, v1, v2}
3932 $  depth 1 = {e0, c0}
3933   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3934 
3935   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3936 
3937   Level: beginner
3938 
3939 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3940 @*/
3941 PetscErrorCode DMPlexStratify(DM dm)
3942 {
3943   DM_Plex       *mesh = (DM_Plex*) dm->data;
3944   DMLabel        label;
3945   PetscInt       pStart, pEnd, p;
3946   PetscInt       numRoots = 0, numLeaves = 0;
3947 
3948   PetscFunctionBegin;
3949   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3950   PetscCall(PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0));
3951 
3952   /* Create depth label */
3953   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3954   PetscCall(DMCreateLabel(dm, "depth"));
3955   PetscCall(DMPlexGetDepthLabel(dm, &label));
3956 
3957   {
3958     /* Initialize roots and count leaves */
3959     PetscInt sMin = PETSC_MAX_INT;
3960     PetscInt sMax = PETSC_MIN_INT;
3961     PetscInt coneSize, supportSize;
3962 
3963     for (p = pStart; p < pEnd; ++p) {
3964       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3965       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3966       if (!coneSize && supportSize) {
3967         sMin = PetscMin(p, sMin);
3968         sMax = PetscMax(p, sMax);
3969         ++numRoots;
3970       } else if (!supportSize && coneSize) {
3971         ++numLeaves;
3972       } else if (!supportSize && !coneSize) {
3973         /* Isolated points */
3974         sMin = PetscMin(p, sMin);
3975         sMax = PetscMax(p, sMax);
3976       }
3977     }
3978     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1));
3979   }
3980 
3981   if (numRoots + numLeaves == (pEnd - pStart)) {
3982     PetscInt sMin = PETSC_MAX_INT;
3983     PetscInt sMax = PETSC_MIN_INT;
3984     PetscInt coneSize, supportSize;
3985 
3986     for (p = pStart; p < pEnd; ++p) {
3987       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3988       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3989       if (!supportSize && coneSize) {
3990         sMin = PetscMin(p, sMin);
3991         sMax = PetscMax(p, sMax);
3992       }
3993     }
3994     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1));
3995   } else {
3996     PetscInt level = 0;
3997     PetscInt qStart, qEnd, q;
3998 
3999     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4000     while (qEnd > qStart) {
4001       PetscInt sMin = PETSC_MAX_INT;
4002       PetscInt sMax = PETSC_MIN_INT;
4003 
4004       for (q = qStart; q < qEnd; ++q) {
4005         const PetscInt *support;
4006         PetscInt        supportSize, s;
4007 
4008         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4009         PetscCall(DMPlexGetSupport(dm, q, &support));
4010         for (s = 0; s < supportSize; ++s) {
4011           sMin = PetscMin(support[s], sMin);
4012           sMax = PetscMax(support[s], sMax);
4013         }
4014       }
4015       PetscCall(DMLabelGetNumValues(label, &level));
4016       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1));
4017       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4018     }
4019   }
4020   { /* just in case there is an empty process */
4021     PetscInt numValues, maxValues = 0, v;
4022 
4023     PetscCall(DMLabelGetNumValues(label, &numValues));
4024     PetscCallMPI(MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm)));
4025     for (v = numValues; v < maxValues; v++) {
4026       PetscCall(DMLabelAddStratum(label, v));
4027     }
4028   }
4029   PetscCall(PetscObjectStateGet((PetscObject) label, &mesh->depthState));
4030   PetscCall(PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0));
4031   PetscFunctionReturn(0);
4032 }
4033 
4034 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4035 {
4036   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4037   PetscInt       dim, depth, pheight, coneSize;
4038 
4039   PetscFunctionBeginHot;
4040   PetscCall(DMGetDimension(dm, &dim));
4041   PetscCall(DMPlexGetDepth(dm, &depth));
4042   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4043   pheight = depth - pdepth;
4044   if (depth <= 1) {
4045     switch (pdepth) {
4046       case 0: ct = DM_POLYTOPE_POINT;break;
4047       case 1:
4048         switch (coneSize) {
4049           case 2: ct = DM_POLYTOPE_SEGMENT;break;
4050           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4051           case 4:
4052           switch (dim) {
4053             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
4054             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
4055             default: break;
4056           }
4057           break;
4058         case 5: ct = DM_POLYTOPE_PYRAMID;break;
4059         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4060         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
4061         default: break;
4062       }
4063     }
4064   } else {
4065     if (pdepth == 0) {
4066       ct = DM_POLYTOPE_POINT;
4067     } else if (pheight == 0) {
4068       switch (dim) {
4069         case 1:
4070           switch (coneSize) {
4071             case 2: ct = DM_POLYTOPE_SEGMENT;break;
4072             default: break;
4073           }
4074           break;
4075         case 2:
4076           switch (coneSize) {
4077             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4078             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4079             default: break;
4080           }
4081           break;
4082         case 3:
4083           switch (coneSize) {
4084             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
4085             case 5:
4086             {
4087               const PetscInt *cone;
4088               PetscInt        faceConeSize;
4089 
4090               PetscCall(DMPlexGetCone(dm, p, &cone));
4091               PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4092               switch (faceConeSize) {
4093                 case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4094                 case 4: ct = DM_POLYTOPE_PYRAMID;break;
4095               }
4096             }
4097             break;
4098             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
4099             default: break;
4100           }
4101           break;
4102         default: break;
4103       }
4104     } else if (pheight > 0) {
4105       switch (coneSize) {
4106         case 2: ct = DM_POLYTOPE_SEGMENT;break;
4107         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4108         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4109         default: break;
4110       }
4111     }
4112   }
4113   *pt = ct;
4114   PetscFunctionReturn(0);
4115 }
4116 
4117 /*@
4118   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4119 
4120   Collective on dm
4121 
4122   Input Parameter:
4123 . mesh - The DMPlex
4124 
4125   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4126 
4127   Level: developer
4128 
4129   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4130   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4131   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4132 
4133 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4134 @*/
4135 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4136 {
4137   DM_Plex       *mesh;
4138   DMLabel        ctLabel;
4139   PetscInt       pStart, pEnd, p;
4140 
4141   PetscFunctionBegin;
4142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4143   mesh = (DM_Plex *) dm->data;
4144   PetscCall(DMCreateLabel(dm, "celltype"));
4145   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4146   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4147   for (p = pStart; p < pEnd; ++p) {
4148     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4149     PetscInt       pdepth;
4150 
4151     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4152     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4153     PetscCheck(ct != DM_POLYTOPE_UNKNOWN,PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4154     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4155   }
4156   PetscCall(PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState));
4157   PetscCall(PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view"));
4158   PetscFunctionReturn(0);
4159 }
4160 
4161 /*@C
4162   DMPlexGetJoin - Get an array for the join of the set of points
4163 
4164   Not Collective
4165 
4166   Input Parameters:
4167 + dm - The DMPlex object
4168 . numPoints - The number of input points for the join
4169 - points - The input points
4170 
4171   Output Parameters:
4172 + numCoveredPoints - The number of points in the join
4173 - coveredPoints - The points in the join
4174 
4175   Level: intermediate
4176 
4177   Note: Currently, this is restricted to a single level join
4178 
4179   Fortran Notes:
4180   Since it returns an array, this routine is only available in Fortran 90, and you must
4181   include petsc.h90 in your code.
4182 
4183   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4184 
4185 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4186 @*/
4187 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4188 {
4189   DM_Plex       *mesh = (DM_Plex*) dm->data;
4190   PetscInt      *join[2];
4191   PetscInt       joinSize, i = 0;
4192   PetscInt       dof, off, p, c, m;
4193   PetscInt       maxSupportSize;
4194 
4195   PetscFunctionBegin;
4196   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4197   PetscValidIntPointer(points, 3);
4198   PetscValidIntPointer(numCoveredPoints, 4);
4199   PetscValidPointer(coveredPoints, 5);
4200   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4201   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4202   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4203   /* Copy in support of first point */
4204   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4205   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4206   for (joinSize = 0; joinSize < dof; ++joinSize) {
4207     join[i][joinSize] = mesh->supports[off+joinSize];
4208   }
4209   /* Check each successive support */
4210   for (p = 1; p < numPoints; ++p) {
4211     PetscInt newJoinSize = 0;
4212 
4213     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4214     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4215     for (c = 0; c < dof; ++c) {
4216       const PetscInt point = mesh->supports[off+c];
4217 
4218       for (m = 0; m < joinSize; ++m) {
4219         if (point == join[i][m]) {
4220           join[1-i][newJoinSize++] = point;
4221           break;
4222         }
4223       }
4224     }
4225     joinSize = newJoinSize;
4226     i        = 1-i;
4227   }
4228   *numCoveredPoints = joinSize;
4229   *coveredPoints    = join[i];
4230   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1-i]));
4231   PetscFunctionReturn(0);
4232 }
4233 
4234 /*@C
4235   DMPlexRestoreJoin - Restore an array for the join of the set of points
4236 
4237   Not Collective
4238 
4239   Input Parameters:
4240 + dm - The DMPlex object
4241 . numPoints - The number of input points for the join
4242 - points - The input points
4243 
4244   Output Parameters:
4245 + numCoveredPoints - The number of points in the join
4246 - coveredPoints - The points in the join
4247 
4248   Fortran Notes:
4249   Since it returns an array, this routine is only available in Fortran 90, and you must
4250   include petsc.h90 in your code.
4251 
4252   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4253 
4254   Level: intermediate
4255 
4256 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4257 @*/
4258 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4259 {
4260   PetscFunctionBegin;
4261   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4262   if (points) PetscValidIntPointer(points,3);
4263   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4264   PetscValidPointer(coveredPoints, 5);
4265   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4266   if (numCoveredPoints) *numCoveredPoints = 0;
4267   PetscFunctionReturn(0);
4268 }
4269 
4270 /*@C
4271   DMPlexGetFullJoin - Get an array for the join of the set of points
4272 
4273   Not Collective
4274 
4275   Input Parameters:
4276 + dm - The DMPlex object
4277 . numPoints - The number of input points for the join
4278 - points - The input points
4279 
4280   Output Parameters:
4281 + numCoveredPoints - The number of points in the join
4282 - coveredPoints - The points in the join
4283 
4284   Fortran Notes:
4285   Since it returns an array, this routine is only available in Fortran 90, and you must
4286   include petsc.h90 in your code.
4287 
4288   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4289 
4290   Level: intermediate
4291 
4292 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4293 @*/
4294 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4295 {
4296   PetscInt      *offsets, **closures;
4297   PetscInt      *join[2];
4298   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
4299   PetscInt       p, d, c, m, ms;
4300 
4301   PetscFunctionBegin;
4302   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4303   PetscValidIntPointer(points, 3);
4304   PetscValidIntPointer(numCoveredPoints, 4);
4305   PetscValidPointer(coveredPoints, 5);
4306 
4307   PetscCall(DMPlexGetDepth(dm, &depth));
4308   PetscCall(PetscCalloc1(numPoints, &closures));
4309   PetscCall(DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4310   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4311   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
4312   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4313   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4314 
4315   for (p = 0; p < numPoints; ++p) {
4316     PetscInt closureSize;
4317 
4318     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4319 
4320     offsets[p*(depth+2)+0] = 0;
4321     for (d = 0; d < depth+1; ++d) {
4322       PetscInt pStart, pEnd, i;
4323 
4324       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4325       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
4326         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4327           offsets[p*(depth+2)+d+1] = i;
4328           break;
4329         }
4330       }
4331       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
4332     }
4333     PetscCheck(offsets[p*(depth+2)+depth+1] == closureSize,PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p*(depth+2)+depth+1], closureSize);
4334   }
4335   for (d = 0; d < depth+1; ++d) {
4336     PetscInt dof;
4337 
4338     /* Copy in support of first point */
4339     dof = offsets[d+1] - offsets[d];
4340     for (joinSize = 0; joinSize < dof; ++joinSize) {
4341       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
4342     }
4343     /* Check each successive cone */
4344     for (p = 1; p < numPoints && joinSize; ++p) {
4345       PetscInt newJoinSize = 0;
4346 
4347       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
4348       for (c = 0; c < dof; ++c) {
4349         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
4350 
4351         for (m = 0; m < joinSize; ++m) {
4352           if (point == join[i][m]) {
4353             join[1-i][newJoinSize++] = point;
4354             break;
4355           }
4356         }
4357       }
4358       joinSize = newJoinSize;
4359       i        = 1-i;
4360     }
4361     if (joinSize) break;
4362   }
4363   *numCoveredPoints = joinSize;
4364   *coveredPoints    = join[i];
4365   for (p = 0; p < numPoints; ++p) {
4366     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4367   }
4368   PetscCall(PetscFree(closures));
4369   PetscCall(DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4370   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1-i]));
4371   PetscFunctionReturn(0);
4372 }
4373 
4374 /*@C
4375   DMPlexGetMeet - Get an array for the meet of the set of points
4376 
4377   Not Collective
4378 
4379   Input Parameters:
4380 + dm - The DMPlex object
4381 . numPoints - The number of input points for the meet
4382 - points - The input points
4383 
4384   Output Parameters:
4385 + numCoveredPoints - The number of points in the meet
4386 - coveredPoints - The points in the meet
4387 
4388   Level: intermediate
4389 
4390   Note: Currently, this is restricted to a single level meet
4391 
4392   Fortran Notes:
4393   Since it returns an array, this routine is only available in Fortran 90, and you must
4394   include petsc.h90 in your code.
4395 
4396   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4397 
4398 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4399 @*/
4400 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4401 {
4402   DM_Plex       *mesh = (DM_Plex*) dm->data;
4403   PetscInt      *meet[2];
4404   PetscInt       meetSize, i = 0;
4405   PetscInt       dof, off, p, c, m;
4406   PetscInt       maxConeSize;
4407 
4408   PetscFunctionBegin;
4409   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4410   PetscValidIntPointer(points, 3);
4411   PetscValidIntPointer(numCoveringPoints, 4);
4412   PetscValidPointer(coveringPoints, 5);
4413   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4414   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4415   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4416   /* Copy in cone of first point */
4417   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4418   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4419   for (meetSize = 0; meetSize < dof; ++meetSize) {
4420     meet[i][meetSize] = mesh->cones[off+meetSize];
4421   }
4422   /* Check each successive cone */
4423   for (p = 1; p < numPoints; ++p) {
4424     PetscInt newMeetSize = 0;
4425 
4426     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4427     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4428     for (c = 0; c < dof; ++c) {
4429       const PetscInt point = mesh->cones[off+c];
4430 
4431       for (m = 0; m < meetSize; ++m) {
4432         if (point == meet[i][m]) {
4433           meet[1-i][newMeetSize++] = point;
4434           break;
4435         }
4436       }
4437     }
4438     meetSize = newMeetSize;
4439     i        = 1-i;
4440   }
4441   *numCoveringPoints = meetSize;
4442   *coveringPoints    = meet[i];
4443   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1-i]));
4444   PetscFunctionReturn(0);
4445 }
4446 
4447 /*@C
4448   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4449 
4450   Not Collective
4451 
4452   Input Parameters:
4453 + dm - The DMPlex object
4454 . numPoints - The number of input points for the meet
4455 - points - The input points
4456 
4457   Output Parameters:
4458 + numCoveredPoints - The number of points in the meet
4459 - coveredPoints - The points in the meet
4460 
4461   Level: intermediate
4462 
4463   Fortran Notes:
4464   Since it returns an array, this routine is only available in Fortran 90, and you must
4465   include petsc.h90 in your code.
4466 
4467   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4468 
4469 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4470 @*/
4471 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4472 {
4473   PetscFunctionBegin;
4474   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4475   if (points) PetscValidIntPointer(points,3);
4476   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4477   PetscValidPointer(coveredPoints,5);
4478   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4479   if (numCoveredPoints) *numCoveredPoints = 0;
4480   PetscFunctionReturn(0);
4481 }
4482 
4483 /*@C
4484   DMPlexGetFullMeet - Get an array for the meet of the set of points
4485 
4486   Not Collective
4487 
4488   Input Parameters:
4489 + dm - The DMPlex object
4490 . numPoints - The number of input points for the meet
4491 - points - The input points
4492 
4493   Output Parameters:
4494 + numCoveredPoints - The number of points in the meet
4495 - coveredPoints - The points in the meet
4496 
4497   Level: intermediate
4498 
4499   Fortran Notes:
4500   Since it returns an array, this routine is only available in Fortran 90, and you must
4501   include petsc.h90 in your code.
4502 
4503   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4504 
4505 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4506 @*/
4507 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4508 {
4509   PetscInt      *offsets, **closures;
4510   PetscInt      *meet[2];
4511   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
4512   PetscInt       p, h, c, m, mc;
4513 
4514   PetscFunctionBegin;
4515   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4516   PetscValidIntPointer(points, 3);
4517   PetscValidIntPointer(numCoveredPoints, 4);
4518   PetscValidPointer(coveredPoints, 5);
4519 
4520   PetscCall(DMPlexGetDepth(dm, &height));
4521   PetscCall(PetscMalloc1(numPoints, &closures));
4522   PetscCall(DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4523   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4524   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
4525   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4526   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4527 
4528   for (p = 0; p < numPoints; ++p) {
4529     PetscInt closureSize;
4530 
4531     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4532 
4533     offsets[p*(height+2)+0] = 0;
4534     for (h = 0; h < height+1; ++h) {
4535       PetscInt pStart, pEnd, i;
4536 
4537       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4538       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
4539         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4540           offsets[p*(height+2)+h+1] = i;
4541           break;
4542         }
4543       }
4544       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
4545     }
4546     PetscCheck(offsets[p*(height+2)+height+1] == closureSize,PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p*(height+2)+height+1], closureSize);
4547   }
4548   for (h = 0; h < height+1; ++h) {
4549     PetscInt dof;
4550 
4551     /* Copy in cone of first point */
4552     dof = offsets[h+1] - offsets[h];
4553     for (meetSize = 0; meetSize < dof; ++meetSize) {
4554       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
4555     }
4556     /* Check each successive cone */
4557     for (p = 1; p < numPoints && meetSize; ++p) {
4558       PetscInt newMeetSize = 0;
4559 
4560       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
4561       for (c = 0; c < dof; ++c) {
4562         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
4563 
4564         for (m = 0; m < meetSize; ++m) {
4565           if (point == meet[i][m]) {
4566             meet[1-i][newMeetSize++] = point;
4567             break;
4568           }
4569         }
4570       }
4571       meetSize = newMeetSize;
4572       i        = 1-i;
4573     }
4574     if (meetSize) break;
4575   }
4576   *numCoveredPoints = meetSize;
4577   *coveredPoints    = meet[i];
4578   for (p = 0; p < numPoints; ++p) {
4579     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4580   }
4581   PetscCall(PetscFree(closures));
4582   PetscCall(DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4583   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1-i]));
4584   PetscFunctionReturn(0);
4585 }
4586 
4587 /*@C
4588   DMPlexEqual - Determine if two DMs have the same topology
4589 
4590   Not Collective
4591 
4592   Input Parameters:
4593 + dmA - A DMPlex object
4594 - dmB - A DMPlex object
4595 
4596   Output Parameters:
4597 . equal - PETSC_TRUE if the topologies are identical
4598 
4599   Level: intermediate
4600 
4601   Notes:
4602   We are not solving graph isomorphism, so we do not permutation.
4603 
4604 .seealso: `DMPlexGetCone()`
4605 @*/
4606 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4607 {
4608   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4609 
4610   PetscFunctionBegin;
4611   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4612   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4613   PetscValidBoolPointer(equal, 3);
4614 
4615   *equal = PETSC_FALSE;
4616   PetscCall(DMPlexGetDepth(dmA, &depth));
4617   PetscCall(DMPlexGetDepth(dmB, &depthB));
4618   if (depth != depthB) PetscFunctionReturn(0);
4619   PetscCall(DMPlexGetChart(dmA, &pStart,  &pEnd));
4620   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4621   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4622   for (p = pStart; p < pEnd; ++p) {
4623     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4624     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4625 
4626     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4627     PetscCall(DMPlexGetCone(dmA, p, &cone));
4628     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4629     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4630     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4631     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4632     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4633     for (c = 0; c < coneSize; ++c) {
4634       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4635       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4636     }
4637     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4638     PetscCall(DMPlexGetSupport(dmA, p, &support));
4639     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4640     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4641     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4642     for (s = 0; s < supportSize; ++s) {
4643       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4644     }
4645   }
4646   *equal = PETSC_TRUE;
4647   PetscFunctionReturn(0);
4648 }
4649 
4650 /*@C
4651   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4652 
4653   Not Collective
4654 
4655   Input Parameters:
4656 + dm         - The DMPlex
4657 . cellDim    - The cell dimension
4658 - numCorners - The number of vertices on a cell
4659 
4660   Output Parameters:
4661 . numFaceVertices - The number of vertices on a face
4662 
4663   Level: developer
4664 
4665   Notes:
4666   Of course this can only work for a restricted set of symmetric shapes
4667 
4668 .seealso: `DMPlexGetCone()`
4669 @*/
4670 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4671 {
4672   MPI_Comm       comm;
4673 
4674   PetscFunctionBegin;
4675   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
4676   PetscValidIntPointer(numFaceVertices,4);
4677   switch (cellDim) {
4678   case 0:
4679     *numFaceVertices = 0;
4680     break;
4681   case 1:
4682     *numFaceVertices = 1;
4683     break;
4684   case 2:
4685     switch (numCorners) {
4686     case 3: /* triangle */
4687       *numFaceVertices = 2; /* Edge has 2 vertices */
4688       break;
4689     case 4: /* quadrilateral */
4690       *numFaceVertices = 2; /* Edge has 2 vertices */
4691       break;
4692     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
4693       *numFaceVertices = 3; /* Edge has 3 vertices */
4694       break;
4695     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4696       *numFaceVertices = 3; /* Edge has 3 vertices */
4697       break;
4698     default:
4699       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4700     }
4701     break;
4702   case 3:
4703     switch (numCorners) {
4704     case 4: /* tetradehdron */
4705       *numFaceVertices = 3; /* Face has 3 vertices */
4706       break;
4707     case 6: /* tet cohesive cells */
4708       *numFaceVertices = 4; /* Face has 4 vertices */
4709       break;
4710     case 8: /* hexahedron */
4711       *numFaceVertices = 4; /* Face has 4 vertices */
4712       break;
4713     case 9: /* tet cohesive Lagrange cells */
4714       *numFaceVertices = 6; /* Face has 6 vertices */
4715       break;
4716     case 10: /* quadratic tetrahedron */
4717       *numFaceVertices = 6; /* Face has 6 vertices */
4718       break;
4719     case 12: /* hex cohesive Lagrange cells */
4720       *numFaceVertices = 6; /* Face has 6 vertices */
4721       break;
4722     case 18: /* quadratic tet cohesive Lagrange cells */
4723       *numFaceVertices = 6; /* Face has 6 vertices */
4724       break;
4725     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4726       *numFaceVertices = 9; /* Face has 9 vertices */
4727       break;
4728     default:
4729       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4730     }
4731     break;
4732   default:
4733     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4734   }
4735   PetscFunctionReturn(0);
4736 }
4737 
4738 /*@
4739   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4740 
4741   Not Collective
4742 
4743   Input Parameter:
4744 . dm    - The DMPlex object
4745 
4746   Output Parameter:
4747 . depthLabel - The DMLabel recording point depth
4748 
4749   Level: developer
4750 
4751 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4752 @*/
4753 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4754 {
4755   PetscFunctionBegin;
4756   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4757   PetscValidPointer(depthLabel, 2);
4758   *depthLabel = dm->depthLabel;
4759   PetscFunctionReturn(0);
4760 }
4761 
4762 /*@
4763   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4764 
4765   Not Collective
4766 
4767   Input Parameter:
4768 . dm    - The DMPlex object
4769 
4770   Output Parameter:
4771 . depth - The number of strata (breadth first levels) in the DAG
4772 
4773   Level: developer
4774 
4775   Notes:
4776   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4777   The point depth is described more in detail in DMPlexGetDepthStratum().
4778   An empty mesh gives -1.
4779 
4780 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4781 @*/
4782 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4783 {
4784   DMLabel        label;
4785   PetscInt       d = 0;
4786 
4787   PetscFunctionBegin;
4788   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4789   PetscValidIntPointer(depth, 2);
4790   PetscCall(DMPlexGetDepthLabel(dm, &label));
4791   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4792   *depth = d-1;
4793   PetscFunctionReturn(0);
4794 }
4795 
4796 /*@
4797   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4798 
4799   Not Collective
4800 
4801   Input Parameters:
4802 + dm    - The DMPlex object
4803 - depth - The requested depth
4804 
4805   Output Parameters:
4806 + start - The first point at this depth
4807 - end   - One beyond the last point at this depth
4808 
4809   Notes:
4810   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4811   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4812   higher dimension, e.g., "edges".
4813 
4814   Level: developer
4815 
4816 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4817 @*/
4818 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4819 {
4820   DMLabel        label;
4821   PetscInt       pStart, pEnd;
4822 
4823   PetscFunctionBegin;
4824   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4825   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4826   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4827   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4828   if (pStart == pEnd) PetscFunctionReturn(0);
4829   if (depth < 0) {
4830     if (start) *start = pStart;
4831     if (end)   *end   = pEnd;
4832     PetscFunctionReturn(0);
4833   }
4834   PetscCall(DMPlexGetDepthLabel(dm, &label));
4835   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4836   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4837   PetscFunctionReturn(0);
4838 }
4839 
4840 /*@
4841   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4842 
4843   Not Collective
4844 
4845   Input Parameters:
4846 + dm     - The DMPlex object
4847 - height - The requested height
4848 
4849   Output Parameters:
4850 + start - The first point at this height
4851 - end   - One beyond the last point at this height
4852 
4853   Notes:
4854   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4855   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4856   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4857 
4858   Level: developer
4859 
4860 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4861 @*/
4862 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4863 {
4864   DMLabel        label;
4865   PetscInt       depth, pStart, pEnd;
4866 
4867   PetscFunctionBegin;
4868   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4869   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4870   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4871   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4872   if (pStart == pEnd) PetscFunctionReturn(0);
4873   if (height < 0) {
4874     if (start) *start = pStart;
4875     if (end)   *end   = pEnd;
4876     PetscFunctionReturn(0);
4877   }
4878   PetscCall(DMPlexGetDepthLabel(dm, &label));
4879   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4880   PetscCall(DMLabelGetNumValues(label, &depth));
4881   PetscCall(DMLabelGetStratumBounds(label, depth-1-height, start, end));
4882   PetscFunctionReturn(0);
4883 }
4884 
4885 /*@
4886   DMPlexGetPointDepth - Get the depth of a given point
4887 
4888   Not Collective
4889 
4890   Input Parameters:
4891 + dm    - The DMPlex object
4892 - point - The point
4893 
4894   Output Parameter:
4895 . depth - The depth of the point
4896 
4897   Level: intermediate
4898 
4899 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4900 @*/
4901 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4902 {
4903   PetscFunctionBegin;
4904   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4905   PetscValidIntPointer(depth, 3);
4906   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4907   PetscFunctionReturn(0);
4908 }
4909 
4910 /*@
4911   DMPlexGetPointHeight - Get the height of a given point
4912 
4913   Not Collective
4914 
4915   Input Parameters:
4916 + dm    - The DMPlex object
4917 - point - The point
4918 
4919   Output Parameter:
4920 . height - The height of the point
4921 
4922   Level: intermediate
4923 
4924 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4925 @*/
4926 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4927 {
4928   PetscInt       n, pDepth;
4929 
4930   PetscFunctionBegin;
4931   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4932   PetscValidIntPointer(height, 3);
4933   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4934   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4935   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
4936   PetscFunctionReturn(0);
4937 }
4938 
4939 /*@
4940   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4941 
4942   Not Collective
4943 
4944   Input Parameter:
4945 . dm - The DMPlex object
4946 
4947   Output Parameter:
4948 . celltypeLabel - The DMLabel recording cell polytope type
4949 
4950   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4951   DMCreateLabel(dm, "celltype") beforehand.
4952 
4953   Level: developer
4954 
4955 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4956 @*/
4957 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
4958 {
4959   PetscFunctionBegin;
4960   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4961   PetscValidPointer(celltypeLabel, 2);
4962   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4963   *celltypeLabel = dm->celltypeLabel;
4964   PetscFunctionReturn(0);
4965 }
4966 
4967 /*@
4968   DMPlexGetCellType - Get the polytope type of a given cell
4969 
4970   Not Collective
4971 
4972   Input Parameters:
4973 + dm   - The DMPlex object
4974 - cell - The cell
4975 
4976   Output Parameter:
4977 . celltype - The polytope type of the cell
4978 
4979   Level: intermediate
4980 
4981 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4982 @*/
4983 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
4984 {
4985   DMLabel        label;
4986   PetscInt       ct;
4987 
4988   PetscFunctionBegin;
4989   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4990   PetscValidPointer(celltype, 3);
4991   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4992   PetscCall(DMLabelGetValue(label, cell, &ct));
4993   PetscCheck(ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4994   *celltype = (DMPolytopeType) ct;
4995   PetscFunctionReturn(0);
4996 }
4997 
4998 /*@
4999   DMPlexSetCellType - Set the polytope type of a given cell
5000 
5001   Not Collective
5002 
5003   Input Parameters:
5004 + dm   - The DMPlex object
5005 . cell - The cell
5006 - celltype - The polytope type of the cell
5007 
5008   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
5009   is executed. This function will override the computed type. However, if automatic classification will not succeed
5010   and a user wants to manually specify all types, the classification must be disabled by calling
5011   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5012 
5013   Level: advanced
5014 
5015 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5016 @*/
5017 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5018 {
5019   DMLabel        label;
5020 
5021   PetscFunctionBegin;
5022   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5023   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5024   PetscCall(DMLabelSetValue(label, cell, celltype));
5025   PetscFunctionReturn(0);
5026 }
5027 
5028 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5029 {
5030   PetscSection   section, s;
5031   Mat            m;
5032   PetscInt       maxHeight;
5033 
5034   PetscFunctionBegin;
5035   PetscCall(DMClone(dm, cdm));
5036   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5037   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5038   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5039   PetscCall(DMSetLocalSection(*cdm, section));
5040   PetscCall(PetscSectionDestroy(&section));
5041   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5042   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5043   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5044   PetscCall(PetscSectionDestroy(&s));
5045   PetscCall(MatDestroy(&m));
5046 
5047   PetscCall(DMSetNumFields(*cdm, 1));
5048   PetscCall(DMCreateDS(*cdm));
5049   PetscFunctionReturn(0);
5050 }
5051 
5052 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5053 {
5054   Vec coordsLocal, cellCoordsLocal;
5055   DM  coordsDM,    cellCoordsDM;
5056 
5057   PetscFunctionBegin;
5058   *field = NULL;
5059   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5060   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5061   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5062   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5063   if (coordsLocal && coordsDM) {
5064     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5065     else                                 PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5066   }
5067   PetscFunctionReturn(0);
5068 }
5069 
5070 /*@C
5071   DMPlexGetConeSection - Return a section which describes the layout of cone data
5072 
5073   Not Collective
5074 
5075   Input Parameters:
5076 . dm        - The DMPlex object
5077 
5078   Output Parameter:
5079 . section - The PetscSection object
5080 
5081   Level: developer
5082 
5083 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5084 @*/
5085 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5086 {
5087   DM_Plex *mesh = (DM_Plex*) dm->data;
5088 
5089   PetscFunctionBegin;
5090   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5091   if (section) *section = mesh->coneSection;
5092   PetscFunctionReturn(0);
5093 }
5094 
5095 /*@C
5096   DMPlexGetSupportSection - Return a section which describes the layout of support data
5097 
5098   Not Collective
5099 
5100   Input Parameters:
5101 . dm        - The DMPlex object
5102 
5103   Output Parameter:
5104 . section - The PetscSection object
5105 
5106   Level: developer
5107 
5108 .seealso: `DMPlexGetConeSection()`
5109 @*/
5110 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5111 {
5112   DM_Plex *mesh = (DM_Plex*) dm->data;
5113 
5114   PetscFunctionBegin;
5115   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5116   if (section) *section = mesh->supportSection;
5117   PetscFunctionReturn(0);
5118 }
5119 
5120 /*@C
5121   DMPlexGetCones - Return cone data
5122 
5123   Not Collective
5124 
5125   Input Parameters:
5126 . dm        - The DMPlex object
5127 
5128   Output Parameter:
5129 . cones - The cone for each point
5130 
5131   Level: developer
5132 
5133 .seealso: `DMPlexGetConeSection()`
5134 @*/
5135 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5136 {
5137   DM_Plex *mesh = (DM_Plex*) dm->data;
5138 
5139   PetscFunctionBegin;
5140   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5141   if (cones) *cones = mesh->cones;
5142   PetscFunctionReturn(0);
5143 }
5144 
5145 /*@C
5146   DMPlexGetConeOrientations - Return cone orientation data
5147 
5148   Not Collective
5149 
5150   Input Parameters:
5151 . dm        - The DMPlex object
5152 
5153   Output Parameter:
5154 . coneOrientations - The array of cone orientations for all points
5155 
5156   Level: developer
5157 
5158   Notes:
5159   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5160 
5161   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5162 
5163 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5164 @*/
5165 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5166 {
5167   DM_Plex *mesh = (DM_Plex*) dm->data;
5168 
5169   PetscFunctionBegin;
5170   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5171   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5172   PetscFunctionReturn(0);
5173 }
5174 
5175 /******************************** FEM Support **********************************/
5176 
5177 /*
5178  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5179  representing a line in the section.
5180 */
5181 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
5182 {
5183   PetscFunctionBeginHot;
5184   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5185   if (line < 0) {
5186     *k = 0;
5187     *Nc = 0;
5188   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
5189     *k = 1;
5190   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
5191     /* An order k SEM disc has k-1 dofs on an edge */
5192     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5193     *k = *k / *Nc + 1;
5194   }
5195   PetscFunctionReturn(0);
5196 }
5197 
5198 /*@
5199 
5200   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5201   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5202   section provided (or the section of the DM).
5203 
5204   Input Parameters:
5205 + dm      - The DM
5206 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5207 - section - The PetscSection to reorder, or NULL for the default section
5208 
5209   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5210   degree of the basis.
5211 
5212   Example:
5213   A typical interpolated single-quad mesh might order points as
5214 .vb
5215   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5216 
5217   v4 -- e6 -- v3
5218   |           |
5219   e7    c0    e8
5220   |           |
5221   v1 -- e5 -- v2
5222 .ve
5223 
5224   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5225   dofs in the order of points, e.g.,
5226 .vb
5227     c0 -> [0,1,2,3]
5228     v1 -> [4]
5229     ...
5230     e5 -> [8, 9]
5231 .ve
5232 
5233   which corresponds to the dofs
5234 .vb
5235     6   10  11  7
5236     13  2   3   15
5237     12  0   1   14
5238     4   8   9   5
5239 .ve
5240 
5241   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5242 .vb
5243   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5244 .ve
5245 
5246   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5247 .vb
5248    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5249 .ve
5250 
5251   Level: developer
5252 
5253 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5254 @*/
5255 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5256 {
5257   DMLabel        label;
5258   PetscInt       dim, depth = -1, eStart = -1, Nf;
5259   PetscBool      vertexchart;
5260 
5261   PetscFunctionBegin;
5262   PetscCall(DMGetDimension(dm, &dim));
5263   if (dim < 1) PetscFunctionReturn(0);
5264   if (point < 0) {
5265     PetscInt sStart,sEnd;
5266 
5267     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5268     point = sEnd-sStart ? sStart : point;
5269   }
5270   PetscCall(DMPlexGetDepthLabel(dm, &label));
5271   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5272   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5273   if (depth == 1) {eStart = point;}
5274   else if  (depth == dim) {
5275     const PetscInt *cone;
5276 
5277     PetscCall(DMPlexGetCone(dm, point, &cone));
5278     if (dim == 2) eStart = cone[0];
5279     else if (dim == 3) {
5280       const PetscInt *cone2;
5281       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5282       eStart = cone2[0];
5283     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " of depth %" PetscInt_FMT " cannot be used to bootstrap spectral ordering for dim %" PetscInt_FMT, point, depth, dim);
5284   } else PetscCheck(depth < 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " of depth %" PetscInt_FMT " cannot be used to bootstrap spectral ordering for dim %" PetscInt_FMT, point, depth, dim);
5285   {                             /* Determine whether the chart covers all points or just vertices. */
5286     PetscInt pStart,pEnd,cStart,cEnd;
5287     PetscCall(DMPlexGetDepthStratum(dm,0,&pStart,&pEnd));
5288     PetscCall(PetscSectionGetChart(section,&cStart,&cEnd));
5289     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5290     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5291     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5292   }
5293   PetscCall(PetscSectionGetNumFields(section, &Nf));
5294   for (PetscInt d=1; d<=dim; d++) {
5295     PetscInt k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5296     PetscInt *perm;
5297 
5298     for (f = 0; f < Nf; ++f) {
5299       PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5300       size += PetscPowInt(k+1, d)*Nc;
5301     }
5302     PetscCall(PetscMalloc1(size, &perm));
5303     for (f = 0; f < Nf; ++f) {
5304       switch (d) {
5305       case 1:
5306         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5307         /*
5308          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5309          We want              [ vtx0; edge of length k-1; vtx1 ]
5310          */
5311         for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
5312         for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
5313         for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
5314         foffset = offset;
5315         break;
5316       case 2:
5317         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5318         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5319         /* The SEM order is
5320 
5321          v_lb, {e_b}, v_rb,
5322          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5323          v_lt, reverse {e_t}, v_rt
5324          */
5325         {
5326           const PetscInt of   = 0;
5327           const PetscInt oeb  = of   + PetscSqr(k-1);
5328           const PetscInt oer  = oeb  + (k-1);
5329           const PetscInt oet  = oer  + (k-1);
5330           const PetscInt oel  = oet  + (k-1);
5331           const PetscInt ovlb = oel  + (k-1);
5332           const PetscInt ovrb = ovlb + 1;
5333           const PetscInt ovrt = ovrb + 1;
5334           const PetscInt ovlt = ovrt + 1;
5335           PetscInt       o;
5336 
5337           /* bottom */
5338           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
5339           for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5340           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
5341           /* middle */
5342           for (i = 0; i < k-1; ++i) {
5343             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
5344             for (o = of+(k-1)*i; o < of+(k-1)*(i+1); ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5345             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
5346           }
5347           /* top */
5348           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
5349           for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5350           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
5351           foffset = offset;
5352         }
5353         break;
5354       case 3:
5355         /* The original hex closure is
5356 
5357          {c,
5358          f_b, f_t, f_f, f_b, f_r, f_l,
5359          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5360          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5361          */
5362         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5363         /* The SEM order is
5364          Bottom Slice
5365          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5366          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5367          v_blb, {e_bb}, v_brb,
5368 
5369          Middle Slice (j)
5370          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5371          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5372          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5373 
5374          Top Slice
5375          v_tlf, {e_tf}, v_trf,
5376          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5377          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5378          */
5379         {
5380           const PetscInt oc    = 0;
5381           const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
5382           const PetscInt oft   = ofb   + PetscSqr(k-1);
5383           const PetscInt off   = oft   + PetscSqr(k-1);
5384           const PetscInt ofk   = off   + PetscSqr(k-1);
5385           const PetscInt ofr   = ofk   + PetscSqr(k-1);
5386           const PetscInt ofl   = ofr   + PetscSqr(k-1);
5387           const PetscInt oebl  = ofl   + PetscSqr(k-1);
5388           const PetscInt oebb  = oebl  + (k-1);
5389           const PetscInt oebr  = oebb  + (k-1);
5390           const PetscInt oebf  = oebr  + (k-1);
5391           const PetscInt oetf  = oebf  + (k-1);
5392           const PetscInt oetr  = oetf  + (k-1);
5393           const PetscInt oetb  = oetr  + (k-1);
5394           const PetscInt oetl  = oetb  + (k-1);
5395           const PetscInt oerf  = oetl  + (k-1);
5396           const PetscInt oelf  = oerf  + (k-1);
5397           const PetscInt oelb  = oelf  + (k-1);
5398           const PetscInt oerb  = oelb  + (k-1);
5399           const PetscInt ovblf = oerb  + (k-1);
5400           const PetscInt ovblb = ovblf + 1;
5401           const PetscInt ovbrb = ovblb + 1;
5402           const PetscInt ovbrf = ovbrb + 1;
5403           const PetscInt ovtlf = ovbrf + 1;
5404           const PetscInt ovtrf = ovtlf + 1;
5405           const PetscInt ovtrb = ovtrf + 1;
5406           const PetscInt ovtlb = ovtrb + 1;
5407           PetscInt       o, n;
5408 
5409           /* Bottom Slice */
5410           /*   bottom */
5411           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
5412           for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5413           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
5414           /*   middle */
5415           for (i = 0; i < k-1; ++i) {
5416             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
5417             for (n = 0; n < k-1; ++n) {o = ofb+n*(k-1)+i; for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;}
5418             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
5419           }
5420           /*   top */
5421           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
5422           for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5423           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
5424 
5425           /* Middle Slice */
5426           for (j = 0; j < k-1; ++j) {
5427             /*   bottom */
5428             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
5429             for (o = off+j*(k-1); o < off+(j+1)*(k-1); ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5430             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
5431             /*   middle */
5432             for (i = 0; i < k-1; ++i) {
5433               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
5434               for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc+(j*(k-1)+i)*(k-1)+n)*Nc + c + foffset;
5435               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
5436             }
5437             /*   top */
5438             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
5439             for (o = ofk+j*(k-1)+(k-2); o >= ofk+j*(k-1); --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5440             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
5441           }
5442 
5443           /* Top Slice */
5444           /*   bottom */
5445           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
5446           for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5447           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
5448           /*   middle */
5449           for (i = 0; i < k-1; ++i) {
5450             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
5451             for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
5452             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
5453           }
5454           /*   top */
5455           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
5456           for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5457           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
5458 
5459           foffset = offset;
5460         }
5461         break;
5462       default: SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5463       }
5464     }
5465     PetscCheck(offset == size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5466     /* Check permutation */
5467     {
5468       PetscInt *check;
5469 
5470       PetscCall(PetscMalloc1(size, &check));
5471       for (i = 0; i < size; ++i) {
5472         check[i] = -1;
5473         PetscCheck(perm[i] >= 0 && perm[i] < size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5474       }
5475       for (i = 0; i < size; ++i) check[perm[i]] = i;
5476       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5477       PetscCall(PetscFree(check));
5478     }
5479     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size, PETSC_OWN_POINTER, perm));
5480     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5481       PetscInt *loc_perm;
5482       PetscCall(PetscMalloc1(size*2, &loc_perm));
5483       for (PetscInt i=0; i<size; i++) {
5484         loc_perm[i] = perm[i];
5485         loc_perm[size+i] = size + perm[i];
5486       }
5487       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size*2, PETSC_OWN_POINTER, loc_perm));
5488     }
5489   }
5490   PetscFunctionReturn(0);
5491 }
5492 
5493 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5494 {
5495   PetscDS        prob;
5496   PetscInt       depth, Nf, h;
5497   DMLabel        label;
5498 
5499   PetscFunctionBeginHot;
5500   PetscCall(DMGetDS(dm, &prob));
5501   Nf      = prob->Nf;
5502   label   = dm->depthLabel;
5503   *dspace = NULL;
5504   if (field < Nf) {
5505     PetscObject disc = prob->disc[field];
5506 
5507     if (disc->classid == PETSCFE_CLASSID) {
5508       PetscDualSpace dsp;
5509 
5510       PetscCall(PetscFEGetDualSpace((PetscFE)disc,&dsp));
5511       PetscCall(DMLabelGetNumValues(label,&depth));
5512       PetscCall(DMLabelGetValue(label,point,&h));
5513       h    = depth - 1 - h;
5514       if (h) {
5515         PetscCall(PetscDualSpaceGetHeightSubspace(dsp,h,dspace));
5516       } else {
5517         *dspace = dsp;
5518       }
5519     }
5520   }
5521   PetscFunctionReturn(0);
5522 }
5523 
5524 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5525 {
5526   PetscScalar    *array;
5527   const PetscScalar *vArray;
5528   const PetscInt *cone, *coneO;
5529   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
5530 
5531   PetscFunctionBeginHot;
5532   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5533   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5534   PetscCall(DMPlexGetCone(dm, point, &cone));
5535   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5536   if (!values || !*values) {
5537     if ((point >= pStart) && (point < pEnd)) {
5538       PetscInt dof;
5539 
5540       PetscCall(PetscSectionGetDof(section, point, &dof));
5541       size += dof;
5542     }
5543     for (p = 0; p < numPoints; ++p) {
5544       const PetscInt cp = cone[p];
5545       PetscInt       dof;
5546 
5547       if ((cp < pStart) || (cp >= pEnd)) continue;
5548       PetscCall(PetscSectionGetDof(section, cp, &dof));
5549       size += dof;
5550     }
5551     if (!values) {
5552       if (csize) *csize = size;
5553       PetscFunctionReturn(0);
5554     }
5555     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5556   } else {
5557     array = *values;
5558   }
5559   size = 0;
5560   PetscCall(VecGetArrayRead(v, &vArray));
5561   if ((point >= pStart) && (point < pEnd)) {
5562     PetscInt     dof, off, d;
5563     const PetscScalar *varr;
5564 
5565     PetscCall(PetscSectionGetDof(section, point, &dof));
5566     PetscCall(PetscSectionGetOffset(section, point, &off));
5567     varr = &vArray[off];
5568     for (d = 0; d < dof; ++d, ++offset) {
5569       array[offset] = varr[d];
5570     }
5571     size += dof;
5572   }
5573   for (p = 0; p < numPoints; ++p) {
5574     const PetscInt cp = cone[p];
5575     PetscInt       o  = coneO[p];
5576     PetscInt       dof, off, d;
5577     const PetscScalar *varr;
5578 
5579     if ((cp < pStart) || (cp >= pEnd)) continue;
5580     PetscCall(PetscSectionGetDof(section, cp, &dof));
5581     PetscCall(PetscSectionGetOffset(section, cp, &off));
5582     varr = &vArray[off];
5583     if (o >= 0) {
5584       for (d = 0; d < dof; ++d, ++offset) {
5585         array[offset] = varr[d];
5586       }
5587     } else {
5588       for (d = dof-1; d >= 0; --d, ++offset) {
5589         array[offset] = varr[d];
5590       }
5591     }
5592     size += dof;
5593   }
5594   PetscCall(VecRestoreArrayRead(v, &vArray));
5595   if (!*values) {
5596     if (csize) *csize = size;
5597     *values = array;
5598   } else {
5599     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5600     *csize = size;
5601   }
5602   PetscFunctionReturn(0);
5603 }
5604 
5605 /* Compress out points not in the section */
5606 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5607 {
5608   const PetscInt np = *numPoints;
5609   PetscInt       pStart, pEnd, p, q;
5610 
5611   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5612   for (p = 0, q = 0; p < np; ++p) {
5613     const PetscInt r = points[p*2];
5614     if ((r >= pStart) && (r < pEnd)) {
5615       points[q*2]   = r;
5616       points[q*2+1] = points[p*2+1];
5617       ++q;
5618     }
5619   }
5620   *numPoints = q;
5621   return 0;
5622 }
5623 
5624 /* Compressed closure does not apply closure permutation */
5625 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5626 {
5627   const PetscInt *cla = NULL;
5628   PetscInt       np, *pts = NULL;
5629 
5630   PetscFunctionBeginHot;
5631   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints));
5632   if (*clPoints) {
5633     PetscInt dof, off;
5634 
5635     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5636     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5637     PetscCall(ISGetIndices(*clPoints, &cla));
5638     np   = dof/2;
5639     pts  = (PetscInt *) &cla[off];
5640   } else {
5641     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5642     PetscCall(CompressPoints_Private(section, &np, pts));
5643   }
5644   *numPoints = np;
5645   *points    = pts;
5646   *clp       = cla;
5647   PetscFunctionReturn(0);
5648 }
5649 
5650 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5651 {
5652   PetscFunctionBeginHot;
5653   if (!*clPoints) {
5654     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5655   } else {
5656     PetscCall(ISRestoreIndices(*clPoints, clp));
5657   }
5658   *numPoints = 0;
5659   *points    = NULL;
5660   *clSec     = NULL;
5661   *clPoints  = NULL;
5662   *clp       = NULL;
5663   PetscFunctionReturn(0);
5664 }
5665 
5666 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5667 {
5668   PetscInt          offset = 0, p;
5669   const PetscInt    **perms = NULL;
5670   const PetscScalar **flips = NULL;
5671 
5672   PetscFunctionBeginHot;
5673   *size = 0;
5674   PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
5675   for (p = 0; p < numPoints; p++) {
5676     const PetscInt    point = points[2*p];
5677     const PetscInt    *perm = perms ? perms[p] : NULL;
5678     const PetscScalar *flip = flips ? flips[p] : NULL;
5679     PetscInt          dof, off, d;
5680     const PetscScalar *varr;
5681 
5682     PetscCall(PetscSectionGetDof(section, point, &dof));
5683     PetscCall(PetscSectionGetOffset(section, point, &off));
5684     varr = &vArray[off];
5685     if (clperm) {
5686       if (perm) {
5687         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5688       } else {
5689         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5690       }
5691       if (flip) {
5692         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5693       }
5694     } else {
5695       if (perm) {
5696         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5697       } else {
5698         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5699       }
5700       if (flip) {
5701         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5702       }
5703     }
5704     offset += dof;
5705   }
5706   PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
5707   *size = offset;
5708   PetscFunctionReturn(0);
5709 }
5710 
5711 static inline PetscErrorCode DMPlexVecGetClosure_Fields_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], PetscInt numFields, const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5712 {
5713   PetscInt          offset = 0, f;
5714 
5715   PetscFunctionBeginHot;
5716   *size = 0;
5717   for (f = 0; f < numFields; ++f) {
5718     PetscInt          p;
5719     const PetscInt    **perms = NULL;
5720     const PetscScalar **flips = NULL;
5721 
5722     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5723     for (p = 0; p < numPoints; p++) {
5724       const PetscInt    point = points[2*p];
5725       PetscInt          fdof, foff, b;
5726       const PetscScalar *varr;
5727       const PetscInt    *perm = perms ? perms[p] : NULL;
5728       const PetscScalar *flip = flips ? flips[p] : NULL;
5729 
5730       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5731       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5732       varr = &vArray[foff];
5733       if (clperm) {
5734         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5735         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5736         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5737       } else {
5738         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5739         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5740         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5741       }
5742       offset += fdof;
5743     }
5744     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5745   }
5746   *size = offset;
5747   PetscFunctionReturn(0);
5748 }
5749 
5750 /*@C
5751   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5752 
5753   Not collective
5754 
5755   Input Parameters:
5756 + dm - The DM
5757 . section - The section describing the layout in v, or NULL to use the default section
5758 . v - The local vector
5759 - point - The point in the DM
5760 
5761   Input/Output Parameters:
5762 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5763 - values - An array to use for the values, or NULL to have it allocated automatically;
5764            if the user provided NULL, it is a borrowed array and should not be freed
5765 
5766 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5767 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5768 $ assembly function, and a user may already have allocated storage for this operation.
5769 $
5770 $ A typical use could be
5771 $
5772 $  values = NULL;
5773 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5774 $  for (cl = 0; cl < clSize; ++cl) {
5775 $    <Compute on closure>
5776 $  }
5777 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5778 $
5779 $ or
5780 $
5781 $  PetscMalloc1(clMaxSize, &values);
5782 $  for (p = pStart; p < pEnd; ++p) {
5783 $    clSize = clMaxSize;
5784 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5785 $    for (cl = 0; cl < clSize; ++cl) {
5786 $      <Compute on closure>
5787 $    }
5788 $  }
5789 $  PetscFree(values);
5790 
5791   Fortran Notes:
5792   Since it returns an array, this routine is only available in Fortran 90, and you must
5793   include petsc.h90 in your code.
5794 
5795   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5796 
5797   Level: intermediate
5798 
5799 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5800 @*/
5801 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5802 {
5803   PetscSection       clSection;
5804   IS                 clPoints;
5805   PetscInt          *points = NULL;
5806   const PetscInt    *clp, *perm;
5807   PetscInt           depth, numFields, numPoints, asize;
5808 
5809   PetscFunctionBeginHot;
5810   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5811   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5812   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5813   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5814   PetscCall(DMPlexGetDepth(dm, &depth));
5815   PetscCall(PetscSectionGetNumFields(section, &numFields));
5816   if (depth == 1 && numFields < 2) {
5817     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5818     PetscFunctionReturn(0);
5819   }
5820   /* Get points */
5821   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5822   /* Get sizes */
5823   asize = 0;
5824   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5825     PetscInt dof;
5826     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5827     asize += dof;
5828   }
5829   if (values) {
5830     const PetscScalar *vArray;
5831     PetscInt          size;
5832 
5833     if (*values) {
5834       PetscCheck(*csize >= asize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Provided array size %" PetscInt_FMT " not sufficient to hold closure size %" PetscInt_FMT, *csize, asize);
5835     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5836     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm));
5837     PetscCall(VecGetArrayRead(v, &vArray));
5838     /* Get values */
5839     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5840     else               PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5841     PetscCheck(asize == size,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5842     /* Cleanup array */
5843     PetscCall(VecRestoreArrayRead(v, &vArray));
5844   }
5845   if (csize) *csize = asize;
5846   /* Cleanup points */
5847   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5848   PetscFunctionReturn(0);
5849 }
5850 
5851 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5852 {
5853   DMLabel            depthLabel;
5854   PetscSection       clSection;
5855   IS                 clPoints;
5856   PetscScalar       *array;
5857   const PetscScalar *vArray;
5858   PetscInt          *points = NULL;
5859   const PetscInt    *clp, *perm = NULL;
5860   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5861 
5862   PetscFunctionBeginHot;
5863   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5864   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5865   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5866   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5867   PetscCall(DMPlexGetDepth(dm, &mdepth));
5868   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5869   PetscCall(PetscSectionGetNumFields(section, &numFields));
5870   if (mdepth == 1 && numFields < 2) {
5871     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5872     PetscFunctionReturn(0);
5873   }
5874   /* Get points */
5875   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5876   for (clsize=0,p=0; p<Np; p++) {
5877     PetscInt dof;
5878     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
5879     clsize += dof;
5880   }
5881   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm));
5882   /* Filter points */
5883   for (p = 0; p < numPoints*2; p += 2) {
5884     PetscInt dep;
5885 
5886     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5887     if (dep != depth) continue;
5888     points[Np*2+0] = points[p];
5889     points[Np*2+1] = points[p+1];
5890     ++Np;
5891   }
5892   /* Get array */
5893   if (!values || !*values) {
5894     PetscInt asize = 0, dof;
5895 
5896     for (p = 0; p < Np*2; p += 2) {
5897       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5898       asize += dof;
5899     }
5900     if (!values) {
5901       PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5902       if (csize) *csize = asize;
5903       PetscFunctionReturn(0);
5904     }
5905     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5906   } else {
5907     array = *values;
5908   }
5909   PetscCall(VecGetArrayRead(v, &vArray));
5910   /* Get values */
5911   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5912   else               PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5913   /* Cleanup points */
5914   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5915   /* Cleanup array */
5916   PetscCall(VecRestoreArrayRead(v, &vArray));
5917   if (!*values) {
5918     if (csize) *csize = size;
5919     *values = array;
5920   } else {
5921     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5922     *csize = size;
5923   }
5924   PetscFunctionReturn(0);
5925 }
5926 
5927 /*@C
5928   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5929 
5930   Not collective
5931 
5932   Input Parameters:
5933 + dm - The DM
5934 . section - The section describing the layout in v, or NULL to use the default section
5935 . v - The local vector
5936 . point - The point in the DM
5937 . csize - The number of values in the closure, or NULL
5938 - values - The array of values, which is a borrowed array and should not be freed
5939 
5940   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5941 
5942   Fortran Notes:
5943   Since it returns an array, this routine is only available in Fortran 90, and you must
5944   include petsc.h90 in your code.
5945 
5946   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5947 
5948   Level: intermediate
5949 
5950 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5951 @*/
5952 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5953 {
5954   PetscInt       size = 0;
5955 
5956   PetscFunctionBegin;
5957   /* Should work without recalculating size */
5958   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values));
5959   *values = NULL;
5960   PetscFunctionReturn(0);
5961 }
5962 
5963 static inline void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5964 static inline void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5965 
5966 static inline PetscErrorCode updatePoint_private(PetscSection section, PetscInt point, PetscInt dof, void (*fuse)(PetscScalar*, PetscScalar), PetscBool setBC, const PetscInt perm[], const PetscScalar flip[], const PetscInt clperm[], const PetscScalar values[], PetscInt offset, PetscScalar array[])
5967 {
5968   PetscInt        cdof;   /* The number of constraints on this point */
5969   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5970   PetscScalar    *a;
5971   PetscInt        off, cind = 0, k;
5972 
5973   PetscFunctionBegin;
5974   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5975   PetscCall(PetscSectionGetOffset(section, point, &off));
5976   a    = &array[off];
5977   if (!cdof || setBC) {
5978     if (clperm) {
5979       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5980       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5981     } else {
5982       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5983       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5984     }
5985   } else {
5986     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5987     if (clperm) {
5988       if (perm) {for (k = 0; k < dof; ++k) {
5989           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5990           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5991         }
5992       } else {
5993         for (k = 0; k < dof; ++k) {
5994           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5995           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5996         }
5997       }
5998     } else {
5999       if (perm) {
6000         for (k = 0; k < dof; ++k) {
6001           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6002           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6003         }
6004       } else {
6005         for (k = 0; k < dof; ++k) {
6006           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6007           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6008         }
6009       }
6010     }
6011   }
6012   PetscFunctionReturn(0);
6013 }
6014 
6015 static inline PetscErrorCode updatePointBC_private(PetscSection section, PetscInt point, PetscInt dof, void (*fuse)(PetscScalar*, PetscScalar), const PetscInt perm[], const PetscScalar flip[], const PetscInt clperm[], const PetscScalar values[], PetscInt offset, PetscScalar array[])
6016 {
6017   PetscInt        cdof;   /* The number of constraints on this point */
6018   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6019   PetscScalar    *a;
6020   PetscInt        off, cind = 0, k;
6021 
6022   PetscFunctionBegin;
6023   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6024   PetscCall(PetscSectionGetOffset(section, point, &off));
6025   a    = &array[off];
6026   if (cdof) {
6027     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6028     if (clperm) {
6029       if (perm) {
6030         for (k = 0; k < dof; ++k) {
6031           if ((cind < cdof) && (k == cdofs[cind])) {
6032             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
6033             cind++;
6034           }
6035         }
6036       } else {
6037         for (k = 0; k < dof; ++k) {
6038           if ((cind < cdof) && (k == cdofs[cind])) {
6039             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
6040             cind++;
6041           }
6042         }
6043       }
6044     } else {
6045       if (perm) {
6046         for (k = 0; k < dof; ++k) {
6047           if ((cind < cdof) && (k == cdofs[cind])) {
6048             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6049             cind++;
6050           }
6051         }
6052       } else {
6053         for (k = 0; k < dof; ++k) {
6054           if ((cind < cdof) && (k == cdofs[cind])) {
6055             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6056             cind++;
6057           }
6058         }
6059       }
6060     }
6061   }
6062   PetscFunctionReturn(0);
6063 }
6064 
6065 static inline PetscErrorCode updatePointFields_private(PetscSection section, PetscInt point, const PetscInt *perm, const PetscScalar *flip, PetscInt f, void (*fuse)(PetscScalar*, PetscScalar), PetscBool setBC, const PetscInt clperm[], const PetscScalar values[], PetscInt *offset, PetscScalar array[])
6066 {
6067   PetscScalar    *a;
6068   PetscInt        fdof, foff, fcdof, foffset = *offset;
6069   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6070   PetscInt        cind = 0, b;
6071 
6072   PetscFunctionBegin;
6073   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6074   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6075   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6076   a    = &array[foff];
6077   if (!fcdof || setBC) {
6078     if (clperm) {
6079       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
6080       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
6081     } else {
6082       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
6083       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
6084     }
6085   } else {
6086     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6087     if (clperm) {
6088       if (perm) {
6089         for (b = 0; b < fdof; b++) {
6090           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6091           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6092         }
6093       } else {
6094         for (b = 0; b < fdof; b++) {
6095           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6096           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6097         }
6098       }
6099     } else {
6100       if (perm) {
6101         for (b = 0; b < fdof; b++) {
6102           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6103           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6104         }
6105       } else {
6106         for (b = 0; b < fdof; b++) {
6107           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6108           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6109         }
6110       }
6111     }
6112   }
6113   *offset += fdof;
6114   PetscFunctionReturn(0);
6115 }
6116 
6117 static inline PetscErrorCode updatePointFieldsBC_private(PetscSection section, PetscInt point, const PetscInt perm[], const PetscScalar flip[], PetscInt f, PetscInt Ncc, const PetscInt comps[], void (*fuse)(PetscScalar*, PetscScalar), const PetscInt clperm[], const PetscScalar values[], PetscInt *offset, PetscScalar array[])
6118 {
6119   PetscScalar    *a;
6120   PetscInt        fdof, foff, fcdof, foffset = *offset;
6121   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6122   PetscInt        Nc, cind = 0, ncind = 0, b;
6123   PetscBool       ncSet, fcSet;
6124 
6125   PetscFunctionBegin;
6126   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6127   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6128   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6129   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6130   a    = &array[foff];
6131   if (fcdof) {
6132     /* We just override fcdof and fcdofs with Ncc and comps */
6133     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6134     if (clperm) {
6135       if (perm) {
6136         if (comps) {
6137           for (b = 0; b < fdof; b++) {
6138             ncSet = fcSet = PETSC_FALSE;
6139             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6140             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6141             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
6142           }
6143         } else {
6144           for (b = 0; b < fdof; b++) {
6145             if ((cind < fcdof) && (b == fcdofs[cind])) {
6146               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6147               ++cind;
6148             }
6149           }
6150         }
6151       } else {
6152         if (comps) {
6153           for (b = 0; b < fdof; b++) {
6154             ncSet = fcSet = PETSC_FALSE;
6155             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6156             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6157             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
6158           }
6159         } else {
6160           for (b = 0; b < fdof; b++) {
6161             if ((cind < fcdof) && (b == fcdofs[cind])) {
6162               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6163               ++cind;
6164             }
6165           }
6166         }
6167       }
6168     } else {
6169       if (perm) {
6170         if (comps) {
6171           for (b = 0; b < fdof; b++) {
6172             ncSet = fcSet = PETSC_FALSE;
6173             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6174             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6175             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
6176           }
6177         } else {
6178           for (b = 0; b < fdof; b++) {
6179             if ((cind < fcdof) && (b == fcdofs[cind])) {
6180               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6181               ++cind;
6182             }
6183           }
6184         }
6185       } else {
6186         if (comps) {
6187           for (b = 0; b < fdof; b++) {
6188             ncSet = fcSet = PETSC_FALSE;
6189             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6190             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6191             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
6192           }
6193         } else {
6194           for (b = 0; b < fdof; b++) {
6195             if ((cind < fcdof) && (b == fcdofs[cind])) {
6196               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6197               ++cind;
6198             }
6199           }
6200         }
6201       }
6202     }
6203   }
6204   *offset += fdof;
6205   PetscFunctionReturn(0);
6206 }
6207 
6208 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6209 {
6210   PetscScalar    *array;
6211   const PetscInt *cone, *coneO;
6212   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6213 
6214   PetscFunctionBeginHot;
6215   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6216   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6217   PetscCall(DMPlexGetCone(dm, point, &cone));
6218   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6219   PetscCall(VecGetArray(v, &array));
6220   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6221     const PetscInt cp = !p ? point : cone[p-1];
6222     const PetscInt o  = !p ? 0     : coneO[p-1];
6223 
6224     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
6225     PetscCall(PetscSectionGetDof(section, cp, &dof));
6226     /* ADD_VALUES */
6227     {
6228       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6229       PetscScalar    *a;
6230       PetscInt        cdof, coff, cind = 0, k;
6231 
6232       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6233       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6234       a    = &array[coff];
6235       if (!cdof) {
6236         if (o >= 0) {
6237           for (k = 0; k < dof; ++k) {
6238             a[k] += values[off+k];
6239           }
6240         } else {
6241           for (k = 0; k < dof; ++k) {
6242             a[k] += values[off+dof-k-1];
6243           }
6244         }
6245       } else {
6246         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6247         if (o >= 0) {
6248           for (k = 0; k < dof; ++k) {
6249             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6250             a[k] += values[off+k];
6251           }
6252         } else {
6253           for (k = 0; k < dof; ++k) {
6254             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6255             a[k] += values[off+dof-k-1];
6256           }
6257         }
6258       }
6259     }
6260   }
6261   PetscCall(VecRestoreArray(v, &array));
6262   PetscFunctionReturn(0);
6263 }
6264 
6265 /*@C
6266   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6267 
6268   Not collective
6269 
6270   Input Parameters:
6271 + dm - The DM
6272 . section - The section describing the layout in v, or NULL to use the default section
6273 . v - The local vector
6274 . point - The point in the DM
6275 . values - The array of values
6276 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6277          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6278 
6279   Fortran Notes:
6280   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6281 
6282   Level: intermediate
6283 
6284 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6285 @*/
6286 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6287 {
6288   PetscSection    clSection;
6289   IS              clPoints;
6290   PetscScalar    *array;
6291   PetscInt       *points = NULL;
6292   const PetscInt *clp, *clperm = NULL;
6293   PetscInt        depth, numFields, numPoints, p, clsize;
6294 
6295   PetscFunctionBeginHot;
6296   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6297   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6298   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6299   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6300   PetscCall(DMPlexGetDepth(dm, &depth));
6301   PetscCall(PetscSectionGetNumFields(section, &numFields));
6302   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6303     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6304     PetscFunctionReturn(0);
6305   }
6306   /* Get points */
6307   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6308   for (clsize=0,p=0; p<numPoints; p++) {
6309     PetscInt dof;
6310     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
6311     clsize += dof;
6312   }
6313   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
6314   /* Get array */
6315   PetscCall(VecGetArray(v, &array));
6316   /* Get values */
6317   if (numFields > 0) {
6318     PetscInt offset = 0, f;
6319     for (f = 0; f < numFields; ++f) {
6320       const PetscInt    **perms = NULL;
6321       const PetscScalar **flips = NULL;
6322 
6323       PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6324       switch (mode) {
6325       case INSERT_VALUES:
6326         for (p = 0; p < numPoints; p++) {
6327           const PetscInt    point = points[2*p];
6328           const PetscInt    *perm = perms ? perms[p] : NULL;
6329           const PetscScalar *flip = flips ? flips[p] : NULL;
6330           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6331         } break;
6332       case INSERT_ALL_VALUES:
6333         for (p = 0; p < numPoints; p++) {
6334           const PetscInt    point = points[2*p];
6335           const PetscInt    *perm = perms ? perms[p] : NULL;
6336           const PetscScalar *flip = flips ? flips[p] : NULL;
6337           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6338         } break;
6339       case INSERT_BC_VALUES:
6340         for (p = 0; p < numPoints; p++) {
6341           const PetscInt    point = points[2*p];
6342           const PetscInt    *perm = perms ? perms[p] : NULL;
6343           const PetscScalar *flip = flips ? flips[p] : NULL;
6344           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6345         } break;
6346       case ADD_VALUES:
6347         for (p = 0; p < numPoints; p++) {
6348           const PetscInt    point = points[2*p];
6349           const PetscInt    *perm = perms ? perms[p] : NULL;
6350           const PetscScalar *flip = flips ? flips[p] : NULL;
6351           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6352         } break;
6353       case ADD_ALL_VALUES:
6354         for (p = 0; p < numPoints; p++) {
6355           const PetscInt    point = points[2*p];
6356           const PetscInt    *perm = perms ? perms[p] : NULL;
6357           const PetscScalar *flip = flips ? flips[p] : NULL;
6358           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6359         } break;
6360       case ADD_BC_VALUES:
6361         for (p = 0; p < numPoints; p++) {
6362           const PetscInt    point = points[2*p];
6363           const PetscInt    *perm = perms ? perms[p] : NULL;
6364           const PetscScalar *flip = flips ? flips[p] : NULL;
6365           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6366         } break;
6367       default:
6368         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6369       }
6370       PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6371     }
6372   } else {
6373     PetscInt dof, off;
6374     const PetscInt    **perms = NULL;
6375     const PetscScalar **flips = NULL;
6376 
6377     PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
6378     switch (mode) {
6379     case INSERT_VALUES:
6380       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6381         const PetscInt    point = points[2*p];
6382         const PetscInt    *perm = perms ? perms[p] : NULL;
6383         const PetscScalar *flip = flips ? flips[p] : NULL;
6384         PetscCall(PetscSectionGetDof(section, point, &dof));
6385         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6386       } break;
6387     case INSERT_ALL_VALUES:
6388       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6389         const PetscInt    point = points[2*p];
6390         const PetscInt    *perm = perms ? perms[p] : NULL;
6391         const PetscScalar *flip = flips ? flips[p] : NULL;
6392         PetscCall(PetscSectionGetDof(section, point, &dof));
6393         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
6394       } break;
6395     case INSERT_BC_VALUES:
6396       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6397         const PetscInt    point = points[2*p];
6398         const PetscInt    *perm = perms ? perms[p] : NULL;
6399         const PetscScalar *flip = flips ? flips[p] : NULL;
6400         PetscCall(PetscSectionGetDof(section, point, &dof));
6401         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
6402       } break;
6403     case ADD_VALUES:
6404       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6405         const PetscInt    point = points[2*p];
6406         const PetscInt    *perm = perms ? perms[p] : NULL;
6407         const PetscScalar *flip = flips ? flips[p] : NULL;
6408         PetscCall(PetscSectionGetDof(section, point, &dof));
6409         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
6410       } break;
6411     case ADD_ALL_VALUES:
6412       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6413         const PetscInt    point = points[2*p];
6414         const PetscInt    *perm = perms ? perms[p] : NULL;
6415         const PetscScalar *flip = flips ? flips[p] : NULL;
6416         PetscCall(PetscSectionGetDof(section, point, &dof));
6417         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
6418       } break;
6419     case ADD_BC_VALUES:
6420       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6421         const PetscInt    point = points[2*p];
6422         const PetscInt    *perm = perms ? perms[p] : NULL;
6423         const PetscScalar *flip = flips ? flips[p] : NULL;
6424         PetscCall(PetscSectionGetDof(section, point, &dof));
6425         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
6426       } break;
6427     default:
6428       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6429     }
6430     PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
6431   }
6432   /* Cleanup points */
6433   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6434   /* Cleanup array */
6435   PetscCall(VecRestoreArray(v, &array));
6436   PetscFunctionReturn(0);
6437 }
6438 
6439 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6440 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
6441 {
6442   PetscFunctionBegin;
6443   if (label) {
6444     PetscBool contains;
6445     PetscInt  fdof;
6446 
6447     PetscCall(DMLabelStratumHasPoint(label, labelId, point, &contains));
6448     if (!contains) {
6449       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6450       *offset += fdof;
6451       PetscFunctionReturn(1);
6452     }
6453   }
6454   PetscFunctionReturn(0);
6455 }
6456 
6457 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6458 PetscErrorCode DMPlexVecSetFieldClosure_Internal(DM dm, PetscSection section, Vec v, PetscBool fieldActive[], PetscInt point, PetscInt Ncc, const PetscInt comps[], DMLabel label, PetscInt labelId, const PetscScalar values[], InsertMode mode)
6459 {
6460   PetscSection    clSection;
6461   IS              clPoints;
6462   PetscScalar    *array;
6463   PetscInt       *points = NULL;
6464   const PetscInt *clp;
6465   PetscInt        numFields, numPoints, p;
6466   PetscInt        offset = 0, f;
6467 
6468   PetscFunctionBeginHot;
6469   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6470   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6471   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6472   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6473   PetscCall(PetscSectionGetNumFields(section, &numFields));
6474   /* Get points */
6475   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6476   /* Get array */
6477   PetscCall(VecGetArray(v, &array));
6478   /* Get values */
6479   for (f = 0; f < numFields; ++f) {
6480     const PetscInt    **perms = NULL;
6481     const PetscScalar **flips = NULL;
6482 
6483     if (!fieldActive[f]) {
6484       for (p = 0; p < numPoints*2; p += 2) {
6485         PetscInt fdof;
6486         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6487         offset += fdof;
6488       }
6489       continue;
6490     }
6491     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6492     switch (mode) {
6493     case INSERT_VALUES:
6494       for (p = 0; p < numPoints; p++) {
6495         const PetscInt    point = points[2*p];
6496         const PetscInt    *perm = perms ? perms[p] : NULL;
6497         const PetscScalar *flip = flips ? flips[p] : NULL;
6498         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6499         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6500       } break;
6501     case INSERT_ALL_VALUES:
6502       for (p = 0; p < numPoints; p++) {
6503         const PetscInt    point = points[2*p];
6504         const PetscInt    *perm = perms ? perms[p] : NULL;
6505         const PetscScalar *flip = flips ? flips[p] : NULL;
6506         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6507         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6508       } break;
6509     case INSERT_BC_VALUES:
6510       for (p = 0; p < numPoints; p++) {
6511         const PetscInt    point = points[2*p];
6512         const PetscInt    *perm = perms ? perms[p] : NULL;
6513         const PetscScalar *flip = flips ? flips[p] : NULL;
6514         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6515         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6516       } break;
6517     case ADD_VALUES:
6518       for (p = 0; p < numPoints; p++) {
6519         const PetscInt    point = points[2*p];
6520         const PetscInt    *perm = perms ? perms[p] : NULL;
6521         const PetscScalar *flip = flips ? flips[p] : NULL;
6522         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6523         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6524       } break;
6525     case ADD_ALL_VALUES:
6526       for (p = 0; p < numPoints; p++) {
6527         const PetscInt    point = points[2*p];
6528         const PetscInt    *perm = perms ? perms[p] : NULL;
6529         const PetscScalar *flip = flips ? flips[p] : NULL;
6530         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6531         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6532       } break;
6533     default:
6534       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6535     }
6536     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6537   }
6538   /* Cleanup points */
6539   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6540   /* Cleanup array */
6541   PetscCall(VecRestoreArray(v, &array));
6542   PetscFunctionReturn(0);
6543 }
6544 
6545 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6546 {
6547   PetscMPIInt    rank;
6548   PetscInt       i, j;
6549 
6550   PetscFunctionBegin;
6551   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6552   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6553   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6554   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6555   numCIndices = numCIndices ? numCIndices : numRIndices;
6556   if (!values) PetscFunctionReturn(0);
6557   for (i = 0; i < numRIndices; i++) {
6558     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6559     for (j = 0; j < numCIndices; j++) {
6560 #if defined(PETSC_USE_COMPLEX)
6561       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j])));
6562 #else
6563       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]));
6564 #endif
6565     }
6566     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6567   }
6568   PetscFunctionReturn(0);
6569 }
6570 
6571 /*
6572   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6573 
6574   Input Parameters:
6575 + section - The section for this data layout
6576 . islocal - Is the section (and thus indices being requested) local or global?
6577 . point   - The point contributing dofs with these indices
6578 . off     - The global offset of this point
6579 . loff    - The local offset of each field
6580 . setBC   - The flag determining whether to include indices of boundary values
6581 . perm    - A permutation of the dofs on this point, or NULL
6582 - indperm - A permutation of the entire indices array, or NULL
6583 
6584   Output Parameter:
6585 . indices - Indices for dofs on this point
6586 
6587   Level: developer
6588 
6589   Note: The indices could be local or global, depending on the value of 'off'.
6590 */
6591 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6592 {
6593   PetscInt        dof;   /* The number of unknowns on this point */
6594   PetscInt        cdof;  /* The number of constraints on this point */
6595   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6596   PetscInt        cind = 0, k;
6597 
6598   PetscFunctionBegin;
6599   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6600   PetscCall(PetscSectionGetDof(section, point, &dof));
6601   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6602   if (!cdof || setBC) {
6603     for (k = 0; k < dof; ++k) {
6604       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6605       const PetscInt ind    = indperm ? indperm[preind] : preind;
6606 
6607       indices[ind] = off + k;
6608     }
6609   } else {
6610     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6611     for (k = 0; k < dof; ++k) {
6612       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6613       const PetscInt ind    = indperm ? indperm[preind] : preind;
6614 
6615       if ((cind < cdof) && (k == cdofs[cind])) {
6616         /* Insert check for returning constrained indices */
6617         indices[ind] = -(off+k+1);
6618         ++cind;
6619       } else {
6620         indices[ind] = off + k - (islocal ? 0 : cind);
6621       }
6622     }
6623   }
6624   *loff += dof;
6625   PetscFunctionReturn(0);
6626 }
6627 
6628 /*
6629  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6630 
6631  Input Parameters:
6632 + section - a section (global or local)
6633 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6634 . point - point within section
6635 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6636 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6637 . setBC - identify constrained (boundary condition) points via involution.
6638 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6639 . permsoff - offset
6640 - indperm - index permutation
6641 
6642  Output Parameter:
6643 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6644 . indices - array to hold indices (as defined by section) of each dof associated with point
6645 
6646  Notes:
6647  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6648  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6649  in the local vector.
6650 
6651  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6652  significant).  It is invalid to call with a global section and setBC=true.
6653 
6654  Developer Note:
6655  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6656  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6657  offset could be obtained from the section instead of passing it explicitly as we do now.
6658 
6659  Example:
6660  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6661  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6662  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6663  The global vector does not store constrained dofs, so when this function returns global indices, say {110, -112, 111}, the value of -112 is an arbitrary flag that should not be interpreted beyond its sign.
6664 
6665  Level: developer
6666 */
6667 PetscErrorCode DMPlexGetIndicesPointFields_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt foffs[], PetscBool setBC, const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6668 {
6669   PetscInt       numFields, foff, f;
6670 
6671   PetscFunctionBegin;
6672   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6673   PetscCall(PetscSectionGetNumFields(section, &numFields));
6674   for (f = 0, foff = 0; f < numFields; ++f) {
6675     PetscInt        fdof, cfdof;
6676     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6677     PetscInt        cind = 0, b;
6678     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6679 
6680     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6681     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6682     if (!cfdof || setBC) {
6683       for (b = 0; b < fdof; ++b) {
6684         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6685         const PetscInt ind    = indperm ? indperm[preind] : preind;
6686 
6687         indices[ind] = off+foff+b;
6688       }
6689     } else {
6690       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6691       for (b = 0; b < fdof; ++b) {
6692         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6693         const PetscInt ind    = indperm ? indperm[preind] : preind;
6694 
6695         if ((cind < cfdof) && (b == fcdofs[cind])) {
6696           indices[ind] = -(off+foff+b+1);
6697           ++cind;
6698         } else {
6699           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6700         }
6701       }
6702     }
6703     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6704     foffs[f] += fdof;
6705   }
6706   PetscFunctionReturn(0);
6707 }
6708 
6709 /*
6710   This version believes the globalSection offsets for each field, rather than just the point offset
6711 
6712  . foffs - The offset into 'indices' for each field, since it is segregated by field
6713 
6714  Notes:
6715  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6716  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6717 */
6718 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6719 {
6720   PetscInt       numFields, foff, f;
6721 
6722   PetscFunctionBegin;
6723   PetscCall(PetscSectionGetNumFields(section, &numFields));
6724   for (f = 0; f < numFields; ++f) {
6725     PetscInt        fdof, cfdof;
6726     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6727     PetscInt        cind = 0, b;
6728     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6729 
6730     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6731     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6732     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6733     if (!cfdof) {
6734       for (b = 0; b < fdof; ++b) {
6735         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6736         const PetscInt ind    = indperm ? indperm[preind] : preind;
6737 
6738         indices[ind] = foff+b;
6739       }
6740     } else {
6741       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6742       for (b = 0; b < fdof; ++b) {
6743         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6744         const PetscInt ind    = indperm ? indperm[preind] : preind;
6745 
6746         if ((cind < cfdof) && (b == fcdofs[cind])) {
6747           indices[ind] = -(foff+b+1);
6748           ++cind;
6749         } else {
6750           indices[ind] = foff+b-cind;
6751         }
6752       }
6753     }
6754     foffs[f] += fdof;
6755   }
6756   PetscFunctionReturn(0);
6757 }
6758 
6759 PetscErrorCode DMPlexAnchorsModifyMat(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, const PetscScalar values[], PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscScalar *outValues[], PetscInt offsets[], PetscBool multiplyLeft)
6760 {
6761   Mat             cMat;
6762   PetscSection    aSec, cSec;
6763   IS              aIS;
6764   PetscInt        aStart = -1, aEnd = -1;
6765   const PetscInt  *anchors;
6766   PetscInt        numFields, f, p, q, newP = 0;
6767   PetscInt        newNumPoints = 0, newNumIndices = 0;
6768   PetscInt        *newPoints, *indices, *newIndices;
6769   PetscInt        maxAnchor, maxDof;
6770   PetscInt        newOffsets[32];
6771   PetscInt        *pointMatOffsets[32];
6772   PetscInt        *newPointOffsets[32];
6773   PetscScalar     *pointMat[32];
6774   PetscScalar     *newValues=NULL,*tmpValues;
6775   PetscBool       anyConstrained = PETSC_FALSE;
6776 
6777   PetscFunctionBegin;
6778   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6779   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6780   PetscCall(PetscSectionGetNumFields(section, &numFields));
6781 
6782   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
6783   /* if there are point-to-point constraints */
6784   if (aSec) {
6785     PetscCall(PetscArrayzero(newOffsets, 32));
6786     PetscCall(ISGetIndices(aIS,&anchors));
6787     PetscCall(PetscSectionGetChart(aSec,&aStart,&aEnd));
6788     /* figure out how many points are going to be in the new element matrix
6789      * (we allow double counting, because it's all just going to be summed
6790      * into the global matrix anyway) */
6791     for (p = 0; p < 2*numPoints; p+=2) {
6792       PetscInt b    = points[p];
6793       PetscInt bDof = 0, bSecDof;
6794 
6795       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6796       if (!bSecDof) {
6797         continue;
6798       }
6799       if (b >= aStart && b < aEnd) {
6800         PetscCall(PetscSectionGetDof(aSec,b,&bDof));
6801       }
6802       if (bDof) {
6803         /* this point is constrained */
6804         /* it is going to be replaced by its anchors */
6805         PetscInt bOff, q;
6806 
6807         anyConstrained = PETSC_TRUE;
6808         newNumPoints  += bDof;
6809         PetscCall(PetscSectionGetOffset(aSec,b,&bOff));
6810         for (q = 0; q < bDof; q++) {
6811           PetscInt a = anchors[bOff + q];
6812           PetscInt aDof;
6813 
6814           PetscCall(PetscSectionGetDof(section,a,&aDof));
6815           newNumIndices += aDof;
6816           for (f = 0; f < numFields; ++f) {
6817             PetscInt fDof;
6818 
6819             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6820             newOffsets[f+1] += fDof;
6821           }
6822         }
6823       }
6824       else {
6825         /* this point is not constrained */
6826         newNumPoints++;
6827         newNumIndices += bSecDof;
6828         for (f = 0; f < numFields; ++f) {
6829           PetscInt fDof;
6830 
6831           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6832           newOffsets[f+1] += fDof;
6833         }
6834       }
6835     }
6836   }
6837   if (!anyConstrained) {
6838     if (outNumPoints)  *outNumPoints  = 0;
6839     if (outNumIndices) *outNumIndices = 0;
6840     if (outPoints)     *outPoints     = NULL;
6841     if (outValues)     *outValues     = NULL;
6842     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6843     PetscFunctionReturn(0);
6844   }
6845 
6846   if (outNumPoints)  *outNumPoints  = newNumPoints;
6847   if (outNumIndices) *outNumIndices = newNumIndices;
6848 
6849   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6850 
6851   if (!outPoints && !outValues) {
6852     if (offsets) {
6853       for (f = 0; f <= numFields; f++) {
6854         offsets[f] = newOffsets[f];
6855       }
6856     }
6857     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6858     PetscFunctionReturn(0);
6859   }
6860 
6861   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6862 
6863   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6864 
6865   /* workspaces */
6866   if (numFields) {
6867     for (f = 0; f < numFields; f++) {
6868       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
6869       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
6870     }
6871   }
6872   else {
6873     PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
6874     PetscCall(DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]));
6875   }
6876 
6877   /* get workspaces for the point-to-point matrices */
6878   if (numFields) {
6879     PetscInt totalOffset, totalMatOffset;
6880 
6881     for (p = 0; p < numPoints; p++) {
6882       PetscInt b    = points[2*p];
6883       PetscInt bDof = 0, bSecDof;
6884 
6885       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6886       if (!bSecDof) {
6887         for (f = 0; f < numFields; f++) {
6888           newPointOffsets[f][p + 1] = 0;
6889           pointMatOffsets[f][p + 1] = 0;
6890         }
6891         continue;
6892       }
6893       if (b >= aStart && b < aEnd) {
6894         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6895       }
6896       if (bDof) {
6897         for (f = 0; f < numFields; f++) {
6898           PetscInt fDof, q, bOff, allFDof = 0;
6899 
6900           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6901           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6902           for (q = 0; q < bDof; q++) {
6903             PetscInt a = anchors[bOff + q];
6904             PetscInt aFDof;
6905 
6906             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6907             allFDof += aFDof;
6908           }
6909           newPointOffsets[f][p+1] = allFDof;
6910           pointMatOffsets[f][p+1] = fDof * allFDof;
6911         }
6912       }
6913       else {
6914         for (f = 0; f < numFields; f++) {
6915           PetscInt fDof;
6916 
6917           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6918           newPointOffsets[f][p+1] = fDof;
6919           pointMatOffsets[f][p+1] = 0;
6920         }
6921       }
6922     }
6923     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6924       newPointOffsets[f][0] = totalOffset;
6925       pointMatOffsets[f][0] = totalMatOffset;
6926       for (p = 0; p < numPoints; p++) {
6927         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6928         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6929       }
6930       totalOffset    = newPointOffsets[f][numPoints];
6931       totalMatOffset = pointMatOffsets[f][numPoints];
6932       PetscCall(DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
6933     }
6934   }
6935   else {
6936     for (p = 0; p < numPoints; p++) {
6937       PetscInt b    = points[2*p];
6938       PetscInt bDof = 0, bSecDof;
6939 
6940       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6941       if (!bSecDof) {
6942         newPointOffsets[0][p + 1] = 0;
6943         pointMatOffsets[0][p + 1] = 0;
6944         continue;
6945       }
6946       if (b >= aStart && b < aEnd) {
6947         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6948       }
6949       if (bDof) {
6950         PetscInt bOff, q, allDof = 0;
6951 
6952         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6953         for (q = 0; q < bDof; q++) {
6954           PetscInt a = anchors[bOff + q], aDof;
6955 
6956           PetscCall(PetscSectionGetDof(section, a, &aDof));
6957           allDof += aDof;
6958         }
6959         newPointOffsets[0][p+1] = allDof;
6960         pointMatOffsets[0][p+1] = bSecDof * allDof;
6961       }
6962       else {
6963         newPointOffsets[0][p+1] = bSecDof;
6964         pointMatOffsets[0][p+1] = 0;
6965       }
6966     }
6967     newPointOffsets[0][0] = 0;
6968     pointMatOffsets[0][0] = 0;
6969     for (p = 0; p < numPoints; p++) {
6970       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6971       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6972     }
6973     PetscCall(DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
6974   }
6975 
6976   /* output arrays */
6977   PetscCall(DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
6978 
6979   /* get the point-to-point matrices; construct newPoints */
6980   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6981   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6982   PetscCall(DMGetWorkArray(dm,maxDof,MPIU_INT,&indices));
6983   PetscCall(DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
6984   if (numFields) {
6985     for (p = 0, newP = 0; p < numPoints; p++) {
6986       PetscInt b    = points[2*p];
6987       PetscInt o    = points[2*p+1];
6988       PetscInt bDof = 0, bSecDof;
6989 
6990       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6991       if (!bSecDof) {
6992         continue;
6993       }
6994       if (b >= aStart && b < aEnd) {
6995         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6996       }
6997       if (bDof) {
6998         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6999 
7000         fStart[0] = 0;
7001         fEnd[0]   = 0;
7002         for (f = 0; f < numFields; f++) {
7003           PetscInt fDof;
7004 
7005           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7006           fStart[f+1] = fStart[f] + fDof;
7007           fEnd[f+1]   = fStart[f+1];
7008         }
7009         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7010         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7011 
7012         fAnchorStart[0] = 0;
7013         fAnchorEnd[0]   = 0;
7014         for (f = 0; f < numFields; f++) {
7015           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7016 
7017           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
7018           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
7019         }
7020         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7021         for (q = 0; q < bDof; q++) {
7022           PetscInt a = anchors[bOff + q], aOff;
7023 
7024           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7025           newPoints[2*(newP + q)]     = a;
7026           newPoints[2*(newP + q) + 1] = 0;
7027           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7028           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7029         }
7030         newP += bDof;
7031 
7032         if (outValues) {
7033           /* get the point-to-point submatrix */
7034           for (f = 0; f < numFields; f++) {
7035             PetscCall(MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]));
7036           }
7037         }
7038       }
7039       else {
7040         newPoints[2 * newP]     = b;
7041         newPoints[2 * newP + 1] = o;
7042         newP++;
7043       }
7044     }
7045   } else {
7046     for (p = 0; p < numPoints; p++) {
7047       PetscInt b    = points[2*p];
7048       PetscInt o    = points[2*p+1];
7049       PetscInt bDof = 0, bSecDof;
7050 
7051       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7052       if (!bSecDof) {
7053         continue;
7054       }
7055       if (b >= aStart && b < aEnd) {
7056         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7057       }
7058       if (bDof) {
7059         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7060 
7061         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7062         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7063 
7064         PetscCall(PetscSectionGetOffset (aSec, b, &bOff));
7065         for (q = 0; q < bDof; q++) {
7066           PetscInt a = anchors[bOff + q], aOff;
7067 
7068           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7069 
7070           newPoints[2*(newP + q)]     = a;
7071           newPoints[2*(newP + q) + 1] = 0;
7072           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7073           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7074         }
7075         newP += bDof;
7076 
7077         /* get the point-to-point submatrix */
7078         if (outValues) {
7079           PetscCall(MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]));
7080         }
7081       }
7082       else {
7083         newPoints[2 * newP]     = b;
7084         newPoints[2 * newP + 1] = o;
7085         newP++;
7086       }
7087     }
7088   }
7089 
7090   if (outValues) {
7091     PetscCall(DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7092     PetscCall(PetscArrayzero(tmpValues,newNumIndices*numIndices));
7093     /* multiply constraints on the right */
7094     if (numFields) {
7095       for (f = 0; f < numFields; f++) {
7096         PetscInt oldOff = offsets[f];
7097 
7098         for (p = 0; p < numPoints; p++) {
7099           PetscInt cStart = newPointOffsets[f][p];
7100           PetscInt b      = points[2 * p];
7101           PetscInt c, r, k;
7102           PetscInt dof;
7103 
7104           PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7105           if (!dof) {
7106             continue;
7107           }
7108           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7109             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
7110             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
7111 
7112             for (r = 0; r < numIndices; r++) {
7113               for (c = 0; c < nCols; c++) {
7114                 for (k = 0; k < dof; k++) {
7115                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7116                 }
7117               }
7118             }
7119           }
7120           else {
7121             /* copy this column as is */
7122             for (r = 0; r < numIndices; r++) {
7123               for (c = 0; c < dof; c++) {
7124                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7125               }
7126             }
7127           }
7128           oldOff += dof;
7129         }
7130       }
7131     }
7132     else {
7133       PetscInt oldOff = 0;
7134       for (p = 0; p < numPoints; p++) {
7135         PetscInt cStart = newPointOffsets[0][p];
7136         PetscInt b      = points[2 * p];
7137         PetscInt c, r, k;
7138         PetscInt dof;
7139 
7140         PetscCall(PetscSectionGetDof(section,b,&dof));
7141         if (!dof) {
7142           continue;
7143         }
7144         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7145           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
7146           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
7147 
7148           for (r = 0; r < numIndices; r++) {
7149             for (c = 0; c < nCols; c++) {
7150               for (k = 0; k < dof; k++) {
7151                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7152               }
7153             }
7154           }
7155         }
7156         else {
7157           /* copy this column as is */
7158           for (r = 0; r < numIndices; r++) {
7159             for (c = 0; c < dof; c++) {
7160               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7161             }
7162           }
7163         }
7164         oldOff += dof;
7165       }
7166     }
7167 
7168     if (multiplyLeft) {
7169       PetscCall(DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues));
7170       PetscCall(PetscArrayzero(newValues,newNumIndices*newNumIndices));
7171       /* multiply constraints transpose on the left */
7172       if (numFields) {
7173         for (f = 0; f < numFields; f++) {
7174           PetscInt oldOff = offsets[f];
7175 
7176           for (p = 0; p < numPoints; p++) {
7177             PetscInt rStart = newPointOffsets[f][p];
7178             PetscInt b      = points[2 * p];
7179             PetscInt c, r, k;
7180             PetscInt dof;
7181 
7182             PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7183             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7184               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
7185               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
7186 
7187               for (r = 0; r < nRows; r++) {
7188                 for (c = 0; c < newNumIndices; c++) {
7189                   for (k = 0; k < dof; k++) {
7190                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7191                   }
7192                 }
7193               }
7194             }
7195             else {
7196               /* copy this row as is */
7197               for (r = 0; r < dof; r++) {
7198                 for (c = 0; c < newNumIndices; c++) {
7199                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7200                 }
7201               }
7202             }
7203             oldOff += dof;
7204           }
7205         }
7206       }
7207       else {
7208         PetscInt oldOff = 0;
7209 
7210         for (p = 0; p < numPoints; p++) {
7211           PetscInt rStart = newPointOffsets[0][p];
7212           PetscInt b      = points[2 * p];
7213           PetscInt c, r, k;
7214           PetscInt dof;
7215 
7216           PetscCall(PetscSectionGetDof(section,b,&dof));
7217           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7218             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
7219             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
7220 
7221             for (r = 0; r < nRows; r++) {
7222               for (c = 0; c < newNumIndices; c++) {
7223                 for (k = 0; k < dof; k++) {
7224                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7225                 }
7226               }
7227             }
7228           }
7229           else {
7230             /* copy this row as is */
7231             for (r = 0; r < dof; r++) {
7232               for (c = 0; c < newNumIndices; c++) {
7233                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7234               }
7235             }
7236           }
7237           oldOff += dof;
7238         }
7239       }
7240 
7241       PetscCall(DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7242     }
7243     else {
7244       newValues = tmpValues;
7245     }
7246   }
7247 
7248   /* clean up */
7249   PetscCall(DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices));
7250   PetscCall(DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
7251 
7252   if (numFields) {
7253     for (f = 0; f < numFields; f++) {
7254       PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
7255       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
7256       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
7257     }
7258   }
7259   else {
7260     PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
7261     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
7262     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]));
7263   }
7264   PetscCall(ISRestoreIndices(aIS,&anchors));
7265 
7266   /* output */
7267   if (outPoints) {
7268     *outPoints = newPoints;
7269   }
7270   else {
7271     PetscCall(DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
7272   }
7273   if (outValues) {
7274     *outValues = newValues;
7275   }
7276   for (f = 0; f <= numFields; f++) {
7277     offsets[f] = newOffsets[f];
7278   }
7279   PetscFunctionReturn(0);
7280 }
7281 
7282 /*@C
7283   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7284 
7285   Not collective
7286 
7287   Input Parameters:
7288 + dm         - The DM
7289 . section    - The PetscSection describing the points (a local section)
7290 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7291 . point      - The point defining the closure
7292 - useClPerm  - Use the closure point permutation if available
7293 
7294   Output Parameters:
7295 + numIndices - The number of dof indices in the closure of point with the input sections
7296 . indices    - The dof indices
7297 . outOffsets - Array to write the field offsets into, or NULL
7298 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7299 
7300   Notes:
7301   Must call DMPlexRestoreClosureIndices() to free allocated memory
7302 
7303   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7304   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7305   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7306   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7307   indices (with the above semantics) are implied.
7308 
7309   Level: advanced
7310 
7311 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7312 @*/
7313 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7314                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7315 {
7316   /* Closure ordering */
7317   PetscSection        clSection;
7318   IS                  clPoints;
7319   const PetscInt     *clp;
7320   PetscInt           *points;
7321   const PetscInt     *clperm = NULL;
7322   /* Dof permutation and sign flips */
7323   const PetscInt    **perms[32] = {NULL};
7324   const PetscScalar **flips[32] = {NULL};
7325   PetscScalar        *valCopy   = NULL;
7326   /* Hanging node constraints */
7327   PetscInt           *pointsC = NULL;
7328   PetscScalar        *valuesC = NULL;
7329   PetscInt            NclC, NiC;
7330 
7331   PetscInt           *idx;
7332   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
7333   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7334 
7335   PetscFunctionBeginHot;
7336   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7337   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7338   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7339   if (numIndices) PetscValidIntPointer(numIndices, 6);
7340   if (indices)    PetscValidPointer(indices, 7);
7341   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7342   if (values)     PetscValidPointer(values, 9);
7343   PetscCall(PetscSectionGetNumFields(section, &Nf));
7344   PetscCheck(Nf <= 31,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7345   PetscCall(PetscArrayzero(offsets, 32));
7346   /* 1) Get points in closure */
7347   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7348   if (useClPerm) {
7349     PetscInt depth, clsize;
7350     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7351     for (clsize=0,p=0; p<Ncl; p++) {
7352       PetscInt dof;
7353       PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
7354       clsize += dof;
7355     }
7356     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
7357   }
7358   /* 2) Get number of indices on these points and field offsets from section */
7359   for (p = 0; p < Ncl*2; p += 2) {
7360     PetscInt dof, fdof;
7361 
7362     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7363     for (f = 0; f < Nf; ++f) {
7364       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7365       offsets[f+1] += fdof;
7366     }
7367     Ni += dof;
7368   }
7369   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
7370   PetscCheck(!Nf || offsets[Nf] == Ni,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7371   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7372   for (f = 0; f < PetscMax(1, Nf); ++f) {
7373     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7374     else    PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7375     /* may need to apply sign changes to the element matrix */
7376     if (values && flips[f]) {
7377       PetscInt foffset = offsets[f];
7378 
7379       for (p = 0; p < Ncl; ++p) {
7380         PetscInt           pnt  = points[2*p], fdof;
7381         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7382 
7383         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7384         else     PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7385         if (flip) {
7386           PetscInt i, j, k;
7387 
7388           if (!valCopy) {
7389             PetscCall(DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7390             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7391             *values = valCopy;
7392           }
7393           for (i = 0; i < fdof; ++i) {
7394             PetscScalar fval = flip[i];
7395 
7396             for (k = 0; k < Ni; ++k) {
7397               valCopy[Ni * (foffset + i) + k] *= fval;
7398               valCopy[Ni * k + (foffset + i)] *= fval;
7399             }
7400           }
7401         }
7402         foffset += fdof;
7403       }
7404     }
7405   }
7406   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7407   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7408   if (NclC) {
7409     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7410     for (f = 0; f < PetscMax(1, Nf); ++f) {
7411       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7412       else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7413     }
7414     for (f = 0; f < PetscMax(1, Nf); ++f) {
7415       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7416       else    PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7417     }
7418     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7419     Ncl     = NclC;
7420     Ni      = NiC;
7421     points  = pointsC;
7422     if (values) *values = valuesC;
7423   }
7424   /* 5) Calculate indices */
7425   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7426   if (Nf) {
7427     PetscInt  idxOff;
7428     PetscBool useFieldOffsets;
7429 
7430     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
7431     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7432     if (useFieldOffsets) {
7433       for (p = 0; p < Ncl; ++p) {
7434         const PetscInt pnt = points[p*2];
7435 
7436         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7437       }
7438     } else {
7439       for (p = 0; p < Ncl; ++p) {
7440         const PetscInt pnt = points[p*2];
7441 
7442         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7443         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7444          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7445          * global section. */
7446         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7447       }
7448     }
7449   } else {
7450     PetscInt off = 0, idxOff;
7451 
7452     for (p = 0; p < Ncl; ++p) {
7453       const PetscInt  pnt  = points[p*2];
7454       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7455 
7456       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7457       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7458        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7459       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7460     }
7461   }
7462   /* 6) Cleanup */
7463   for (f = 0; f < PetscMax(1, Nf); ++f) {
7464     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7465     else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7466   }
7467   if (NclC) {
7468     PetscCall(DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC));
7469   } else {
7470     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7471   }
7472 
7473   if (numIndices) *numIndices = Ni;
7474   if (indices)    *indices    = idx;
7475   PetscFunctionReturn(0);
7476 }
7477 
7478 /*@C
7479   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7480 
7481   Not collective
7482 
7483   Input Parameters:
7484 + dm         - The DM
7485 . section    - The PetscSection describing the points (a local section)
7486 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7487 . point      - The point defining the closure
7488 - useClPerm  - Use the closure point permutation if available
7489 
7490   Output Parameters:
7491 + numIndices - The number of dof indices in the closure of point with the input sections
7492 . indices    - The dof indices
7493 . outOffsets - Array to write the field offsets into, or NULL
7494 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7495 
7496   Notes:
7497   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7498 
7499   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7500   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7501   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7502   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7503   indices (with the above semantics) are implied.
7504 
7505   Level: advanced
7506 
7507 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7508 @*/
7509 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7510                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7511 {
7512   PetscFunctionBegin;
7513   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7514   PetscValidPointer(indices, 7);
7515   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7516   PetscFunctionReturn(0);
7517 }
7518 
7519 /*@C
7520   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7521 
7522   Not collective
7523 
7524   Input Parameters:
7525 + dm - The DM
7526 . section - The section describing the layout in v, or NULL to use the default section
7527 . globalSection - The section describing the layout in v, or NULL to use the default global section
7528 . A - The matrix
7529 . point - The point in the DM
7530 . values - The array of values
7531 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7532 
7533   Fortran Notes:
7534   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7535 
7536   Level: intermediate
7537 
7538 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7539 @*/
7540 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7541 {
7542   DM_Plex           *mesh = (DM_Plex*) dm->data;
7543   PetscInt          *indices;
7544   PetscInt           numIndices;
7545   const PetscScalar *valuesOrig = values;
7546   PetscErrorCode     ierr;
7547 
7548   PetscFunctionBegin;
7549   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7550   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7551   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7552   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7553   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7554   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7555 
7556   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7557 
7558   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7559   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7560   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7561   if (ierr) {
7562     PetscMPIInt    rank;
7563 
7564     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7565     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7566     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7567     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7568     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7569     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7570   }
7571   if (mesh->printFEM > 1) {
7572     PetscInt i;
7573     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7574     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7575     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7576   }
7577 
7578   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7579   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7580   PetscFunctionReturn(0);
7581 }
7582 
7583 /*@C
7584   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7585 
7586   Not collective
7587 
7588   Input Parameters:
7589 + dmRow - The DM for the row fields
7590 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7591 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7592 . dmCol - The DM for the column fields
7593 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7594 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7595 . A - The matrix
7596 . point - The point in the DMs
7597 . values - The array of values
7598 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7599 
7600   Level: intermediate
7601 
7602 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7603 @*/
7604 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7605 {
7606   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7607   PetscInt          *indicesRow, *indicesCol;
7608   PetscInt           numIndicesRow, numIndicesCol;
7609   const PetscScalar *valuesOrig = values;
7610   PetscErrorCode     ierr;
7611 
7612   PetscFunctionBegin;
7613   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7614   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7615   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7616   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7617   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7618   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7619   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7620   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7621   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7622   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7623   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7624 
7625   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7626   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7627 
7628   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7629   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7630   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7631   if (ierr) {
7632     PetscMPIInt    rank;
7633 
7634     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7635     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7636     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7637     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7638     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7639     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7640   }
7641 
7642   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7643   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7644   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7645   PetscFunctionReturn(0);
7646 }
7647 
7648 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7649 {
7650   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7651   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7652   PetscInt       *cpoints = NULL;
7653   PetscInt       *findices, *cindices;
7654   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7655   PetscInt        foffsets[32], coffsets[32];
7656   DMPolytopeType  ct;
7657   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7658   PetscErrorCode  ierr;
7659 
7660   PetscFunctionBegin;
7661   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7662   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7663   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7664   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7665   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7666   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7667   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7668   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7669   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7670   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7671   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7672   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7673   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7674   PetscCall(PetscArrayzero(foffsets, 32));
7675   PetscCall(PetscArrayzero(coffsets, 32));
7676   /* Column indices */
7677   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7678   maxFPoints = numCPoints;
7679   /* Compress out points not in the section */
7680   /*   TODO: Squeeze out points with 0 dof as well */
7681   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7682   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7683     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7684       cpoints[q*2]   = cpoints[p];
7685       cpoints[q*2+1] = cpoints[p+1];
7686       ++q;
7687     }
7688   }
7689   numCPoints = q;
7690   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7691     PetscInt fdof;
7692 
7693     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7694     if (!dof) continue;
7695     for (f = 0; f < numFields; ++f) {
7696       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7697       coffsets[f+1] += fdof;
7698     }
7699     numCIndices += dof;
7700   }
7701   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7702   /* Row indices */
7703   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7704   {
7705     DMPlexTransform tr;
7706     DMPolytopeType *rct;
7707     PetscInt       *rsize, *rcone, *rornt, Nt;
7708 
7709     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7710     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7711     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7712     numSubcells = rsize[Nt-1];
7713     PetscCall(DMPlexTransformDestroy(&tr));
7714   }
7715   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7716   for (r = 0, q = 0; r < numSubcells; ++r) {
7717     /* TODO Map from coarse to fine cells */
7718     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7719     /* Compress out points not in the section */
7720     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7721     for (p = 0; p < numFPoints*2; p += 2) {
7722       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7723         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7724         if (!dof) continue;
7725         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7726         if (s < q) continue;
7727         ftotpoints[q*2]   = fpoints[p];
7728         ftotpoints[q*2+1] = fpoints[p+1];
7729         ++q;
7730       }
7731     }
7732     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7733   }
7734   numFPoints = q;
7735   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7736     PetscInt fdof;
7737 
7738     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7739     if (!dof) continue;
7740     for (f = 0; f < numFields; ++f) {
7741       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7742       foffsets[f+1] += fdof;
7743     }
7744     numFIndices += dof;
7745   }
7746   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7747 
7748   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7749   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7750   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7751   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7752   if (numFields) {
7753     const PetscInt **permsF[32] = {NULL};
7754     const PetscInt **permsC[32] = {NULL};
7755 
7756     for (f = 0; f < numFields; f++) {
7757       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7758       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7759     }
7760     for (p = 0; p < numFPoints; p++) {
7761       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7762       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7763     }
7764     for (p = 0; p < numCPoints; p++) {
7765       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7766       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7767     }
7768     for (f = 0; f < numFields; f++) {
7769       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7770       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7771     }
7772   } else {
7773     const PetscInt **permsF = NULL;
7774     const PetscInt **permsC = NULL;
7775 
7776     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7777     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7778     for (p = 0, off = 0; p < numFPoints; p++) {
7779       const PetscInt *perm = permsF ? permsF[p] : NULL;
7780 
7781       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7782       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7783     }
7784     for (p = 0, off = 0; p < numCPoints; p++) {
7785       const PetscInt *perm = permsC ? permsC[p] : NULL;
7786 
7787       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7788       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7789     }
7790     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7791     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7792   }
7793   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7794   /* TODO: flips */
7795   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7796   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7797   if (ierr) {
7798     PetscMPIInt    rank;
7799 
7800     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7801     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7802     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7803     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7804     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7805   }
7806   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7807   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7808   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7809   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7810   PetscFunctionReturn(0);
7811 }
7812 
7813 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7814 {
7815   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7816   PetscInt      *cpoints = NULL;
7817   PetscInt       foffsets[32], coffsets[32];
7818   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7819   DMPolytopeType ct;
7820   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7821 
7822   PetscFunctionBegin;
7823   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7824   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7825   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7826   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7827   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7828   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7829   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7830   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7831   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7832   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7833   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7834   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7835   PetscCall(PetscArrayzero(foffsets, 32));
7836   PetscCall(PetscArrayzero(coffsets, 32));
7837   /* Column indices */
7838   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7839   maxFPoints = numCPoints;
7840   /* Compress out points not in the section */
7841   /*   TODO: Squeeze out points with 0 dof as well */
7842   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7843   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7844     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7845       cpoints[q*2]   = cpoints[p];
7846       cpoints[q*2+1] = cpoints[p+1];
7847       ++q;
7848     }
7849   }
7850   numCPoints = q;
7851   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7852     PetscInt fdof;
7853 
7854     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7855     if (!dof) continue;
7856     for (f = 0; f < numFields; ++f) {
7857       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7858       coffsets[f+1] += fdof;
7859     }
7860     numCIndices += dof;
7861   }
7862   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7863   /* Row indices */
7864   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7865   {
7866     DMPlexTransform tr;
7867     DMPolytopeType *rct;
7868     PetscInt       *rsize, *rcone, *rornt, Nt;
7869 
7870     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7871     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7872     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7873     numSubcells = rsize[Nt-1];
7874     PetscCall(DMPlexTransformDestroy(&tr));
7875   }
7876   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7877   for (r = 0, q = 0; r < numSubcells; ++r) {
7878     /* TODO Map from coarse to fine cells */
7879     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7880     /* Compress out points not in the section */
7881     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7882     for (p = 0; p < numFPoints*2; p += 2) {
7883       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7884         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7885         if (!dof) continue;
7886         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7887         if (s < q) continue;
7888         ftotpoints[q*2]   = fpoints[p];
7889         ftotpoints[q*2+1] = fpoints[p+1];
7890         ++q;
7891       }
7892     }
7893     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7894   }
7895   numFPoints = q;
7896   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7897     PetscInt fdof;
7898 
7899     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7900     if (!dof) continue;
7901     for (f = 0; f < numFields; ++f) {
7902       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7903       foffsets[f+1] += fdof;
7904     }
7905     numFIndices += dof;
7906   }
7907   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7908 
7909   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7910   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7911   if (numFields) {
7912     const PetscInt **permsF[32] = {NULL};
7913     const PetscInt **permsC[32] = {NULL};
7914 
7915     for (f = 0; f < numFields; f++) {
7916       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7917       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7918     }
7919     for (p = 0; p < numFPoints; p++) {
7920       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7921       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7922     }
7923     for (p = 0; p < numCPoints; p++) {
7924       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7925       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7926     }
7927     for (f = 0; f < numFields; f++) {
7928       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7929       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7930     }
7931   } else {
7932     const PetscInt **permsF = NULL;
7933     const PetscInt **permsC = NULL;
7934 
7935     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7936     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7937     for (p = 0, off = 0; p < numFPoints; p++) {
7938       const PetscInt *perm = permsF ? permsF[p] : NULL;
7939 
7940       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7941       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7942     }
7943     for (p = 0, off = 0; p < numCPoints; p++) {
7944       const PetscInt *perm = permsC ? permsC[p] : NULL;
7945 
7946       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7947       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7948     }
7949     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7950     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7951   }
7952   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7953   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7954   PetscFunctionReturn(0);
7955 }
7956 
7957 /*@C
7958   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7959 
7960   Input Parameter:
7961 . dm   - The DMPlex object
7962 
7963   Output Parameter:
7964 . cellHeight - The height of a cell
7965 
7966   Level: developer
7967 
7968 .seealso `DMPlexSetVTKCellHeight()`
7969 @*/
7970 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7971 {
7972   DM_Plex *mesh = (DM_Plex*) dm->data;
7973 
7974   PetscFunctionBegin;
7975   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7976   PetscValidIntPointer(cellHeight, 2);
7977   *cellHeight = mesh->vtkCellHeight;
7978   PetscFunctionReturn(0);
7979 }
7980 
7981 /*@C
7982   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7983 
7984   Input Parameters:
7985 + dm   - The DMPlex object
7986 - cellHeight - The height of a cell
7987 
7988   Level: developer
7989 
7990 .seealso `DMPlexGetVTKCellHeight()`
7991 @*/
7992 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7993 {
7994   DM_Plex *mesh = (DM_Plex*) dm->data;
7995 
7996   PetscFunctionBegin;
7997   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7998   mesh->vtkCellHeight = cellHeight;
7999   PetscFunctionReturn(0);
8000 }
8001 
8002 /*@
8003   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8004 
8005   Input Parameter:
8006 . dm - The DMPlex object
8007 
8008   Output Parameters:
8009 + gcStart - The first ghost cell, or NULL
8010 - gcEnd   - The upper bound on ghost cells, or NULL
8011 
8012   Level: advanced
8013 
8014 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8015 @*/
8016 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8017 {
8018   DMLabel        ctLabel;
8019 
8020   PetscFunctionBegin;
8021   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8022   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8023   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8024   // Reset label for fast lookup
8025   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8026   PetscFunctionReturn(0);
8027 }
8028 
8029 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8030 {
8031   PetscSection   section, globalSection;
8032   PetscInt      *numbers, p;
8033 
8034   PetscFunctionBegin;
8035   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
8036   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8037   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8038   for (p = pStart; p < pEnd; ++p) {
8039     PetscCall(PetscSectionSetDof(section, p, 1));
8040   }
8041   PetscCall(PetscSectionSetUp(section));
8042   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8043   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8044   for (p = pStart; p < pEnd; ++p) {
8045     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8046     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8047     else                       numbers[p-pStart] += shift;
8048   }
8049   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8050   if (globalSize) {
8051     PetscLayout layout;
8052     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8053     PetscCall(PetscLayoutGetSize(layout, globalSize));
8054     PetscCall(PetscLayoutDestroy(&layout));
8055   }
8056   PetscCall(PetscSectionDestroy(&section));
8057   PetscCall(PetscSectionDestroy(&globalSection));
8058   PetscFunctionReturn(0);
8059 }
8060 
8061 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8062 {
8063   PetscInt       cellHeight, cStart, cEnd;
8064 
8065   PetscFunctionBegin;
8066   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8067   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8068   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8069   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8070   PetscFunctionReturn(0);
8071 }
8072 
8073 /*@
8074   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8075 
8076   Input Parameter:
8077 . dm   - The DMPlex object
8078 
8079   Output Parameter:
8080 . globalCellNumbers - Global cell numbers for all cells on this process
8081 
8082   Level: developer
8083 
8084 .seealso `DMPlexGetVertexNumbering()`
8085 @*/
8086 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8087 {
8088   DM_Plex       *mesh = (DM_Plex*) dm->data;
8089 
8090   PetscFunctionBegin;
8091   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8092   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8093   *globalCellNumbers = mesh->globalCellNumbers;
8094   PetscFunctionReturn(0);
8095 }
8096 
8097 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8098 {
8099   PetscInt       vStart, vEnd;
8100 
8101   PetscFunctionBegin;
8102   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8103   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8104   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8105   PetscFunctionReturn(0);
8106 }
8107 
8108 /*@
8109   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8110 
8111   Input Parameter:
8112 . dm   - The DMPlex object
8113 
8114   Output Parameter:
8115 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8116 
8117   Level: developer
8118 
8119 .seealso `DMPlexGetCellNumbering()`
8120 @*/
8121 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8122 {
8123   DM_Plex       *mesh = (DM_Plex*) dm->data;
8124 
8125   PetscFunctionBegin;
8126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8127   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8128   *globalVertexNumbers = mesh->globalVertexNumbers;
8129   PetscFunctionReturn(0);
8130 }
8131 
8132 /*@
8133   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
8134 
8135   Input Parameter:
8136 . dm   - The DMPlex object
8137 
8138   Output Parameter:
8139 . globalPointNumbers - Global numbers for all points on this process
8140 
8141   Level: developer
8142 
8143 .seealso `DMPlexGetCellNumbering()`
8144 @*/
8145 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8146 {
8147   IS             nums[4];
8148   PetscInt       depths[4], gdepths[4], starts[4];
8149   PetscInt       depth, d, shift = 0;
8150 
8151   PetscFunctionBegin;
8152   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8153   PetscCall(DMPlexGetDepth(dm, &depth));
8154   /* For unstratified meshes use dim instead of depth */
8155   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8156   for (d = 0; d <= depth; ++d) {
8157     PetscInt end;
8158 
8159     depths[d] = depth-d;
8160     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8161     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8162   }
8163   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8164   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8165   for (d = 0; d <= depth; ++d) {
8166     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]);
8167   }
8168   for (d = 0; d <= depth; ++d) {
8169     PetscInt pStart, pEnd, gsize;
8170 
8171     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8172     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8173     shift += gsize;
8174   }
8175   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8176   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8177   PetscFunctionReturn(0);
8178 }
8179 
8180 /*@
8181   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8182 
8183   Input Parameter:
8184 . dm - The DMPlex object
8185 
8186   Output Parameter:
8187 . ranks - The rank field
8188 
8189   Options Database Keys:
8190 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8191 
8192   Level: intermediate
8193 
8194 .seealso: `DMView()`
8195 @*/
8196 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8197 {
8198   DM             rdm;
8199   PetscFE        fe;
8200   PetscScalar   *r;
8201   PetscMPIInt    rank;
8202   DMPolytopeType ct;
8203   PetscInt       dim, cStart, cEnd, c;
8204   PetscBool      simplex;
8205 
8206   PetscFunctionBeginUser;
8207   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8208   PetscValidPointer(ranks, 2);
8209   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8210   PetscCall(DMClone(dm, &rdm));
8211   PetscCall(DMGetDimension(rdm, &dim));
8212   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8213   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8214   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8215   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8216   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8217   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8218   PetscCall(PetscFEDestroy(&fe));
8219   PetscCall(DMCreateDS(rdm));
8220   PetscCall(DMCreateGlobalVector(rdm, ranks));
8221   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8222   PetscCall(VecGetArray(*ranks, &r));
8223   for (c = cStart; c < cEnd; ++c) {
8224     PetscScalar *lr;
8225 
8226     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8227     if (lr) *lr = rank;
8228   }
8229   PetscCall(VecRestoreArray(*ranks, &r));
8230   PetscCall(DMDestroy(&rdm));
8231   PetscFunctionReturn(0);
8232 }
8233 
8234 /*@
8235   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8236 
8237   Input Parameters:
8238 + dm    - The DMPlex
8239 - label - The DMLabel
8240 
8241   Output Parameter:
8242 . val - The label value field
8243 
8244   Options Database Keys:
8245 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8246 
8247   Level: intermediate
8248 
8249 .seealso: `DMView()`
8250 @*/
8251 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8252 {
8253   DM             rdm;
8254   PetscFE        fe;
8255   PetscScalar   *v;
8256   PetscInt       dim, cStart, cEnd, c;
8257 
8258   PetscFunctionBeginUser;
8259   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8260   PetscValidPointer(label, 2);
8261   PetscValidPointer(val, 3);
8262   PetscCall(DMClone(dm, &rdm));
8263   PetscCall(DMGetDimension(rdm, &dim));
8264   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8265   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8266   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8267   PetscCall(PetscFEDestroy(&fe));
8268   PetscCall(DMCreateDS(rdm));
8269   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8270   PetscCall(DMCreateGlobalVector(rdm, val));
8271   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8272   PetscCall(VecGetArray(*val, &v));
8273   for (c = cStart; c < cEnd; ++c) {
8274     PetscScalar *lv;
8275     PetscInt     cval;
8276 
8277     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8278     PetscCall(DMLabelGetValue(label, c, &cval));
8279     *lv = cval;
8280   }
8281   PetscCall(VecRestoreArray(*val, &v));
8282   PetscCall(DMDestroy(&rdm));
8283   PetscFunctionReturn(0);
8284 }
8285 
8286 /*@
8287   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8288 
8289   Input Parameter:
8290 . dm - The DMPlex object
8291 
8292   Notes:
8293   This is a useful diagnostic when creating meshes programmatically.
8294 
8295   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8296 
8297   Level: developer
8298 
8299 .seealso: `DMCreate()`, `DMSetFromOptions()`
8300 @*/
8301 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8302 {
8303   PetscSection    coneSection, supportSection;
8304   const PetscInt *cone, *support;
8305   PetscInt        coneSize, c, supportSize, s;
8306   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8307   PetscBool       storagecheck = PETSC_TRUE;
8308 
8309   PetscFunctionBegin;
8310   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8311   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8312   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8313   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8314   /* Check that point p is found in the support of its cone points, and vice versa */
8315   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8316   for (p = pStart; p < pEnd; ++p) {
8317     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8318     PetscCall(DMPlexGetCone(dm, p, &cone));
8319     for (c = 0; c < coneSize; ++c) {
8320       PetscBool dup = PETSC_FALSE;
8321       PetscInt  d;
8322       for (d = c-1; d >= 0; --d) {
8323         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8324       }
8325       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8326       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8327       for (s = 0; s < supportSize; ++s) {
8328         if (support[s] == p) break;
8329       }
8330       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8331         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8332         for (s = 0; s < coneSize; ++s) {
8333           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8334         }
8335         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8336         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8337         for (s = 0; s < supportSize; ++s) {
8338           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8339         }
8340         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8341         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]);
8342         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8343       }
8344     }
8345     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8346     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8347     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8348     PetscCall(DMPlexGetSupport(dm, p, &support));
8349     for (s = 0; s < supportSize; ++s) {
8350       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8351       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8352       for (c = 0; c < coneSize; ++c) {
8353         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8354         if (cone[c] != pp) { c = 0; break; }
8355         if (cone[c] == p) break;
8356       }
8357       if (c >= coneSize) {
8358         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8359         for (c = 0; c < supportSize; ++c) {
8360           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8361         }
8362         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8363         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8364         for (c = 0; c < coneSize; ++c) {
8365           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8366         }
8367         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8368         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8369       }
8370     }
8371   }
8372   if (storagecheck) {
8373     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8374     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8375     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8376   }
8377   PetscFunctionReturn(0);
8378 }
8379 
8380 /*
8381   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.
8382 */
8383 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8384 {
8385   DMPolytopeType  cct;
8386   PetscInt        ptpoints[4];
8387   const PetscInt *cone, *ccone, *ptcone;
8388   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8389 
8390   PetscFunctionBegin;
8391   *unsplit = 0;
8392   switch (ct) {
8393     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8394       ptpoints[npt++] = c;
8395       break;
8396     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8397       PetscCall(DMPlexGetCone(dm, c, &cone));
8398       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8399       for (cp = 0; cp < coneSize; ++cp) {
8400         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8401         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8402       }
8403       break;
8404     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8405     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8406       PetscCall(DMPlexGetCone(dm, c, &cone));
8407       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8408       for (cp = 0; cp < coneSize; ++cp) {
8409         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8410         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8411         for (ccp = 0; ccp < cconeSize; ++ccp) {
8412           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8413           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8414             PetscInt p;
8415             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8416             if (p == npt) ptpoints[npt++] = ccone[ccp];
8417           }
8418         }
8419       }
8420       break;
8421     default: break;
8422   }
8423   for (pt = 0; pt < npt; ++pt) {
8424     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8425     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8426   }
8427   PetscFunctionReturn(0);
8428 }
8429 
8430 /*@
8431   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8432 
8433   Input Parameters:
8434 + dm - The DMPlex object
8435 - cellHeight - Normally 0
8436 
8437   Notes:
8438   This is a useful diagnostic when creating meshes programmatically.
8439   Currently applicable only to homogeneous simplex or tensor meshes.
8440 
8441   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8442 
8443   Level: developer
8444 
8445 .seealso: `DMCreate()`, `DMSetFromOptions()`
8446 @*/
8447 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8448 {
8449   DMPlexInterpolatedFlag interp;
8450   DMPolytopeType         ct;
8451   PetscInt               vStart, vEnd, cStart, cEnd, c;
8452 
8453   PetscFunctionBegin;
8454   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8455   PetscCall(DMPlexIsInterpolated(dm, &interp));
8456   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8457   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8458   for (c = cStart; c < cEnd; ++c) {
8459     PetscInt *closure = NULL;
8460     PetscInt  coneSize, closureSize, cl, Nv = 0;
8461 
8462     PetscCall(DMPlexGetCellType(dm, c, &ct));
8463     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8464     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8465     if (interp == DMPLEX_INTERPOLATED_FULL) {
8466       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8467       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));
8468     }
8469     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8470     for (cl = 0; cl < closureSize*2; cl += 2) {
8471       const PetscInt p = closure[cl];
8472       if ((p >= vStart) && (p < vEnd)) ++Nv;
8473     }
8474     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8475     /* Special Case: Tensor faces with identified vertices */
8476     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8477       PetscInt unsplit;
8478 
8479       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8480       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8481     }
8482     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));
8483   }
8484   PetscFunctionReturn(0);
8485 }
8486 
8487 /*@
8488   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8489 
8490   Collective
8491 
8492   Input Parameters:
8493 + dm - The DMPlex object
8494 - cellHeight - Normally 0
8495 
8496   Notes:
8497   This is a useful diagnostic when creating meshes programmatically.
8498   This routine is only relevant for meshes that are fully interpolated across all ranks.
8499   It will error out if a partially interpolated mesh is given on some rank.
8500   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8501 
8502   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8503 
8504   Level: developer
8505 
8506 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8507 @*/
8508 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8509 {
8510   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8511   DMPlexInterpolatedFlag interpEnum;
8512 
8513   PetscFunctionBegin;
8514   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8515   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8516   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8517   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8518     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8519     PetscFunctionReturn(0);
8520   }
8521 
8522   PetscCall(DMGetDimension(dm, &dim));
8523   PetscCall(DMPlexGetDepth(dm, &depth));
8524   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8525   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8526     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8527     for (c = cStart; c < cEnd; ++c) {
8528       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8529       const DMPolytopeType *faceTypes;
8530       DMPolytopeType        ct;
8531       PetscInt              numFaces, coneSize, f;
8532       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8533 
8534       PetscCall(DMPlexGetCellType(dm, c, &ct));
8535       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8536       if (unsplit) continue;
8537       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8538       PetscCall(DMPlexGetCone(dm, c, &cone));
8539       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8540       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8541       for (cl = 0; cl < closureSize*2; cl += 2) {
8542         const PetscInt p = closure[cl];
8543         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8544       }
8545       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8546       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);
8547       for (f = 0; f < numFaces; ++f) {
8548         DMPolytopeType fct;
8549         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8550 
8551         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8552         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8553         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8554           const PetscInt p = fclosure[cl];
8555           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8556         }
8557         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]);
8558         for (v = 0; v < fnumCorners; ++v) {
8559           if (fclosure[v] != faces[fOff+v]) {
8560             PetscInt v1;
8561 
8562             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8563             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8564             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8565             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff+v1]));
8566             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8567             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]);
8568           }
8569         }
8570         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8571         fOff += faceSizes[f];
8572       }
8573       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8574       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8575     }
8576   }
8577   PetscFunctionReturn(0);
8578 }
8579 
8580 /*@
8581   DMPlexCheckGeometry - Check the geometry of mesh cells
8582 
8583   Input Parameter:
8584 . dm - The DMPlex object
8585 
8586   Notes:
8587   This is a useful diagnostic when creating meshes programmatically.
8588 
8589   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8590 
8591   Level: developer
8592 
8593 .seealso: `DMCreate()`, `DMSetFromOptions()`
8594 @*/
8595 PetscErrorCode DMPlexCheckGeometry(DM dm)
8596 {
8597   Vec       coordinates;
8598   PetscReal detJ, J[9], refVol = 1.0;
8599   PetscReal vol;
8600   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8601 
8602   PetscFunctionBegin;
8603   PetscCall(DMGetDimension(dm, &dim));
8604   PetscCall(DMGetCoordinateDim(dm, &dE));
8605   if (dim != dE) PetscFunctionReturn(0);
8606   PetscCall(DMPlexGetDepth(dm, &depth));
8607   for (d = 0; d < dim; ++d) refVol *= 2.0;
8608   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8609   /* Make sure local coordinates are created, because that step is collective */
8610   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8611   for (c = cStart; c < cEnd; ++c) {
8612     DMPolytopeType ct;
8613     PetscInt       unsplit;
8614     PetscBool      ignoreZeroVol = PETSC_FALSE;
8615 
8616     PetscCall(DMPlexGetCellType(dm, c, &ct));
8617     switch (ct) {
8618       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8619       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8620       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8621         ignoreZeroVol = PETSC_TRUE; break;
8622       default: break;
8623     }
8624     switch (ct) {
8625       case DM_POLYTOPE_TRI_PRISM:
8626       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8627       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8628       case DM_POLYTOPE_PYRAMID:
8629         continue;
8630       default: break;
8631     }
8632     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8633     if (unsplit) continue;
8634     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8635     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);
8636     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ*refVol)));
8637     /* This should work with periodicity since DG coordinates should be used */
8638     if (depth > 1) {
8639       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8640       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);
8641       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double) vol));
8642     }
8643   }
8644   PetscFunctionReturn(0);
8645 }
8646 
8647 /*@
8648   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8649 
8650   Collective
8651 
8652   Input Parameters:
8653 + dm - The DMPlex object
8654 - pointSF - The Point SF, or NULL for Point SF attached to DM
8655 
8656   Notes:
8657   This is mainly intended for debugging/testing purposes.
8658 
8659   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8660 
8661   Level: developer
8662 
8663 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8664 @*/
8665 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF)
8666 {
8667   PetscInt        l, nleaves, nroots, overlap;
8668   const PetscInt *locals;
8669   const PetscSFNode *remotes;
8670   PetscBool       distributed;
8671   MPI_Comm        comm;
8672   PetscMPIInt     rank;
8673 
8674   PetscFunctionBegin;
8675   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8676   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8677   else         pointSF = dm->sf;
8678   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8679   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8680   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8681   {
8682     PetscMPIInt    mpiFlag;
8683 
8684     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF),&mpiFlag));
8685     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)",mpiFlag);
8686   }
8687   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8688   PetscCall(DMPlexIsDistributed(dm, &distributed));
8689   if (!distributed) {
8690     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);
8691     PetscFunctionReturn(0);
8692   }
8693   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);
8694   PetscCall(DMPlexGetOverlap(dm, &overlap));
8695 
8696   /* Check SF graph is compatible with DMPlex chart */
8697   {
8698     PetscInt pStart, pEnd, maxLeaf;
8699 
8700     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8701     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8702     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd-pStart, nroots);
8703     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8704   }
8705 
8706   /* Check Point SF has no local points referenced */
8707   for (l = 0; l < nleaves; l++) {
8708     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);
8709   }
8710 
8711   /* Check there are no cells in interface */
8712   if (!overlap) {
8713     PetscInt cellHeight, cStart, cEnd;
8714 
8715     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8716     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8717     for (l = 0; l < nleaves; ++l) {
8718       const PetscInt point = locals ? locals[l] : l;
8719 
8720       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8721     }
8722   }
8723 
8724   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8725   {
8726     const PetscInt *rootdegree;
8727 
8728     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8729     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8730     for (l = 0; l < nleaves; ++l) {
8731       const PetscInt  point = locals ? locals[l] : l;
8732       const PetscInt *cone;
8733       PetscInt        coneSize, c, idx;
8734 
8735       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8736       PetscCall(DMPlexGetCone(dm, point, &cone));
8737       for (c = 0; c < coneSize; ++c) {
8738         if (!rootdegree[cone[c]]) {
8739           if (locals) {
8740             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8741           } else {
8742             idx = (cone[c] < nleaves) ? cone[c] : -1;
8743           }
8744           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8745         }
8746       }
8747     }
8748   }
8749   PetscFunctionReturn(0);
8750 }
8751 
8752 /*@
8753   DMPlexCheck - Perform various checks of Plex sanity
8754 
8755   Input Parameter:
8756 . dm - The DMPlex object
8757 
8758   Notes:
8759   This is a useful diagnostic when creating meshes programmatically.
8760 
8761   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8762 
8763   Currently does not include DMPlexCheckCellShape().
8764 
8765   Level: developer
8766 
8767 .seealso: DMCreate(), DMSetFromOptions()
8768 @*/
8769 PetscErrorCode DMPlexCheck(DM dm)
8770 {
8771   PetscInt cellHeight;
8772 
8773   PetscFunctionBegin;
8774   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8775   PetscCall(DMPlexCheckSymmetry(dm));
8776   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8777   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8778   PetscCall(DMPlexCheckGeometry(dm));
8779   PetscCall(DMPlexCheckPointSF(dm, NULL));
8780   PetscCall(DMPlexCheckInterfaceCones(dm));
8781   PetscFunctionReturn(0);
8782 }
8783 
8784 typedef struct cell_stats
8785 {
8786   PetscReal min, max, sum, squaresum;
8787   PetscInt  count;
8788 } cell_stats_t;
8789 
8790 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8791 {
8792   PetscInt i, N = *len;
8793 
8794   for (i = 0; i < N; i++) {
8795     cell_stats_t *A = (cell_stats_t *) a;
8796     cell_stats_t *B = (cell_stats_t *) b;
8797 
8798     B->min = PetscMin(A->min,B->min);
8799     B->max = PetscMax(A->max,B->max);
8800     B->sum += A->sum;
8801     B->squaresum += A->squaresum;
8802     B->count += A->count;
8803   }
8804 }
8805 
8806 /*@
8807   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8808 
8809   Collective on dm
8810 
8811   Input Parameters:
8812 + dm        - The DMPlex object
8813 . output    - If true, statistics will be displayed on stdout
8814 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8815 
8816   Notes:
8817   This is mainly intended for debugging/testing purposes.
8818 
8819   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8820 
8821   Level: developer
8822 
8823 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8824 @*/
8825 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8826 {
8827   DM             dmCoarse;
8828   cell_stats_t   stats, globalStats;
8829   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8830   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8831   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8832   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8833   PetscMPIInt    rank,size;
8834 
8835   PetscFunctionBegin;
8836   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8837   stats.min   = PETSC_MAX_REAL;
8838   stats.max   = PETSC_MIN_REAL;
8839   stats.sum   = stats.squaresum = 0.;
8840   stats.count = 0;
8841 
8842   PetscCallMPI(MPI_Comm_size(comm, &size));
8843   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8844   PetscCall(DMGetCoordinateDim(dm,&cdim));
8845   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8846   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8847   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8848   for (c = cStart; c < cEnd; c++) {
8849     PetscInt  i;
8850     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8851 
8852     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8853     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8854     for (i = 0; i < PetscSqr(cdim); ++i) {
8855       frobJ    += J[i] * J[i];
8856       frobInvJ += invJ[i] * invJ[i];
8857     }
8858     cond2 = frobJ * frobInvJ;
8859     cond  = PetscSqrtReal(cond2);
8860 
8861     stats.min        = PetscMin(stats.min,cond);
8862     stats.max        = PetscMax(stats.max,cond);
8863     stats.sum       += cond;
8864     stats.squaresum += cond2;
8865     stats.count++;
8866     if (output && cond > limit) {
8867       PetscSection coordSection;
8868       Vec          coordsLocal;
8869       PetscScalar *coords = NULL;
8870       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8871 
8872       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8873       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8874       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8875       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double) cond));
8876       for (i = 0; i < Nv/cdim; ++i) {
8877         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8878         for (d = 0; d < cdim; ++d) {
8879           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8880           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8881         }
8882         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8883       }
8884       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8885       for (cl = 0; cl < clSize*2; cl += 2) {
8886         const PetscInt edge = closure[cl];
8887 
8888         if ((edge >= eStart) && (edge < eEnd)) {
8889           PetscReal len;
8890 
8891           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8892           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double) len));
8893         }
8894       }
8895       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8896       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8897     }
8898   }
8899   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8900 
8901   if (size > 1) {
8902     PetscMPIInt   blockLengths[2] = {4,1};
8903     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8904     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8905     MPI_Op        statReduce;
8906 
8907     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8908     PetscCallMPI(MPI_Type_commit(&statType));
8909     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8910     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8911     PetscCallMPI(MPI_Op_free(&statReduce));
8912     PetscCallMPI(MPI_Type_free(&statType));
8913   } else {
8914     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8915   }
8916   if (rank == 0) {
8917     count = globalStats.count;
8918     min   = globalStats.min;
8919     max   = globalStats.max;
8920     mean  = globalStats.sum / globalStats.count;
8921     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8922   }
8923 
8924   if (output) {
8925     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));
8926   }
8927   PetscCall(PetscFree2(J,invJ));
8928 
8929   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8930   if (dmCoarse) {
8931     PetscBool isplex;
8932 
8933     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8934     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8935   }
8936   PetscFunctionReturn(0);
8937 }
8938 
8939 /*@
8940   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8941   orthogonal quality below given tolerance.
8942 
8943   Collective on dm
8944 
8945   Input Parameters:
8946 + dm   - The DMPlex object
8947 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8948 - atol - [0, 1] Absolute tolerance for tagging cells.
8949 
8950   Output Parameters:
8951 + OrthQual      - Vec containing orthogonal quality per cell
8952 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8953 
8954   Options Database Keys:
8955 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8956 supported.
8957 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8958 
8959   Notes:
8960   Orthogonal quality is given by the following formula:
8961 
8962   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8963 
8964   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
8965   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8966   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8967   calculating the cosine of the angle between these vectors.
8968 
8969   Orthogonal quality ranges from 1 (best) to 0 (worst).
8970 
8971   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8972   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8973 
8974   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8975 
8976   Level: intermediate
8977 
8978 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8979 @*/
8980 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8981 {
8982   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8983   PetscInt                *idx;
8984   PetscScalar             *oqVals;
8985   const PetscScalar       *cellGeomArr, *faceGeomArr;
8986   PetscReal               *ci, *fi, *Ai;
8987   MPI_Comm                comm;
8988   Vec                     cellgeom, facegeom;
8989   DM                      dmFace, dmCell;
8990   IS                      glob;
8991   ISLocalToGlobalMapping  ltog;
8992   PetscViewer             vwr;
8993 
8994   PetscFunctionBegin;
8995   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8996   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
8997   PetscValidPointer(OrthQual, 4);
8998   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
8999   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
9000   PetscCall(DMGetDimension(dm, &nc));
9001   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9002   {
9003     DMPlexInterpolatedFlag interpFlag;
9004 
9005     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9006     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9007       PetscMPIInt rank;
9008 
9009       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9010       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9011     }
9012   }
9013   if (OrthQualLabel) {
9014     PetscValidPointer(OrthQualLabel, 5);
9015     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9016     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9017   } else {*OrthQualLabel = NULL;}
9018   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9019   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9020   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9021   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9022   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9023   PetscCall(VecCreate(comm, OrthQual));
9024   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9025   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
9026   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9027   PetscCall(VecSetUp(*OrthQual));
9028   PetscCall(ISDestroy(&glob));
9029   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9030   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9031   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9032   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9033   PetscCall(VecGetDM(cellgeom, &dmCell));
9034   PetscCall(VecGetDM(facegeom, &dmFace));
9035   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9036   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
9037     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9038     PetscInt           cellarr[2], *adj = NULL;
9039     PetscScalar        *cArr, *fArr;
9040     PetscReal          minvalc = 1.0, minvalf = 1.0;
9041     PetscFVCellGeom    *cg;
9042 
9043     idx[cellIter] = cell-cStart;
9044     cellarr[0] = cell;
9045     /* Make indexing into cellGeom easier */
9046     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9047     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9048     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9049     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9050     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
9051       PetscInt         i;
9052       const PetscInt   neigh = adj[cellneigh];
9053       PetscReal        normci = 0, normfi = 0, normai = 0;
9054       PetscFVCellGeom  *cgneigh;
9055       PetscFVFaceGeom  *fg;
9056 
9057       /* Don't count ourselves in the neighbor list */
9058       if (neigh == cell) continue;
9059       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9060       cellarr[1] = neigh;
9061       {
9062         PetscInt       numcovpts;
9063         const PetscInt *covpts;
9064 
9065         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9066         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9067         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9068       }
9069 
9070       /* Compute c_i, f_i and their norms */
9071       for (i = 0; i < nc; i++) {
9072         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9073         fi[i] = fg->centroid[i] - cg->centroid[i];
9074         Ai[i] = fg->normal[i];
9075         normci += PetscPowReal(ci[i], 2);
9076         normfi += PetscPowReal(fi[i], 2);
9077         normai += PetscPowReal(Ai[i], 2);
9078       }
9079       normci = PetscSqrtReal(normci);
9080       normfi = PetscSqrtReal(normfi);
9081       normai = PetscSqrtReal(normai);
9082 
9083       /* Normalize and compute for each face-cell-normal pair */
9084       for (i = 0; i < nc; i++) {
9085         ci[i] = ci[i]/normci;
9086         fi[i] = fi[i]/normfi;
9087         Ai[i] = Ai[i]/normai;
9088         /* PetscAbs because I don't know if normals are guaranteed to point out */
9089         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9090         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9091       }
9092       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9093         minvalc = PetscRealPart(cArr[cellneighiter]);
9094       }
9095       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9096         minvalf = PetscRealPart(fArr[cellneighiter]);
9097       }
9098     }
9099     PetscCall(PetscFree(adj));
9100     PetscCall(PetscFree2(cArr, fArr));
9101     /* Defer to cell if they're equal */
9102     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9103     if (OrthQualLabel) {
9104       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9105     }
9106   }
9107   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9108   PetscCall(VecAssemblyBegin(*OrthQual));
9109   PetscCall(VecAssemblyEnd(*OrthQual));
9110   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9111   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9112   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9113   if (OrthQualLabel) {
9114     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9115   }
9116   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9117   PetscCall(PetscViewerDestroy(&vwr));
9118   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9119   PetscFunctionReturn(0);
9120 }
9121 
9122 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9123  * interpolator construction */
9124 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9125 {
9126   PetscSection   section, newSection, gsection;
9127   PetscSF        sf;
9128   PetscBool      hasConstraints, ghasConstraints;
9129 
9130   PetscFunctionBegin;
9131   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9132   PetscValidPointer(odm,2);
9133   PetscCall(DMGetLocalSection(dm, &section));
9134   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9135   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9136   if (!ghasConstraints) {
9137     PetscCall(PetscObjectReference((PetscObject)dm));
9138     *odm = dm;
9139     PetscFunctionReturn(0);
9140   }
9141   PetscCall(DMClone(dm, odm));
9142   PetscCall(DMCopyFields(dm, *odm));
9143   PetscCall(DMGetLocalSection(*odm, &newSection));
9144   PetscCall(DMGetPointSF(*odm, &sf));
9145   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9146   PetscCall(DMSetGlobalSection(*odm, gsection));
9147   PetscCall(PetscSectionDestroy(&gsection));
9148   PetscFunctionReturn(0);
9149 }
9150 
9151 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9152 {
9153   DM             dmco, dmfo;
9154   Mat            interpo;
9155   Vec            rscale;
9156   Vec            cglobalo, clocal;
9157   Vec            fglobal, fglobalo, flocal;
9158   PetscBool      regular;
9159 
9160   PetscFunctionBegin;
9161   PetscCall(DMGetFullDM(dmc, &dmco));
9162   PetscCall(DMGetFullDM(dmf, &dmfo));
9163   PetscCall(DMSetCoarseDM(dmfo, dmco));
9164   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9165   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9166   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9167   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9168   PetscCall(DMCreateLocalVector(dmc, &clocal));
9169   PetscCall(VecSet(cglobalo, 0.));
9170   PetscCall(VecSet(clocal, 0.));
9171   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9172   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9173   PetscCall(DMCreateLocalVector(dmf, &flocal));
9174   PetscCall(VecSet(fglobal, 0.));
9175   PetscCall(VecSet(fglobalo, 0.));
9176   PetscCall(VecSet(flocal, 0.));
9177   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9178   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9179   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9180   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9181   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9182   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9183   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9184   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9185   *shift = fglobal;
9186   PetscCall(VecDestroy(&flocal));
9187   PetscCall(VecDestroy(&fglobalo));
9188   PetscCall(VecDestroy(&clocal));
9189   PetscCall(VecDestroy(&cglobalo));
9190   PetscCall(VecDestroy(&rscale));
9191   PetscCall(MatDestroy(&interpo));
9192   PetscCall(DMDestroy(&dmfo));
9193   PetscCall(DMDestroy(&dmco));
9194   PetscFunctionReturn(0);
9195 }
9196 
9197 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9198 {
9199   PetscObject    shifto;
9200   Vec            shift;
9201 
9202   PetscFunctionBegin;
9203   if (!interp) {
9204     Vec rscale;
9205 
9206     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9207     PetscCall(VecDestroy(&rscale));
9208   } else {
9209     PetscCall(PetscObjectReference((PetscObject)interp));
9210   }
9211   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9212   if (!shifto) {
9213     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9214     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9215     shifto = (PetscObject) shift;
9216     PetscCall(VecDestroy(&shift));
9217   }
9218   shift = (Vec) shifto;
9219   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9220   PetscCall(VecAXPY(fineSol, 1.0, shift));
9221   PetscCall(MatDestroy(&interp));
9222   PetscFunctionReturn(0);
9223 }
9224 
9225 /* Pointwise interpolation
9226      Just code FEM for now
9227      u^f = I u^c
9228      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9229      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9230      I_{ij} = psi^f_i phi^c_j
9231 */
9232 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9233 {
9234   PetscSection   gsc, gsf;
9235   PetscInt       m, n;
9236   void          *ctx;
9237   DM             cdm;
9238   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9239 
9240   PetscFunctionBegin;
9241   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9242   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9243   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9244   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9245 
9246   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9247   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9248   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9249   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9250   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9251 
9252   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9253   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9254   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9255   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9256   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9257   if (scaling) {
9258     /* Use naive scaling */
9259     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9260   }
9261   PetscFunctionReturn(0);
9262 }
9263 
9264 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9265 {
9266   VecScatter     ctx;
9267 
9268   PetscFunctionBegin;
9269   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9270   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9271   PetscCall(VecScatterDestroy(&ctx));
9272   PetscFunctionReturn(0);
9273 }
9274 
9275 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9276                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9277                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9278                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9279 {
9280   const PetscInt Nc = uOff[1] - uOff[0];
9281   PetscInt       c;
9282   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9283 }
9284 
9285 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9286 {
9287   DM             dmc;
9288   PetscDS        ds;
9289   Vec            ones, locmass;
9290   IS             cellIS;
9291   PetscFormKey   key;
9292   PetscInt       depth;
9293 
9294   PetscFunctionBegin;
9295   PetscCall(DMClone(dm, &dmc));
9296   PetscCall(DMCopyDisc(dm, dmc));
9297   PetscCall(DMGetDS(dmc, &ds));
9298   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9299   PetscCall(DMCreateGlobalVector(dmc, mass));
9300   PetscCall(DMGetLocalVector(dmc, &ones));
9301   PetscCall(DMGetLocalVector(dmc, &locmass));
9302   PetscCall(DMPlexGetDepth(dmc, &depth));
9303   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9304   PetscCall(VecSet(locmass, 0.0));
9305   PetscCall(VecSet(ones, 1.0));
9306   key.label = NULL;
9307   key.value = 0;
9308   key.field = 0;
9309   key.part  = 0;
9310   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9311   PetscCall(ISDestroy(&cellIS));
9312   PetscCall(VecSet(*mass, 0.0));
9313   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9314   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9315   PetscCall(DMRestoreLocalVector(dmc, &ones));
9316   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9317   PetscCall(DMDestroy(&dmc));
9318   PetscFunctionReturn(0);
9319 }
9320 
9321 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9322 {
9323   PetscSection   gsc, gsf;
9324   PetscInt       m, n;
9325   void          *ctx;
9326   DM             cdm;
9327   PetscBool      regular;
9328 
9329   PetscFunctionBegin;
9330   if (dmFine == dmCoarse) {
9331     DM            dmc;
9332     PetscDS       ds;
9333     PetscWeakForm wf;
9334     Vec           u;
9335     IS            cellIS;
9336     PetscFormKey  key;
9337     PetscInt      depth;
9338 
9339     PetscCall(DMClone(dmFine, &dmc));
9340     PetscCall(DMCopyDisc(dmFine, dmc));
9341     PetscCall(DMGetDS(dmc, &ds));
9342     PetscCall(PetscDSGetWeakForm(ds, &wf));
9343     PetscCall(PetscWeakFormClear(wf));
9344     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9345     PetscCall(DMCreateMatrix(dmc, mass));
9346     PetscCall(DMGetLocalVector(dmc, &u));
9347     PetscCall(DMPlexGetDepth(dmc, &depth));
9348     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9349     PetscCall(MatZeroEntries(*mass));
9350     key.label = NULL;
9351     key.value = 0;
9352     key.field = 0;
9353     key.part  = 0;
9354     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9355     PetscCall(ISDestroy(&cellIS));
9356     PetscCall(DMRestoreLocalVector(dmc, &u));
9357     PetscCall(DMDestroy(&dmc));
9358   } else {
9359     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9360     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9361     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9362     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9363 
9364     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9365     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9366     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9367     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9368 
9369     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9370     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9371     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9372     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9373   }
9374   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9375   PetscFunctionReturn(0);
9376 }
9377 
9378 /*@
9379   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9380 
9381   Input Parameter:
9382 . dm - The DMPlex object
9383 
9384   Output Parameter:
9385 . regular - The flag
9386 
9387   Level: intermediate
9388 
9389 .seealso: `DMPlexSetRegularRefinement()`
9390 @*/
9391 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9392 {
9393   PetscFunctionBegin;
9394   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9395   PetscValidBoolPointer(regular, 2);
9396   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9397   PetscFunctionReturn(0);
9398 }
9399 
9400 /*@
9401   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9402 
9403   Input Parameters:
9404 + dm - The DMPlex object
9405 - regular - The flag
9406 
9407   Level: intermediate
9408 
9409 .seealso: `DMPlexGetRegularRefinement()`
9410 @*/
9411 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9412 {
9413   PetscFunctionBegin;
9414   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9415   ((DM_Plex *) dm->data)->regularRefinement = regular;
9416   PetscFunctionReturn(0);
9417 }
9418 
9419 /* anchors */
9420 /*@
9421   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9422   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9423 
9424   not collective
9425 
9426   Input Parameter:
9427 . dm - The DMPlex object
9428 
9429   Output Parameters:
9430 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9431 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9432 
9433   Level: intermediate
9434 
9435 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9436 @*/
9437 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9438 {
9439   DM_Plex *plex = (DM_Plex *)dm->data;
9440 
9441   PetscFunctionBegin;
9442   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9443   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9444   if (anchorSection) *anchorSection = plex->anchorSection;
9445   if (anchorIS) *anchorIS = plex->anchorIS;
9446   PetscFunctionReturn(0);
9447 }
9448 
9449 /*@
9450   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9451   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9452   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9453 
9454   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9455   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9456 
9457   collective on dm
9458 
9459   Input Parameters:
9460 + dm - The DMPlex object
9461 . 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).
9462 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9463 
9464   The reference counts of anchorSection and anchorIS are incremented.
9465 
9466   Level: intermediate
9467 
9468 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9469 @*/
9470 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9471 {
9472   DM_Plex        *plex = (DM_Plex *)dm->data;
9473   PetscMPIInt    result;
9474 
9475   PetscFunctionBegin;
9476   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9477   if (anchorSection) {
9478     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9479     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9480     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9481   }
9482   if (anchorIS) {
9483     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9484     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9485     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9486   }
9487 
9488   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9489   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9490   plex->anchorSection = anchorSection;
9491 
9492   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9493   PetscCall(ISDestroy(&plex->anchorIS));
9494   plex->anchorIS = anchorIS;
9495 
9496   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9497     PetscInt size, a, pStart, pEnd;
9498     const PetscInt *anchors;
9499 
9500     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9501     PetscCall(ISGetLocalSize(anchorIS,&size));
9502     PetscCall(ISGetIndices(anchorIS,&anchors));
9503     for (a = 0; a < size; a++) {
9504       PetscInt p;
9505 
9506       p = anchors[a];
9507       if (p >= pStart && p < pEnd) {
9508         PetscInt dof;
9509 
9510         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9511         if (dof) {
9512 
9513           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9514           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %" PetscInt_FMT " cannot be constrained and an anchor",p);
9515         }
9516       }
9517     }
9518     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9519   }
9520   /* reset the generic constraints */
9521   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9522   PetscFunctionReturn(0);
9523 }
9524 
9525 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9526 {
9527   PetscSection anchorSection;
9528   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9529 
9530   PetscFunctionBegin;
9531   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9532   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9533   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9534   PetscCall(PetscSectionGetNumFields(section,&numFields));
9535   if (numFields) {
9536     PetscInt f;
9537     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9538 
9539     for (f = 0; f < numFields; f++) {
9540       PetscInt numComp;
9541 
9542       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9543       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9544     }
9545   }
9546   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9547   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9548   pStart = PetscMax(pStart,sStart);
9549   pEnd   = PetscMin(pEnd,sEnd);
9550   pEnd   = PetscMax(pStart,pEnd);
9551   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9552   for (p = pStart; p < pEnd; p++) {
9553     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9554     if (dof) {
9555       PetscCall(PetscSectionGetDof(section,p,&dof));
9556       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9557       for (f = 0; f < numFields; f++) {
9558         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9559         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9560       }
9561     }
9562   }
9563   PetscCall(PetscSectionSetUp(*cSec));
9564   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9565   PetscFunctionReturn(0);
9566 }
9567 
9568 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9569 {
9570   PetscSection   aSec;
9571   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9572   const PetscInt *anchors;
9573   PetscInt       numFields, f;
9574   IS             aIS;
9575   MatType        mtype;
9576   PetscBool      iscuda,iskokkos;
9577 
9578   PetscFunctionBegin;
9579   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9580   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9581   PetscCall(PetscSectionGetStorageSize(section, &n));
9582   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9583   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9584   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9585   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9586   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9587   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9588   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9589   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9590   else mtype = MATSEQAIJ;
9591   PetscCall(MatSetType(*cMat,mtype));
9592   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9593   PetscCall(ISGetIndices(aIS,&anchors));
9594   /* cSec will be a subset of aSec and section */
9595   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9596   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9597   PetscCall(PetscMalloc1(m+1,&i));
9598   i[0] = 0;
9599   PetscCall(PetscSectionGetNumFields(section,&numFields));
9600   for (p = pStart; p < pEnd; p++) {
9601     PetscInt rDof, rOff, r;
9602 
9603     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9604     if (!rDof) continue;
9605     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9606     if (numFields) {
9607       for (f = 0; f < numFields; f++) {
9608         annz = 0;
9609         for (r = 0; r < rDof; r++) {
9610           a = anchors[rOff + r];
9611           if (a < sStart || a >= sEnd) continue;
9612           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9613           annz += aDof;
9614         }
9615         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9616         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9617         for (q = 0; q < dof; q++) {
9618           i[off + q + 1] = i[off + q] + annz;
9619         }
9620       }
9621     } else {
9622       annz = 0;
9623       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9624       for (q = 0; q < dof; q++) {
9625         a = anchors[rOff + q];
9626         if (a < sStart || a >= sEnd) continue;
9627         PetscCall(PetscSectionGetDof(section,a,&aDof));
9628         annz += aDof;
9629       }
9630       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9631       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9632       for (q = 0; q < dof; q++) {
9633         i[off + q + 1] = i[off + q] + annz;
9634       }
9635     }
9636   }
9637   nnz = i[m];
9638   PetscCall(PetscMalloc1(nnz,&j));
9639   offset = 0;
9640   for (p = pStart; p < pEnd; p++) {
9641     if (numFields) {
9642       for (f = 0; f < numFields; f++) {
9643         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9644         for (q = 0; q < dof; q++) {
9645           PetscInt rDof, rOff, r;
9646           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9647           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9648           for (r = 0; r < rDof; r++) {
9649             PetscInt s;
9650 
9651             a = anchors[rOff + r];
9652             if (a < sStart || a >= sEnd) continue;
9653             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9654             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9655             for (s = 0; s < aDof; s++) {
9656               j[offset++] = aOff + s;
9657             }
9658           }
9659         }
9660       }
9661     } else {
9662       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9663       for (q = 0; q < dof; q++) {
9664         PetscInt rDof, rOff, r;
9665         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9666         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9667         for (r = 0; r < rDof; r++) {
9668           PetscInt s;
9669 
9670           a = anchors[rOff + r];
9671           if (a < sStart || a >= sEnd) continue;
9672           PetscCall(PetscSectionGetDof(section,a,&aDof));
9673           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9674           for (s = 0; s < aDof; s++) {
9675             j[offset++] = aOff + s;
9676           }
9677         }
9678       }
9679     }
9680   }
9681   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9682   PetscCall(PetscFree(i));
9683   PetscCall(PetscFree(j));
9684   PetscCall(ISRestoreIndices(aIS,&anchors));
9685   PetscFunctionReturn(0);
9686 }
9687 
9688 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9689 {
9690   DM_Plex        *plex = (DM_Plex *)dm->data;
9691   PetscSection   anchorSection, section, cSec;
9692   Mat            cMat;
9693 
9694   PetscFunctionBegin;
9695   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9696   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9697   if (anchorSection) {
9698     PetscInt Nf;
9699 
9700     PetscCall(DMGetLocalSection(dm,&section));
9701     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9702     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9703     PetscCall(DMGetNumFields(dm,&Nf));
9704     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9705     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9706     PetscCall(PetscSectionDestroy(&cSec));
9707     PetscCall(MatDestroy(&cMat));
9708   }
9709   PetscFunctionReturn(0);
9710 }
9711 
9712 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9713 {
9714   IS             subis;
9715   PetscSection   section, subsection;
9716 
9717   PetscFunctionBegin;
9718   PetscCall(DMGetLocalSection(dm, &section));
9719   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9720   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9721   /* Create subdomain */
9722   PetscCall(DMPlexFilter(dm, label, value, subdm));
9723   /* Create submodel */
9724   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9725   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9726   PetscCall(DMSetLocalSection(*subdm, subsection));
9727   PetscCall(PetscSectionDestroy(&subsection));
9728   PetscCall(DMCopyDisc(dm, *subdm));
9729   /* Create map from submodel to global model */
9730   if (is) {
9731     PetscSection    sectionGlobal, subsectionGlobal;
9732     IS              spIS;
9733     const PetscInt *spmap;
9734     PetscInt       *subIndices;
9735     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9736     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9737 
9738     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9739     PetscCall(ISGetIndices(spIS, &spmap));
9740     PetscCall(PetscSectionGetNumFields(section, &Nf));
9741     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9742     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9743     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9744     for (p = pStart; p < pEnd; ++p) {
9745       PetscInt gdof, pSubSize  = 0;
9746 
9747       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9748       if (gdof > 0) {
9749         for (f = 0; f < Nf; ++f) {
9750           PetscInt fdof, fcdof;
9751 
9752           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9753           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9754           pSubSize += fdof-fcdof;
9755         }
9756         subSize += pSubSize;
9757         if (pSubSize) {
9758           if (bs < 0) {
9759             bs = pSubSize;
9760           } else if (bs != pSubSize) {
9761             /* Layout does not admit a pointwise block size */
9762             bs = 1;
9763           }
9764         }
9765       }
9766     }
9767     /* Must have same blocksize on all procs (some might have no points) */
9768     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9769     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9770     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9771     else                            {bs = bsMinMax[0];}
9772     PetscCall(PetscMalloc1(subSize, &subIndices));
9773     for (p = pStart; p < pEnd; ++p) {
9774       PetscInt gdof, goff;
9775 
9776       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9777       if (gdof > 0) {
9778         const PetscInt point = spmap[p];
9779 
9780         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9781         for (f = 0; f < Nf; ++f) {
9782           PetscInt fdof, fcdof, fc, f2, poff = 0;
9783 
9784           /* Can get rid of this loop by storing field information in the global section */
9785           for (f2 = 0; f2 < f; ++f2) {
9786             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9787             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9788             poff += fdof-fcdof;
9789           }
9790           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9791           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9792           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9793             subIndices[subOff] = goff+poff+fc;
9794           }
9795         }
9796       }
9797     }
9798     PetscCall(ISRestoreIndices(spIS, &spmap));
9799     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9800     if (bs > 1) {
9801       /* We need to check that the block size does not come from non-contiguous fields */
9802       PetscInt i, j, set = 1;
9803       for (i = 0; i < subSize; i += bs) {
9804         for (j = 0; j < bs; ++j) {
9805           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9806         }
9807       }
9808       if (set) PetscCall(ISSetBlockSize(*is, bs));
9809     }
9810     /* Attach nullspace */
9811     for (f = 0; f < Nf; ++f) {
9812       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9813       if ((*subdm)->nullspaceConstructors[f]) break;
9814     }
9815     if (f < Nf) {
9816       MatNullSpace nullSpace;
9817       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9818 
9819       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9820       PetscCall(MatNullSpaceDestroy(&nullSpace));
9821     }
9822   }
9823   PetscFunctionReturn(0);
9824 }
9825 
9826 /*@
9827   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9828 
9829   Input Parameter:
9830 - dm - The DM
9831 
9832   Level: developer
9833 
9834   Options Database Keys:
9835 . -dm_plex_monitor_throughput - Activate the monitor
9836 
9837 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9838 @*/
9839 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9840 {
9841 #if defined(PETSC_USE_LOG)
9842   PetscStageLog      stageLog;
9843   PetscLogEvent      event;
9844   PetscLogStage      stage;
9845   PetscEventPerfInfo eventInfo;
9846   PetscReal          cellRate, flopRate;
9847   PetscInt           cStart, cEnd, Nf, N;
9848   const char        *name;
9849 #endif
9850 
9851   PetscFunctionBegin;
9852   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9853 #if defined(PETSC_USE_LOG)
9854   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9855   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9856   PetscCall(DMGetNumFields(dm, &Nf));
9857   PetscCall(PetscLogGetStageLog(&stageLog));
9858   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9859   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9860   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9861   N        = (cEnd - cStart)*Nf*eventInfo.count;
9862   flopRate = eventInfo.flops/eventInfo.time;
9863   cellRate = N/eventInfo.time;
9864   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)));
9865 #else
9866   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9867 #endif
9868   PetscFunctionReturn(0);
9869 }
9870