xref: /petsc/src/dm/impls/plex/plex.c (revision 695799ffa2f12317ee5880f0f106bd0968664309)
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, *vArray;
5527   const PetscInt *cone, *coneO;
5528   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
5529 
5530   PetscFunctionBeginHot;
5531   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5532   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5533   PetscCall(DMPlexGetCone(dm, point, &cone));
5534   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5535   if (!values || !*values) {
5536     if ((point >= pStart) && (point < pEnd)) {
5537       PetscInt dof;
5538 
5539       PetscCall(PetscSectionGetDof(section, point, &dof));
5540       size += dof;
5541     }
5542     for (p = 0; p < numPoints; ++p) {
5543       const PetscInt cp = cone[p];
5544       PetscInt       dof;
5545 
5546       if ((cp < pStart) || (cp >= pEnd)) continue;
5547       PetscCall(PetscSectionGetDof(section, cp, &dof));
5548       size += dof;
5549     }
5550     if (!values) {
5551       if (csize) *csize = size;
5552       PetscFunctionReturn(0);
5553     }
5554     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5555   } else {
5556     array = *values;
5557   }
5558   size = 0;
5559   PetscCall(VecGetArray(v, &vArray));
5560   if ((point >= pStart) && (point < pEnd)) {
5561     PetscInt     dof, off, d;
5562     PetscScalar *varr;
5563 
5564     PetscCall(PetscSectionGetDof(section, point, &dof));
5565     PetscCall(PetscSectionGetOffset(section, point, &off));
5566     varr = &vArray[off];
5567     for (d = 0; d < dof; ++d, ++offset) {
5568       array[offset] = varr[d];
5569     }
5570     size += dof;
5571   }
5572   for (p = 0; p < numPoints; ++p) {
5573     const PetscInt cp = cone[p];
5574     PetscInt       o  = coneO[p];
5575     PetscInt       dof, off, d;
5576     PetscScalar   *varr;
5577 
5578     if ((cp < pStart) || (cp >= pEnd)) continue;
5579     PetscCall(PetscSectionGetDof(section, cp, &dof));
5580     PetscCall(PetscSectionGetOffset(section, cp, &off));
5581     varr = &vArray[off];
5582     if (o >= 0) {
5583       for (d = 0; d < dof; ++d, ++offset) {
5584         array[offset] = varr[d];
5585       }
5586     } else {
5587       for (d = dof-1; d >= 0; --d, ++offset) {
5588         array[offset] = varr[d];
5589       }
5590     }
5591     size += dof;
5592   }
5593   PetscCall(VecRestoreArray(v, &vArray));
5594   if (!*values) {
5595     if (csize) *csize = size;
5596     *values = array;
5597   } else {
5598     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5599     *csize = size;
5600   }
5601   PetscFunctionReturn(0);
5602 }
5603 
5604 /* Compress out points not in the section */
5605 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5606 {
5607   const PetscInt np = *numPoints;
5608   PetscInt       pStart, pEnd, p, q;
5609 
5610   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5611   for (p = 0, q = 0; p < np; ++p) {
5612     const PetscInt r = points[p*2];
5613     if ((r >= pStart) && (r < pEnd)) {
5614       points[q*2]   = r;
5615       points[q*2+1] = points[p*2+1];
5616       ++q;
5617     }
5618   }
5619   *numPoints = q;
5620   return 0;
5621 }
5622 
5623 /* Compressed closure does not apply closure permutation */
5624 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5625 {
5626   const PetscInt *cla = NULL;
5627   PetscInt       np, *pts = NULL;
5628 
5629   PetscFunctionBeginHot;
5630   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints));
5631   if (*clPoints) {
5632     PetscInt dof, off;
5633 
5634     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5635     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5636     PetscCall(ISGetIndices(*clPoints, &cla));
5637     np   = dof/2;
5638     pts  = (PetscInt *) &cla[off];
5639   } else {
5640     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5641     PetscCall(CompressPoints_Private(section, &np, pts));
5642   }
5643   *numPoints = np;
5644   *points    = pts;
5645   *clp       = cla;
5646   PetscFunctionReturn(0);
5647 }
5648 
5649 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5650 {
5651   PetscFunctionBeginHot;
5652   if (!*clPoints) {
5653     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5654   } else {
5655     PetscCall(ISRestoreIndices(*clPoints, clp));
5656   }
5657   *numPoints = 0;
5658   *points    = NULL;
5659   *clSec     = NULL;
5660   *clPoints  = NULL;
5661   *clp       = NULL;
5662   PetscFunctionReturn(0);
5663 }
5664 
5665 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5666 {
5667   PetscInt          offset = 0, p;
5668   const PetscInt    **perms = NULL;
5669   const PetscScalar **flips = NULL;
5670 
5671   PetscFunctionBeginHot;
5672   *size = 0;
5673   PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
5674   for (p = 0; p < numPoints; p++) {
5675     const PetscInt    point = points[2*p];
5676     const PetscInt    *perm = perms ? perms[p] : NULL;
5677     const PetscScalar *flip = flips ? flips[p] : NULL;
5678     PetscInt          dof, off, d;
5679     const PetscScalar *varr;
5680 
5681     PetscCall(PetscSectionGetDof(section, point, &dof));
5682     PetscCall(PetscSectionGetOffset(section, point, &off));
5683     varr = &vArray[off];
5684     if (clperm) {
5685       if (perm) {
5686         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5687       } else {
5688         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5689       }
5690       if (flip) {
5691         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5692       }
5693     } else {
5694       if (perm) {
5695         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5696       } else {
5697         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5698       }
5699       if (flip) {
5700         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5701       }
5702     }
5703     offset += dof;
5704   }
5705   PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
5706   *size = offset;
5707   PetscFunctionReturn(0);
5708 }
5709 
5710 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[])
5711 {
5712   PetscInt          offset = 0, f;
5713 
5714   PetscFunctionBeginHot;
5715   *size = 0;
5716   for (f = 0; f < numFields; ++f) {
5717     PetscInt          p;
5718     const PetscInt    **perms = NULL;
5719     const PetscScalar **flips = NULL;
5720 
5721     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5722     for (p = 0; p < numPoints; p++) {
5723       const PetscInt    point = points[2*p];
5724       PetscInt          fdof, foff, b;
5725       const PetscScalar *varr;
5726       const PetscInt    *perm = perms ? perms[p] : NULL;
5727       const PetscScalar *flip = flips ? flips[p] : NULL;
5728 
5729       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5730       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5731       varr = &vArray[foff];
5732       if (clperm) {
5733         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5734         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5735         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5736       } else {
5737         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5738         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5739         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5740       }
5741       offset += fdof;
5742     }
5743     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5744   }
5745   *size = offset;
5746   PetscFunctionReturn(0);
5747 }
5748 
5749 /*@C
5750   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5751 
5752   Not collective
5753 
5754   Input Parameters:
5755 + dm - The DM
5756 . section - The section describing the layout in v, or NULL to use the default section
5757 . v - The local vector
5758 - point - The point in the DM
5759 
5760   Input/Output Parameters:
5761 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5762 - values - An array to use for the values, or NULL to have it allocated automatically;
5763            if the user provided NULL, it is a borrowed array and should not be freed
5764 
5765 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5766 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5767 $ assembly function, and a user may already have allocated storage for this operation.
5768 $
5769 $ A typical use could be
5770 $
5771 $  values = NULL;
5772 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5773 $  for (cl = 0; cl < clSize; ++cl) {
5774 $    <Compute on closure>
5775 $  }
5776 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5777 $
5778 $ or
5779 $
5780 $  PetscMalloc1(clMaxSize, &values);
5781 $  for (p = pStart; p < pEnd; ++p) {
5782 $    clSize = clMaxSize;
5783 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5784 $    for (cl = 0; cl < clSize; ++cl) {
5785 $      <Compute on closure>
5786 $    }
5787 $  }
5788 $  PetscFree(values);
5789 
5790   Fortran Notes:
5791   Since it returns an array, this routine is only available in Fortran 90, and you must
5792   include petsc.h90 in your code.
5793 
5794   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5795 
5796   Level: intermediate
5797 
5798 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5799 @*/
5800 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5801 {
5802   PetscSection       clSection;
5803   IS                 clPoints;
5804   PetscInt          *points = NULL;
5805   const PetscInt    *clp, *perm;
5806   PetscInt           depth, numFields, numPoints, asize;
5807 
5808   PetscFunctionBeginHot;
5809   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5810   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5811   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5812   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5813   PetscCall(DMPlexGetDepth(dm, &depth));
5814   PetscCall(PetscSectionGetNumFields(section, &numFields));
5815   if (depth == 1 && numFields < 2) {
5816     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5817     PetscFunctionReturn(0);
5818   }
5819   /* Get points */
5820   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5821   /* Get sizes */
5822   asize = 0;
5823   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5824     PetscInt dof;
5825     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5826     asize += dof;
5827   }
5828   if (values) {
5829     const PetscScalar *vArray;
5830     PetscInt          size;
5831 
5832     if (*values) {
5833       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);
5834     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5835     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm));
5836     PetscCall(VecGetArrayRead(v, &vArray));
5837     /* Get values */
5838     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5839     else               PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5840     PetscCheck(asize == size,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5841     /* Cleanup array */
5842     PetscCall(VecRestoreArrayRead(v, &vArray));
5843   }
5844   if (csize) *csize = asize;
5845   /* Cleanup points */
5846   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5847   PetscFunctionReturn(0);
5848 }
5849 
5850 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5851 {
5852   DMLabel            depthLabel;
5853   PetscSection       clSection;
5854   IS                 clPoints;
5855   PetscScalar       *array;
5856   const PetscScalar *vArray;
5857   PetscInt          *points = NULL;
5858   const PetscInt    *clp, *perm = NULL;
5859   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5860 
5861   PetscFunctionBeginHot;
5862   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5863   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5864   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5865   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5866   PetscCall(DMPlexGetDepth(dm, &mdepth));
5867   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5868   PetscCall(PetscSectionGetNumFields(section, &numFields));
5869   if (mdepth == 1 && numFields < 2) {
5870     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5871     PetscFunctionReturn(0);
5872   }
5873   /* Get points */
5874   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5875   for (clsize=0,p=0; p<Np; p++) {
5876     PetscInt dof;
5877     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
5878     clsize += dof;
5879   }
5880   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm));
5881   /* Filter points */
5882   for (p = 0; p < numPoints*2; p += 2) {
5883     PetscInt dep;
5884 
5885     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5886     if (dep != depth) continue;
5887     points[Np*2+0] = points[p];
5888     points[Np*2+1] = points[p+1];
5889     ++Np;
5890   }
5891   /* Get array */
5892   if (!values || !*values) {
5893     PetscInt asize = 0, dof;
5894 
5895     for (p = 0; p < Np*2; p += 2) {
5896       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5897       asize += dof;
5898     }
5899     if (!values) {
5900       PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5901       if (csize) *csize = asize;
5902       PetscFunctionReturn(0);
5903     }
5904     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5905   } else {
5906     array = *values;
5907   }
5908   PetscCall(VecGetArrayRead(v, &vArray));
5909   /* Get values */
5910   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5911   else               PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5912   /* Cleanup points */
5913   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5914   /* Cleanup array */
5915   PetscCall(VecRestoreArrayRead(v, &vArray));
5916   if (!*values) {
5917     if (csize) *csize = size;
5918     *values = array;
5919   } else {
5920     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5921     *csize = size;
5922   }
5923   PetscFunctionReturn(0);
5924 }
5925 
5926 /*@C
5927   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5928 
5929   Not collective
5930 
5931   Input Parameters:
5932 + dm - The DM
5933 . section - The section describing the layout in v, or NULL to use the default section
5934 . v - The local vector
5935 . point - The point in the DM
5936 . csize - The number of values in the closure, or NULL
5937 - values - The array of values, which is a borrowed array and should not be freed
5938 
5939   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5940 
5941   Fortran Notes:
5942   Since it returns an array, this routine is only available in Fortran 90, and you must
5943   include petsc.h90 in your code.
5944 
5945   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5946 
5947   Level: intermediate
5948 
5949 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5950 @*/
5951 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5952 {
5953   PetscInt       size = 0;
5954 
5955   PetscFunctionBegin;
5956   /* Should work without recalculating size */
5957   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values));
5958   *values = NULL;
5959   PetscFunctionReturn(0);
5960 }
5961 
5962 static inline void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5963 static inline void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5964 
5965 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[])
5966 {
5967   PetscInt        cdof;   /* The number of constraints on this point */
5968   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5969   PetscScalar    *a;
5970   PetscInt        off, cind = 0, k;
5971 
5972   PetscFunctionBegin;
5973   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5974   PetscCall(PetscSectionGetOffset(section, point, &off));
5975   a    = &array[off];
5976   if (!cdof || setBC) {
5977     if (clperm) {
5978       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5979       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5980     } else {
5981       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5982       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5983     }
5984   } else {
5985     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5986     if (clperm) {
5987       if (perm) {for (k = 0; k < dof; ++k) {
5988           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5989           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5990         }
5991       } else {
5992         for (k = 0; k < dof; ++k) {
5993           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5994           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5995         }
5996       }
5997     } else {
5998       if (perm) {
5999         for (k = 0; k < dof; ++k) {
6000           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6001           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6002         }
6003       } else {
6004         for (k = 0; k < dof; ++k) {
6005           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6006           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6007         }
6008       }
6009     }
6010   }
6011   PetscFunctionReturn(0);
6012 }
6013 
6014 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[])
6015 {
6016   PetscInt        cdof;   /* The number of constraints on this point */
6017   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6018   PetscScalar    *a;
6019   PetscInt        off, cind = 0, k;
6020 
6021   PetscFunctionBegin;
6022   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6023   PetscCall(PetscSectionGetOffset(section, point, &off));
6024   a    = &array[off];
6025   if (cdof) {
6026     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6027     if (clperm) {
6028       if (perm) {
6029         for (k = 0; k < dof; ++k) {
6030           if ((cind < cdof) && (k == cdofs[cind])) {
6031             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
6032             cind++;
6033           }
6034         }
6035       } else {
6036         for (k = 0; k < dof; ++k) {
6037           if ((cind < cdof) && (k == cdofs[cind])) {
6038             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
6039             cind++;
6040           }
6041         }
6042       }
6043     } else {
6044       if (perm) {
6045         for (k = 0; k < dof; ++k) {
6046           if ((cind < cdof) && (k == cdofs[cind])) {
6047             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6048             cind++;
6049           }
6050         }
6051       } else {
6052         for (k = 0; k < dof; ++k) {
6053           if ((cind < cdof) && (k == cdofs[cind])) {
6054             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6055             cind++;
6056           }
6057         }
6058       }
6059     }
6060   }
6061   PetscFunctionReturn(0);
6062 }
6063 
6064 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[])
6065 {
6066   PetscScalar    *a;
6067   PetscInt        fdof, foff, fcdof, foffset = *offset;
6068   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6069   PetscInt        cind = 0, b;
6070 
6071   PetscFunctionBegin;
6072   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6073   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6074   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6075   a    = &array[foff];
6076   if (!fcdof || setBC) {
6077     if (clperm) {
6078       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
6079       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
6080     } else {
6081       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
6082       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
6083     }
6084   } else {
6085     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6086     if (clperm) {
6087       if (perm) {
6088         for (b = 0; b < fdof; b++) {
6089           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6090           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6091         }
6092       } else {
6093         for (b = 0; b < fdof; b++) {
6094           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6095           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6096         }
6097       }
6098     } else {
6099       if (perm) {
6100         for (b = 0; b < fdof; b++) {
6101           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6102           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6103         }
6104       } else {
6105         for (b = 0; b < fdof; b++) {
6106           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6107           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6108         }
6109       }
6110     }
6111   }
6112   *offset += fdof;
6113   PetscFunctionReturn(0);
6114 }
6115 
6116 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[])
6117 {
6118   PetscScalar    *a;
6119   PetscInt        fdof, foff, fcdof, foffset = *offset;
6120   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6121   PetscInt        Nc, cind = 0, ncind = 0, b;
6122   PetscBool       ncSet, fcSet;
6123 
6124   PetscFunctionBegin;
6125   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6126   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6127   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6128   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6129   a    = &array[foff];
6130   if (fcdof) {
6131     /* We just override fcdof and fcdofs with Ncc and comps */
6132     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6133     if (clperm) {
6134       if (perm) {
6135         if (comps) {
6136           for (b = 0; b < fdof; b++) {
6137             ncSet = fcSet = PETSC_FALSE;
6138             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6139             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6140             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
6141           }
6142         } else {
6143           for (b = 0; b < fdof; b++) {
6144             if ((cind < fcdof) && (b == fcdofs[cind])) {
6145               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6146               ++cind;
6147             }
6148           }
6149         }
6150       } else {
6151         if (comps) {
6152           for (b = 0; b < fdof; b++) {
6153             ncSet = fcSet = PETSC_FALSE;
6154             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6155             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6156             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
6157           }
6158         } else {
6159           for (b = 0; b < fdof; b++) {
6160             if ((cind < fcdof) && (b == fcdofs[cind])) {
6161               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6162               ++cind;
6163             }
6164           }
6165         }
6166       }
6167     } else {
6168       if (perm) {
6169         if (comps) {
6170           for (b = 0; b < fdof; b++) {
6171             ncSet = fcSet = PETSC_FALSE;
6172             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6173             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6174             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
6175           }
6176         } else {
6177           for (b = 0; b < fdof; b++) {
6178             if ((cind < fcdof) && (b == fcdofs[cind])) {
6179               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6180               ++cind;
6181             }
6182           }
6183         }
6184       } else {
6185         if (comps) {
6186           for (b = 0; b < fdof; b++) {
6187             ncSet = fcSet = PETSC_FALSE;
6188             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6189             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6190             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
6191           }
6192         } else {
6193           for (b = 0; b < fdof; b++) {
6194             if ((cind < fcdof) && (b == fcdofs[cind])) {
6195               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6196               ++cind;
6197             }
6198           }
6199         }
6200       }
6201     }
6202   }
6203   *offset += fdof;
6204   PetscFunctionReturn(0);
6205 }
6206 
6207 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6208 {
6209   PetscScalar    *array;
6210   const PetscInt *cone, *coneO;
6211   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6212 
6213   PetscFunctionBeginHot;
6214   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6215   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6216   PetscCall(DMPlexGetCone(dm, point, &cone));
6217   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6218   PetscCall(VecGetArray(v, &array));
6219   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6220     const PetscInt cp = !p ? point : cone[p-1];
6221     const PetscInt o  = !p ? 0     : coneO[p-1];
6222 
6223     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
6224     PetscCall(PetscSectionGetDof(section, cp, &dof));
6225     /* ADD_VALUES */
6226     {
6227       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6228       PetscScalar    *a;
6229       PetscInt        cdof, coff, cind = 0, k;
6230 
6231       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6232       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6233       a    = &array[coff];
6234       if (!cdof) {
6235         if (o >= 0) {
6236           for (k = 0; k < dof; ++k) {
6237             a[k] += values[off+k];
6238           }
6239         } else {
6240           for (k = 0; k < dof; ++k) {
6241             a[k] += values[off+dof-k-1];
6242           }
6243         }
6244       } else {
6245         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6246         if (o >= 0) {
6247           for (k = 0; k < dof; ++k) {
6248             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6249             a[k] += values[off+k];
6250           }
6251         } else {
6252           for (k = 0; k < dof; ++k) {
6253             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6254             a[k] += values[off+dof-k-1];
6255           }
6256         }
6257       }
6258     }
6259   }
6260   PetscCall(VecRestoreArray(v, &array));
6261   PetscFunctionReturn(0);
6262 }
6263 
6264 /*@C
6265   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6266 
6267   Not collective
6268 
6269   Input Parameters:
6270 + dm - The DM
6271 . section - The section describing the layout in v, or NULL to use the default section
6272 . v - The local vector
6273 . point - The point in the DM
6274 . values - The array of values
6275 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6276          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6277 
6278   Fortran Notes:
6279   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6280 
6281   Level: intermediate
6282 
6283 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6284 @*/
6285 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6286 {
6287   PetscSection    clSection;
6288   IS              clPoints;
6289   PetscScalar    *array;
6290   PetscInt       *points = NULL;
6291   const PetscInt *clp, *clperm = NULL;
6292   PetscInt        depth, numFields, numPoints, p, clsize;
6293 
6294   PetscFunctionBeginHot;
6295   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6296   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6297   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6298   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6299   PetscCall(DMPlexGetDepth(dm, &depth));
6300   PetscCall(PetscSectionGetNumFields(section, &numFields));
6301   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6302     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6303     PetscFunctionReturn(0);
6304   }
6305   /* Get points */
6306   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6307   for (clsize=0,p=0; p<numPoints; p++) {
6308     PetscInt dof;
6309     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
6310     clsize += dof;
6311   }
6312   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
6313   /* Get array */
6314   PetscCall(VecGetArray(v, &array));
6315   /* Get values */
6316   if (numFields > 0) {
6317     PetscInt offset = 0, f;
6318     for (f = 0; f < numFields; ++f) {
6319       const PetscInt    **perms = NULL;
6320       const PetscScalar **flips = NULL;
6321 
6322       PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6323       switch (mode) {
6324       case INSERT_VALUES:
6325         for (p = 0; p < numPoints; p++) {
6326           const PetscInt    point = points[2*p];
6327           const PetscInt    *perm = perms ? perms[p] : NULL;
6328           const PetscScalar *flip = flips ? flips[p] : NULL;
6329           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6330         } break;
6331       case INSERT_ALL_VALUES:
6332         for (p = 0; p < numPoints; p++) {
6333           const PetscInt    point = points[2*p];
6334           const PetscInt    *perm = perms ? perms[p] : NULL;
6335           const PetscScalar *flip = flips ? flips[p] : NULL;
6336           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6337         } break;
6338       case INSERT_BC_VALUES:
6339         for (p = 0; p < numPoints; p++) {
6340           const PetscInt    point = points[2*p];
6341           const PetscInt    *perm = perms ? perms[p] : NULL;
6342           const PetscScalar *flip = flips ? flips[p] : NULL;
6343           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6344         } break;
6345       case ADD_VALUES:
6346         for (p = 0; p < numPoints; p++) {
6347           const PetscInt    point = points[2*p];
6348           const PetscInt    *perm = perms ? perms[p] : NULL;
6349           const PetscScalar *flip = flips ? flips[p] : NULL;
6350           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6351         } break;
6352       case ADD_ALL_VALUES:
6353         for (p = 0; p < numPoints; p++) {
6354           const PetscInt    point = points[2*p];
6355           const PetscInt    *perm = perms ? perms[p] : NULL;
6356           const PetscScalar *flip = flips ? flips[p] : NULL;
6357           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6358         } break;
6359       case ADD_BC_VALUES:
6360         for (p = 0; p < numPoints; p++) {
6361           const PetscInt    point = points[2*p];
6362           const PetscInt    *perm = perms ? perms[p] : NULL;
6363           const PetscScalar *flip = flips ? flips[p] : NULL;
6364           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6365         } break;
6366       default:
6367         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6368       }
6369       PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6370     }
6371   } else {
6372     PetscInt dof, off;
6373     const PetscInt    **perms = NULL;
6374     const PetscScalar **flips = NULL;
6375 
6376     PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
6377     switch (mode) {
6378     case INSERT_VALUES:
6379       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6380         const PetscInt    point = points[2*p];
6381         const PetscInt    *perm = perms ? perms[p] : NULL;
6382         const PetscScalar *flip = flips ? flips[p] : NULL;
6383         PetscCall(PetscSectionGetDof(section, point, &dof));
6384         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6385       } break;
6386     case INSERT_ALL_VALUES:
6387       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6388         const PetscInt    point = points[2*p];
6389         const PetscInt    *perm = perms ? perms[p] : NULL;
6390         const PetscScalar *flip = flips ? flips[p] : NULL;
6391         PetscCall(PetscSectionGetDof(section, point, &dof));
6392         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
6393       } break;
6394     case INSERT_BC_VALUES:
6395       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6396         const PetscInt    point = points[2*p];
6397         const PetscInt    *perm = perms ? perms[p] : NULL;
6398         const PetscScalar *flip = flips ? flips[p] : NULL;
6399         PetscCall(PetscSectionGetDof(section, point, &dof));
6400         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
6401       } break;
6402     case ADD_VALUES:
6403       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6404         const PetscInt    point = points[2*p];
6405         const PetscInt    *perm = perms ? perms[p] : NULL;
6406         const PetscScalar *flip = flips ? flips[p] : NULL;
6407         PetscCall(PetscSectionGetDof(section, point, &dof));
6408         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
6409       } break;
6410     case ADD_ALL_VALUES:
6411       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6412         const PetscInt    point = points[2*p];
6413         const PetscInt    *perm = perms ? perms[p] : NULL;
6414         const PetscScalar *flip = flips ? flips[p] : NULL;
6415         PetscCall(PetscSectionGetDof(section, point, &dof));
6416         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
6417       } break;
6418     case ADD_BC_VALUES:
6419       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6420         const PetscInt    point = points[2*p];
6421         const PetscInt    *perm = perms ? perms[p] : NULL;
6422         const PetscScalar *flip = flips ? flips[p] : NULL;
6423         PetscCall(PetscSectionGetDof(section, point, &dof));
6424         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
6425       } break;
6426     default:
6427       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6428     }
6429     PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
6430   }
6431   /* Cleanup points */
6432   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6433   /* Cleanup array */
6434   PetscCall(VecRestoreArray(v, &array));
6435   PetscFunctionReturn(0);
6436 }
6437 
6438 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6439 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
6440 {
6441   PetscFunctionBegin;
6442   if (label) {
6443     PetscBool contains;
6444     PetscInt  fdof;
6445 
6446     PetscCall(DMLabelStratumHasPoint(label, labelId, point, &contains));
6447     if (!contains) {
6448       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6449       *offset += fdof;
6450       PetscFunctionReturn(1);
6451     }
6452   }
6453   PetscFunctionReturn(0);
6454 }
6455 
6456 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6457 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)
6458 {
6459   PetscSection    clSection;
6460   IS              clPoints;
6461   PetscScalar    *array;
6462   PetscInt       *points = NULL;
6463   const PetscInt *clp;
6464   PetscInt        numFields, numPoints, p;
6465   PetscInt        offset = 0, f;
6466 
6467   PetscFunctionBeginHot;
6468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6469   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6470   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6471   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6472   PetscCall(PetscSectionGetNumFields(section, &numFields));
6473   /* Get points */
6474   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6475   /* Get array */
6476   PetscCall(VecGetArray(v, &array));
6477   /* Get values */
6478   for (f = 0; f < numFields; ++f) {
6479     const PetscInt    **perms = NULL;
6480     const PetscScalar **flips = NULL;
6481 
6482     if (!fieldActive[f]) {
6483       for (p = 0; p < numPoints*2; p += 2) {
6484         PetscInt fdof;
6485         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6486         offset += fdof;
6487       }
6488       continue;
6489     }
6490     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6491     switch (mode) {
6492     case INSERT_VALUES:
6493       for (p = 0; p < numPoints; p++) {
6494         const PetscInt    point = points[2*p];
6495         const PetscInt    *perm = perms ? perms[p] : NULL;
6496         const PetscScalar *flip = flips ? flips[p] : NULL;
6497         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6498         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6499       } break;
6500     case INSERT_ALL_VALUES:
6501       for (p = 0; p < numPoints; p++) {
6502         const PetscInt    point = points[2*p];
6503         const PetscInt    *perm = perms ? perms[p] : NULL;
6504         const PetscScalar *flip = flips ? flips[p] : NULL;
6505         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6506         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6507       } break;
6508     case INSERT_BC_VALUES:
6509       for (p = 0; p < numPoints; p++) {
6510         const PetscInt    point = points[2*p];
6511         const PetscInt    *perm = perms ? perms[p] : NULL;
6512         const PetscScalar *flip = flips ? flips[p] : NULL;
6513         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6514         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6515       } break;
6516     case ADD_VALUES:
6517       for (p = 0; p < numPoints; p++) {
6518         const PetscInt    point = points[2*p];
6519         const PetscInt    *perm = perms ? perms[p] : NULL;
6520         const PetscScalar *flip = flips ? flips[p] : NULL;
6521         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6522         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6523       } break;
6524     case ADD_ALL_VALUES:
6525       for (p = 0; p < numPoints; p++) {
6526         const PetscInt    point = points[2*p];
6527         const PetscInt    *perm = perms ? perms[p] : NULL;
6528         const PetscScalar *flip = flips ? flips[p] : NULL;
6529         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6530         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6531       } break;
6532     default:
6533       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6534     }
6535     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6536   }
6537   /* Cleanup points */
6538   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6539   /* Cleanup array */
6540   PetscCall(VecRestoreArray(v, &array));
6541   PetscFunctionReturn(0);
6542 }
6543 
6544 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6545 {
6546   PetscMPIInt    rank;
6547   PetscInt       i, j;
6548 
6549   PetscFunctionBegin;
6550   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6551   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6552   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6553   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6554   numCIndices = numCIndices ? numCIndices : numRIndices;
6555   if (!values) PetscFunctionReturn(0);
6556   for (i = 0; i < numRIndices; i++) {
6557     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6558     for (j = 0; j < numCIndices; j++) {
6559 #if defined(PETSC_USE_COMPLEX)
6560       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j])));
6561 #else
6562       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]));
6563 #endif
6564     }
6565     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6566   }
6567   PetscFunctionReturn(0);
6568 }
6569 
6570 /*
6571   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6572 
6573   Input Parameters:
6574 + section - The section for this data layout
6575 . islocal - Is the section (and thus indices being requested) local or global?
6576 . point   - The point contributing dofs with these indices
6577 . off     - The global offset of this point
6578 . loff    - The local offset of each field
6579 . setBC   - The flag determining whether to include indices of boundary values
6580 . perm    - A permutation of the dofs on this point, or NULL
6581 - indperm - A permutation of the entire indices array, or NULL
6582 
6583   Output Parameter:
6584 . indices - Indices for dofs on this point
6585 
6586   Level: developer
6587 
6588   Note: The indices could be local or global, depending on the value of 'off'.
6589 */
6590 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6591 {
6592   PetscInt        dof;   /* The number of unknowns on this point */
6593   PetscInt        cdof;  /* The number of constraints on this point */
6594   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6595   PetscInt        cind = 0, k;
6596 
6597   PetscFunctionBegin;
6598   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6599   PetscCall(PetscSectionGetDof(section, point, &dof));
6600   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6601   if (!cdof || setBC) {
6602     for (k = 0; k < dof; ++k) {
6603       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6604       const PetscInt ind    = indperm ? indperm[preind] : preind;
6605 
6606       indices[ind] = off + k;
6607     }
6608   } else {
6609     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6610     for (k = 0; k < dof; ++k) {
6611       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6612       const PetscInt ind    = indperm ? indperm[preind] : preind;
6613 
6614       if ((cind < cdof) && (k == cdofs[cind])) {
6615         /* Insert check for returning constrained indices */
6616         indices[ind] = -(off+k+1);
6617         ++cind;
6618       } else {
6619         indices[ind] = off + k - (islocal ? 0 : cind);
6620       }
6621     }
6622   }
6623   *loff += dof;
6624   PetscFunctionReturn(0);
6625 }
6626 
6627 /*
6628  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6629 
6630  Input Parameters:
6631 + section - a section (global or local)
6632 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6633 . point - point within section
6634 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6635 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6636 . setBC - identify constrained (boundary condition) points via involution.
6637 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6638 . permsoff - offset
6639 - indperm - index permutation
6640 
6641  Output Parameter:
6642 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6643 . indices - array to hold indices (as defined by section) of each dof associated with point
6644 
6645  Notes:
6646  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6647  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6648  in the local vector.
6649 
6650  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6651  significant).  It is invalid to call with a global section and setBC=true.
6652 
6653  Developer Note:
6654  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6655  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6656  offset could be obtained from the section instead of passing it explicitly as we do now.
6657 
6658  Example:
6659  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6660  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6661  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6662  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.
6663 
6664  Level: developer
6665 */
6666 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[])
6667 {
6668   PetscInt       numFields, foff, f;
6669 
6670   PetscFunctionBegin;
6671   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6672   PetscCall(PetscSectionGetNumFields(section, &numFields));
6673   for (f = 0, foff = 0; f < numFields; ++f) {
6674     PetscInt        fdof, cfdof;
6675     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6676     PetscInt        cind = 0, b;
6677     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6678 
6679     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6680     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6681     if (!cfdof || setBC) {
6682       for (b = 0; b < fdof; ++b) {
6683         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6684         const PetscInt ind    = indperm ? indperm[preind] : preind;
6685 
6686         indices[ind] = off+foff+b;
6687       }
6688     } else {
6689       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6690       for (b = 0; b < fdof; ++b) {
6691         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6692         const PetscInt ind    = indperm ? indperm[preind] : preind;
6693 
6694         if ((cind < cfdof) && (b == fcdofs[cind])) {
6695           indices[ind] = -(off+foff+b+1);
6696           ++cind;
6697         } else {
6698           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6699         }
6700       }
6701     }
6702     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6703     foffs[f] += fdof;
6704   }
6705   PetscFunctionReturn(0);
6706 }
6707 
6708 /*
6709   This version believes the globalSection offsets for each field, rather than just the point offset
6710 
6711  . foffs - The offset into 'indices' for each field, since it is segregated by field
6712 
6713  Notes:
6714  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6715  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6716 */
6717 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6718 {
6719   PetscInt       numFields, foff, f;
6720 
6721   PetscFunctionBegin;
6722   PetscCall(PetscSectionGetNumFields(section, &numFields));
6723   for (f = 0; f < numFields; ++f) {
6724     PetscInt        fdof, cfdof;
6725     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6726     PetscInt        cind = 0, b;
6727     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6728 
6729     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6730     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6731     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6732     if (!cfdof) {
6733       for (b = 0; b < fdof; ++b) {
6734         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6735         const PetscInt ind    = indperm ? indperm[preind] : preind;
6736 
6737         indices[ind] = foff+b;
6738       }
6739     } else {
6740       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6741       for (b = 0; b < fdof; ++b) {
6742         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6743         const PetscInt ind    = indperm ? indperm[preind] : preind;
6744 
6745         if ((cind < cfdof) && (b == fcdofs[cind])) {
6746           indices[ind] = -(foff+b+1);
6747           ++cind;
6748         } else {
6749           indices[ind] = foff+b-cind;
6750         }
6751       }
6752     }
6753     foffs[f] += fdof;
6754   }
6755   PetscFunctionReturn(0);
6756 }
6757 
6758 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)
6759 {
6760   Mat             cMat;
6761   PetscSection    aSec, cSec;
6762   IS              aIS;
6763   PetscInt        aStart = -1, aEnd = -1;
6764   const PetscInt  *anchors;
6765   PetscInt        numFields, f, p, q, newP = 0;
6766   PetscInt        newNumPoints = 0, newNumIndices = 0;
6767   PetscInt        *newPoints, *indices, *newIndices;
6768   PetscInt        maxAnchor, maxDof;
6769   PetscInt        newOffsets[32];
6770   PetscInt        *pointMatOffsets[32];
6771   PetscInt        *newPointOffsets[32];
6772   PetscScalar     *pointMat[32];
6773   PetscScalar     *newValues=NULL,*tmpValues;
6774   PetscBool       anyConstrained = PETSC_FALSE;
6775 
6776   PetscFunctionBegin;
6777   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6778   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6779   PetscCall(PetscSectionGetNumFields(section, &numFields));
6780 
6781   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
6782   /* if there are point-to-point constraints */
6783   if (aSec) {
6784     PetscCall(PetscArrayzero(newOffsets, 32));
6785     PetscCall(ISGetIndices(aIS,&anchors));
6786     PetscCall(PetscSectionGetChart(aSec,&aStart,&aEnd));
6787     /* figure out how many points are going to be in the new element matrix
6788      * (we allow double counting, because it's all just going to be summed
6789      * into the global matrix anyway) */
6790     for (p = 0; p < 2*numPoints; p+=2) {
6791       PetscInt b    = points[p];
6792       PetscInt bDof = 0, bSecDof;
6793 
6794       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6795       if (!bSecDof) {
6796         continue;
6797       }
6798       if (b >= aStart && b < aEnd) {
6799         PetscCall(PetscSectionGetDof(aSec,b,&bDof));
6800       }
6801       if (bDof) {
6802         /* this point is constrained */
6803         /* it is going to be replaced by its anchors */
6804         PetscInt bOff, q;
6805 
6806         anyConstrained = PETSC_TRUE;
6807         newNumPoints  += bDof;
6808         PetscCall(PetscSectionGetOffset(aSec,b,&bOff));
6809         for (q = 0; q < bDof; q++) {
6810           PetscInt a = anchors[bOff + q];
6811           PetscInt aDof;
6812 
6813           PetscCall(PetscSectionGetDof(section,a,&aDof));
6814           newNumIndices += aDof;
6815           for (f = 0; f < numFields; ++f) {
6816             PetscInt fDof;
6817 
6818             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6819             newOffsets[f+1] += fDof;
6820           }
6821         }
6822       }
6823       else {
6824         /* this point is not constrained */
6825         newNumPoints++;
6826         newNumIndices += bSecDof;
6827         for (f = 0; f < numFields; ++f) {
6828           PetscInt fDof;
6829 
6830           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6831           newOffsets[f+1] += fDof;
6832         }
6833       }
6834     }
6835   }
6836   if (!anyConstrained) {
6837     if (outNumPoints)  *outNumPoints  = 0;
6838     if (outNumIndices) *outNumIndices = 0;
6839     if (outPoints)     *outPoints     = NULL;
6840     if (outValues)     *outValues     = NULL;
6841     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6842     PetscFunctionReturn(0);
6843   }
6844 
6845   if (outNumPoints)  *outNumPoints  = newNumPoints;
6846   if (outNumIndices) *outNumIndices = newNumIndices;
6847 
6848   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6849 
6850   if (!outPoints && !outValues) {
6851     if (offsets) {
6852       for (f = 0; f <= numFields; f++) {
6853         offsets[f] = newOffsets[f];
6854       }
6855     }
6856     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6857     PetscFunctionReturn(0);
6858   }
6859 
6860   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6861 
6862   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6863 
6864   /* workspaces */
6865   if (numFields) {
6866     for (f = 0; f < numFields; f++) {
6867       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
6868       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
6869     }
6870   }
6871   else {
6872     PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
6873     PetscCall(DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]));
6874   }
6875 
6876   /* get workspaces for the point-to-point matrices */
6877   if (numFields) {
6878     PetscInt totalOffset, totalMatOffset;
6879 
6880     for (p = 0; p < numPoints; p++) {
6881       PetscInt b    = points[2*p];
6882       PetscInt bDof = 0, bSecDof;
6883 
6884       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6885       if (!bSecDof) {
6886         for (f = 0; f < numFields; f++) {
6887           newPointOffsets[f][p + 1] = 0;
6888           pointMatOffsets[f][p + 1] = 0;
6889         }
6890         continue;
6891       }
6892       if (b >= aStart && b < aEnd) {
6893         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6894       }
6895       if (bDof) {
6896         for (f = 0; f < numFields; f++) {
6897           PetscInt fDof, q, bOff, allFDof = 0;
6898 
6899           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6900           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6901           for (q = 0; q < bDof; q++) {
6902             PetscInt a = anchors[bOff + q];
6903             PetscInt aFDof;
6904 
6905             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6906             allFDof += aFDof;
6907           }
6908           newPointOffsets[f][p+1] = allFDof;
6909           pointMatOffsets[f][p+1] = fDof * allFDof;
6910         }
6911       }
6912       else {
6913         for (f = 0; f < numFields; f++) {
6914           PetscInt fDof;
6915 
6916           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6917           newPointOffsets[f][p+1] = fDof;
6918           pointMatOffsets[f][p+1] = 0;
6919         }
6920       }
6921     }
6922     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6923       newPointOffsets[f][0] = totalOffset;
6924       pointMatOffsets[f][0] = totalMatOffset;
6925       for (p = 0; p < numPoints; p++) {
6926         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6927         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6928       }
6929       totalOffset    = newPointOffsets[f][numPoints];
6930       totalMatOffset = pointMatOffsets[f][numPoints];
6931       PetscCall(DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
6932     }
6933   }
6934   else {
6935     for (p = 0; p < numPoints; p++) {
6936       PetscInt b    = points[2*p];
6937       PetscInt bDof = 0, bSecDof;
6938 
6939       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6940       if (!bSecDof) {
6941         newPointOffsets[0][p + 1] = 0;
6942         pointMatOffsets[0][p + 1] = 0;
6943         continue;
6944       }
6945       if (b >= aStart && b < aEnd) {
6946         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6947       }
6948       if (bDof) {
6949         PetscInt bOff, q, allDof = 0;
6950 
6951         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6952         for (q = 0; q < bDof; q++) {
6953           PetscInt a = anchors[bOff + q], aDof;
6954 
6955           PetscCall(PetscSectionGetDof(section, a, &aDof));
6956           allDof += aDof;
6957         }
6958         newPointOffsets[0][p+1] = allDof;
6959         pointMatOffsets[0][p+1] = bSecDof * allDof;
6960       }
6961       else {
6962         newPointOffsets[0][p+1] = bSecDof;
6963         pointMatOffsets[0][p+1] = 0;
6964       }
6965     }
6966     newPointOffsets[0][0] = 0;
6967     pointMatOffsets[0][0] = 0;
6968     for (p = 0; p < numPoints; p++) {
6969       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6970       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6971     }
6972     PetscCall(DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
6973   }
6974 
6975   /* output arrays */
6976   PetscCall(DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
6977 
6978   /* get the point-to-point matrices; construct newPoints */
6979   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6980   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6981   PetscCall(DMGetWorkArray(dm,maxDof,MPIU_INT,&indices));
6982   PetscCall(DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
6983   if (numFields) {
6984     for (p = 0, newP = 0; p < numPoints; p++) {
6985       PetscInt b    = points[2*p];
6986       PetscInt o    = points[2*p+1];
6987       PetscInt bDof = 0, bSecDof;
6988 
6989       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6990       if (!bSecDof) {
6991         continue;
6992       }
6993       if (b >= aStart && b < aEnd) {
6994         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6995       }
6996       if (bDof) {
6997         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6998 
6999         fStart[0] = 0;
7000         fEnd[0]   = 0;
7001         for (f = 0; f < numFields; f++) {
7002           PetscInt fDof;
7003 
7004           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7005           fStart[f+1] = fStart[f] + fDof;
7006           fEnd[f+1]   = fStart[f+1];
7007         }
7008         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7009         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7010 
7011         fAnchorStart[0] = 0;
7012         fAnchorEnd[0]   = 0;
7013         for (f = 0; f < numFields; f++) {
7014           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7015 
7016           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
7017           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
7018         }
7019         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7020         for (q = 0; q < bDof; q++) {
7021           PetscInt a = anchors[bOff + q], aOff;
7022 
7023           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7024           newPoints[2*(newP + q)]     = a;
7025           newPoints[2*(newP + q) + 1] = 0;
7026           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7027           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7028         }
7029         newP += bDof;
7030 
7031         if (outValues) {
7032           /* get the point-to-point submatrix */
7033           for (f = 0; f < numFields; f++) {
7034             PetscCall(MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]));
7035           }
7036         }
7037       }
7038       else {
7039         newPoints[2 * newP]     = b;
7040         newPoints[2 * newP + 1] = o;
7041         newP++;
7042       }
7043     }
7044   } else {
7045     for (p = 0; p < numPoints; p++) {
7046       PetscInt b    = points[2*p];
7047       PetscInt o    = points[2*p+1];
7048       PetscInt bDof = 0, bSecDof;
7049 
7050       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7051       if (!bSecDof) {
7052         continue;
7053       }
7054       if (b >= aStart && b < aEnd) {
7055         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7056       }
7057       if (bDof) {
7058         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7059 
7060         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7061         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7062 
7063         PetscCall(PetscSectionGetOffset (aSec, b, &bOff));
7064         for (q = 0; q < bDof; q++) {
7065           PetscInt a = anchors[bOff + q], aOff;
7066 
7067           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7068 
7069           newPoints[2*(newP + q)]     = a;
7070           newPoints[2*(newP + q) + 1] = 0;
7071           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7072           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7073         }
7074         newP += bDof;
7075 
7076         /* get the point-to-point submatrix */
7077         if (outValues) {
7078           PetscCall(MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]));
7079         }
7080       }
7081       else {
7082         newPoints[2 * newP]     = b;
7083         newPoints[2 * newP + 1] = o;
7084         newP++;
7085       }
7086     }
7087   }
7088 
7089   if (outValues) {
7090     PetscCall(DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7091     PetscCall(PetscArrayzero(tmpValues,newNumIndices*numIndices));
7092     /* multiply constraints on the right */
7093     if (numFields) {
7094       for (f = 0; f < numFields; f++) {
7095         PetscInt oldOff = offsets[f];
7096 
7097         for (p = 0; p < numPoints; p++) {
7098           PetscInt cStart = newPointOffsets[f][p];
7099           PetscInt b      = points[2 * p];
7100           PetscInt c, r, k;
7101           PetscInt dof;
7102 
7103           PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7104           if (!dof) {
7105             continue;
7106           }
7107           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7108             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
7109             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
7110 
7111             for (r = 0; r < numIndices; r++) {
7112               for (c = 0; c < nCols; c++) {
7113                 for (k = 0; k < dof; k++) {
7114                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7115                 }
7116               }
7117             }
7118           }
7119           else {
7120             /* copy this column as is */
7121             for (r = 0; r < numIndices; r++) {
7122               for (c = 0; c < dof; c++) {
7123                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7124               }
7125             }
7126           }
7127           oldOff += dof;
7128         }
7129       }
7130     }
7131     else {
7132       PetscInt oldOff = 0;
7133       for (p = 0; p < numPoints; p++) {
7134         PetscInt cStart = newPointOffsets[0][p];
7135         PetscInt b      = points[2 * p];
7136         PetscInt c, r, k;
7137         PetscInt dof;
7138 
7139         PetscCall(PetscSectionGetDof(section,b,&dof));
7140         if (!dof) {
7141           continue;
7142         }
7143         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7144           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
7145           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
7146 
7147           for (r = 0; r < numIndices; r++) {
7148             for (c = 0; c < nCols; c++) {
7149               for (k = 0; k < dof; k++) {
7150                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7151               }
7152             }
7153           }
7154         }
7155         else {
7156           /* copy this column as is */
7157           for (r = 0; r < numIndices; r++) {
7158             for (c = 0; c < dof; c++) {
7159               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7160             }
7161           }
7162         }
7163         oldOff += dof;
7164       }
7165     }
7166 
7167     if (multiplyLeft) {
7168       PetscCall(DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues));
7169       PetscCall(PetscArrayzero(newValues,newNumIndices*newNumIndices));
7170       /* multiply constraints transpose on the left */
7171       if (numFields) {
7172         for (f = 0; f < numFields; f++) {
7173           PetscInt oldOff = offsets[f];
7174 
7175           for (p = 0; p < numPoints; p++) {
7176             PetscInt rStart = newPointOffsets[f][p];
7177             PetscInt b      = points[2 * p];
7178             PetscInt c, r, k;
7179             PetscInt dof;
7180 
7181             PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7182             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7183               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
7184               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
7185 
7186               for (r = 0; r < nRows; r++) {
7187                 for (c = 0; c < newNumIndices; c++) {
7188                   for (k = 0; k < dof; k++) {
7189                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7190                   }
7191                 }
7192               }
7193             }
7194             else {
7195               /* copy this row as is */
7196               for (r = 0; r < dof; r++) {
7197                 for (c = 0; c < newNumIndices; c++) {
7198                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7199                 }
7200               }
7201             }
7202             oldOff += dof;
7203           }
7204         }
7205       }
7206       else {
7207         PetscInt oldOff = 0;
7208 
7209         for (p = 0; p < numPoints; p++) {
7210           PetscInt rStart = newPointOffsets[0][p];
7211           PetscInt b      = points[2 * p];
7212           PetscInt c, r, k;
7213           PetscInt dof;
7214 
7215           PetscCall(PetscSectionGetDof(section,b,&dof));
7216           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7217             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
7218             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
7219 
7220             for (r = 0; r < nRows; r++) {
7221               for (c = 0; c < newNumIndices; c++) {
7222                 for (k = 0; k < dof; k++) {
7223                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7224                 }
7225               }
7226             }
7227           }
7228           else {
7229             /* copy this row as is */
7230             for (r = 0; r < dof; r++) {
7231               for (c = 0; c < newNumIndices; c++) {
7232                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7233               }
7234             }
7235           }
7236           oldOff += dof;
7237         }
7238       }
7239 
7240       PetscCall(DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7241     }
7242     else {
7243       newValues = tmpValues;
7244     }
7245   }
7246 
7247   /* clean up */
7248   PetscCall(DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices));
7249   PetscCall(DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
7250 
7251   if (numFields) {
7252     for (f = 0; f < numFields; f++) {
7253       PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
7254       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
7255       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
7256     }
7257   }
7258   else {
7259     PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
7260     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
7261     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]));
7262   }
7263   PetscCall(ISRestoreIndices(aIS,&anchors));
7264 
7265   /* output */
7266   if (outPoints) {
7267     *outPoints = newPoints;
7268   }
7269   else {
7270     PetscCall(DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
7271   }
7272   if (outValues) {
7273     *outValues = newValues;
7274   }
7275   for (f = 0; f <= numFields; f++) {
7276     offsets[f] = newOffsets[f];
7277   }
7278   PetscFunctionReturn(0);
7279 }
7280 
7281 /*@C
7282   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7283 
7284   Not collective
7285 
7286   Input Parameters:
7287 + dm         - The DM
7288 . section    - The PetscSection describing the points (a local section)
7289 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7290 . point      - The point defining the closure
7291 - useClPerm  - Use the closure point permutation if available
7292 
7293   Output Parameters:
7294 + numIndices - The number of dof indices in the closure of point with the input sections
7295 . indices    - The dof indices
7296 . outOffsets - Array to write the field offsets into, or NULL
7297 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7298 
7299   Notes:
7300   Must call DMPlexRestoreClosureIndices() to free allocated memory
7301 
7302   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7303   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7304   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7305   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7306   indices (with the above semantics) are implied.
7307 
7308   Level: advanced
7309 
7310 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7311 @*/
7312 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7313                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7314 {
7315   /* Closure ordering */
7316   PetscSection        clSection;
7317   IS                  clPoints;
7318   const PetscInt     *clp;
7319   PetscInt           *points;
7320   const PetscInt     *clperm = NULL;
7321   /* Dof permutation and sign flips */
7322   const PetscInt    **perms[32] = {NULL};
7323   const PetscScalar **flips[32] = {NULL};
7324   PetscScalar        *valCopy   = NULL;
7325   /* Hanging node constraints */
7326   PetscInt           *pointsC = NULL;
7327   PetscScalar        *valuesC = NULL;
7328   PetscInt            NclC, NiC;
7329 
7330   PetscInt           *idx;
7331   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
7332   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7333 
7334   PetscFunctionBeginHot;
7335   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7336   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7337   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7338   if (numIndices) PetscValidIntPointer(numIndices, 6);
7339   if (indices)    PetscValidPointer(indices, 7);
7340   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7341   if (values)     PetscValidPointer(values, 9);
7342   PetscCall(PetscSectionGetNumFields(section, &Nf));
7343   PetscCheck(Nf <= 31,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7344   PetscCall(PetscArrayzero(offsets, 32));
7345   /* 1) Get points in closure */
7346   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7347   if (useClPerm) {
7348     PetscInt depth, clsize;
7349     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7350     for (clsize=0,p=0; p<Ncl; p++) {
7351       PetscInt dof;
7352       PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
7353       clsize += dof;
7354     }
7355     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
7356   }
7357   /* 2) Get number of indices on these points and field offsets from section */
7358   for (p = 0; p < Ncl*2; p += 2) {
7359     PetscInt dof, fdof;
7360 
7361     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7362     for (f = 0; f < Nf; ++f) {
7363       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7364       offsets[f+1] += fdof;
7365     }
7366     Ni += dof;
7367   }
7368   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
7369   PetscCheck(!Nf || offsets[Nf] == Ni,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7370   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7371   for (f = 0; f < PetscMax(1, Nf); ++f) {
7372     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7373     else    PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7374     /* may need to apply sign changes to the element matrix */
7375     if (values && flips[f]) {
7376       PetscInt foffset = offsets[f];
7377 
7378       for (p = 0; p < Ncl; ++p) {
7379         PetscInt           pnt  = points[2*p], fdof;
7380         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7381 
7382         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7383         else     PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7384         if (flip) {
7385           PetscInt i, j, k;
7386 
7387           if (!valCopy) {
7388             PetscCall(DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7389             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7390             *values = valCopy;
7391           }
7392           for (i = 0; i < fdof; ++i) {
7393             PetscScalar fval = flip[i];
7394 
7395             for (k = 0; k < Ni; ++k) {
7396               valCopy[Ni * (foffset + i) + k] *= fval;
7397               valCopy[Ni * k + (foffset + i)] *= fval;
7398             }
7399           }
7400         }
7401         foffset += fdof;
7402       }
7403     }
7404   }
7405   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7406   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7407   if (NclC) {
7408     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7409     for (f = 0; f < PetscMax(1, Nf); ++f) {
7410       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7411       else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7412     }
7413     for (f = 0; f < PetscMax(1, Nf); ++f) {
7414       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7415       else    PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7416     }
7417     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7418     Ncl     = NclC;
7419     Ni      = NiC;
7420     points  = pointsC;
7421     if (values) *values = valuesC;
7422   }
7423   /* 5) Calculate indices */
7424   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7425   if (Nf) {
7426     PetscInt  idxOff;
7427     PetscBool useFieldOffsets;
7428 
7429     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
7430     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7431     if (useFieldOffsets) {
7432       for (p = 0; p < Ncl; ++p) {
7433         const PetscInt pnt = points[p*2];
7434 
7435         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7436       }
7437     } else {
7438       for (p = 0; p < Ncl; ++p) {
7439         const PetscInt pnt = points[p*2];
7440 
7441         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7442         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7443          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7444          * global section. */
7445         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7446       }
7447     }
7448   } else {
7449     PetscInt off = 0, idxOff;
7450 
7451     for (p = 0; p < Ncl; ++p) {
7452       const PetscInt  pnt  = points[p*2];
7453       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7454 
7455       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7456       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7457        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7458       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7459     }
7460   }
7461   /* 6) Cleanup */
7462   for (f = 0; f < PetscMax(1, Nf); ++f) {
7463     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7464     else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7465   }
7466   if (NclC) {
7467     PetscCall(DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC));
7468   } else {
7469     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7470   }
7471 
7472   if (numIndices) *numIndices = Ni;
7473   if (indices)    *indices    = idx;
7474   PetscFunctionReturn(0);
7475 }
7476 
7477 /*@C
7478   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7479 
7480   Not collective
7481 
7482   Input Parameters:
7483 + dm         - The DM
7484 . section    - The PetscSection describing the points (a local section)
7485 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7486 . point      - The point defining the closure
7487 - useClPerm  - Use the closure point permutation if available
7488 
7489   Output Parameters:
7490 + numIndices - The number of dof indices in the closure of point with the input sections
7491 . indices    - The dof indices
7492 . outOffsets - Array to write the field offsets into, or NULL
7493 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7494 
7495   Notes:
7496   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7497 
7498   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7499   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7500   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7501   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7502   indices (with the above semantics) are implied.
7503 
7504   Level: advanced
7505 
7506 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7507 @*/
7508 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7509                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7510 {
7511   PetscFunctionBegin;
7512   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7513   PetscValidPointer(indices, 7);
7514   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7515   PetscFunctionReturn(0);
7516 }
7517 
7518 /*@C
7519   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7520 
7521   Not collective
7522 
7523   Input Parameters:
7524 + dm - The DM
7525 . section - The section describing the layout in v, or NULL to use the default section
7526 . globalSection - The section describing the layout in v, or NULL to use the default global section
7527 . A - The matrix
7528 . point - The point in the DM
7529 . values - The array of values
7530 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7531 
7532   Fortran Notes:
7533   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7534 
7535   Level: intermediate
7536 
7537 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7538 @*/
7539 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7540 {
7541   DM_Plex           *mesh = (DM_Plex*) dm->data;
7542   PetscInt          *indices;
7543   PetscInt           numIndices;
7544   const PetscScalar *valuesOrig = values;
7545   PetscErrorCode     ierr;
7546 
7547   PetscFunctionBegin;
7548   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7549   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7550   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7551   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7552   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7553   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7554 
7555   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7556 
7557   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7558   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7559   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7560   if (ierr) {
7561     PetscMPIInt    rank;
7562 
7563     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7564     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7565     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7566     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7567     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7568     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7569   }
7570   if (mesh->printFEM > 1) {
7571     PetscInt i;
7572     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7573     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7574     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7575   }
7576 
7577   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7578   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7579   PetscFunctionReturn(0);
7580 }
7581 
7582 /*@C
7583   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7584 
7585   Not collective
7586 
7587   Input Parameters:
7588 + dmRow - The DM for the row fields
7589 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7590 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7591 . dmCol - The DM for the column fields
7592 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7593 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7594 . A - The matrix
7595 . point - The point in the DMs
7596 . values - The array of values
7597 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7598 
7599   Level: intermediate
7600 
7601 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7602 @*/
7603 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7604 {
7605   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7606   PetscInt          *indicesRow, *indicesCol;
7607   PetscInt           numIndicesRow, numIndicesCol;
7608   const PetscScalar *valuesOrig = values;
7609   PetscErrorCode     ierr;
7610 
7611   PetscFunctionBegin;
7612   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7613   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7614   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7615   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7616   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7617   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7618   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7619   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7620   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7621   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7622   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7623 
7624   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7625   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7626 
7627   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7628   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7629   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7630   if (ierr) {
7631     PetscMPIInt    rank;
7632 
7633     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7634     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7635     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7636     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7637     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7638     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7639   }
7640 
7641   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7642   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7643   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7644   PetscFunctionReturn(0);
7645 }
7646 
7647 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7648 {
7649   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7650   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7651   PetscInt       *cpoints = NULL;
7652   PetscInt       *findices, *cindices;
7653   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7654   PetscInt        foffsets[32], coffsets[32];
7655   DMPolytopeType  ct;
7656   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7657   PetscErrorCode  ierr;
7658 
7659   PetscFunctionBegin;
7660   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7661   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7662   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7663   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7664   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7665   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7666   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7667   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7668   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7669   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7670   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7671   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7672   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7673   PetscCall(PetscArrayzero(foffsets, 32));
7674   PetscCall(PetscArrayzero(coffsets, 32));
7675   /* Column indices */
7676   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7677   maxFPoints = numCPoints;
7678   /* Compress out points not in the section */
7679   /*   TODO: Squeeze out points with 0 dof as well */
7680   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7681   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7682     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7683       cpoints[q*2]   = cpoints[p];
7684       cpoints[q*2+1] = cpoints[p+1];
7685       ++q;
7686     }
7687   }
7688   numCPoints = q;
7689   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7690     PetscInt fdof;
7691 
7692     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7693     if (!dof) continue;
7694     for (f = 0; f < numFields; ++f) {
7695       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7696       coffsets[f+1] += fdof;
7697     }
7698     numCIndices += dof;
7699   }
7700   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7701   /* Row indices */
7702   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7703   {
7704     DMPlexTransform tr;
7705     DMPolytopeType *rct;
7706     PetscInt       *rsize, *rcone, *rornt, Nt;
7707 
7708     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7709     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7710     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7711     numSubcells = rsize[Nt-1];
7712     PetscCall(DMPlexTransformDestroy(&tr));
7713   }
7714   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7715   for (r = 0, q = 0; r < numSubcells; ++r) {
7716     /* TODO Map from coarse to fine cells */
7717     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7718     /* Compress out points not in the section */
7719     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7720     for (p = 0; p < numFPoints*2; p += 2) {
7721       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7722         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7723         if (!dof) continue;
7724         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7725         if (s < q) continue;
7726         ftotpoints[q*2]   = fpoints[p];
7727         ftotpoints[q*2+1] = fpoints[p+1];
7728         ++q;
7729       }
7730     }
7731     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7732   }
7733   numFPoints = q;
7734   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7735     PetscInt fdof;
7736 
7737     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7738     if (!dof) continue;
7739     for (f = 0; f < numFields; ++f) {
7740       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7741       foffsets[f+1] += fdof;
7742     }
7743     numFIndices += dof;
7744   }
7745   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7746 
7747   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7748   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7749   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7750   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7751   if (numFields) {
7752     const PetscInt **permsF[32] = {NULL};
7753     const PetscInt **permsC[32] = {NULL};
7754 
7755     for (f = 0; f < numFields; f++) {
7756       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7757       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7758     }
7759     for (p = 0; p < numFPoints; p++) {
7760       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7761       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7762     }
7763     for (p = 0; p < numCPoints; p++) {
7764       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7765       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7766     }
7767     for (f = 0; f < numFields; f++) {
7768       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7769       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7770     }
7771   } else {
7772     const PetscInt **permsF = NULL;
7773     const PetscInt **permsC = NULL;
7774 
7775     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7776     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7777     for (p = 0, off = 0; p < numFPoints; p++) {
7778       const PetscInt *perm = permsF ? permsF[p] : NULL;
7779 
7780       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7781       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7782     }
7783     for (p = 0, off = 0; p < numCPoints; p++) {
7784       const PetscInt *perm = permsC ? permsC[p] : NULL;
7785 
7786       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7787       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7788     }
7789     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7790     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7791   }
7792   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7793   /* TODO: flips */
7794   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7795   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7796   if (ierr) {
7797     PetscMPIInt    rank;
7798 
7799     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7800     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7801     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7802     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7803     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7804   }
7805   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7806   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7807   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7808   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7809   PetscFunctionReturn(0);
7810 }
7811 
7812 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7813 {
7814   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7815   PetscInt      *cpoints = NULL;
7816   PetscInt       foffsets[32], coffsets[32];
7817   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7818   DMPolytopeType ct;
7819   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7820 
7821   PetscFunctionBegin;
7822   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7823   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7824   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7825   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7826   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7827   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7828   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7829   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7830   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7831   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7832   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7833   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7834   PetscCall(PetscArrayzero(foffsets, 32));
7835   PetscCall(PetscArrayzero(coffsets, 32));
7836   /* Column indices */
7837   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7838   maxFPoints = numCPoints;
7839   /* Compress out points not in the section */
7840   /*   TODO: Squeeze out points with 0 dof as well */
7841   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7842   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7843     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7844       cpoints[q*2]   = cpoints[p];
7845       cpoints[q*2+1] = cpoints[p+1];
7846       ++q;
7847     }
7848   }
7849   numCPoints = q;
7850   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7851     PetscInt fdof;
7852 
7853     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7854     if (!dof) continue;
7855     for (f = 0; f < numFields; ++f) {
7856       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7857       coffsets[f+1] += fdof;
7858     }
7859     numCIndices += dof;
7860   }
7861   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7862   /* Row indices */
7863   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7864   {
7865     DMPlexTransform tr;
7866     DMPolytopeType *rct;
7867     PetscInt       *rsize, *rcone, *rornt, Nt;
7868 
7869     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7870     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7871     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7872     numSubcells = rsize[Nt-1];
7873     PetscCall(DMPlexTransformDestroy(&tr));
7874   }
7875   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7876   for (r = 0, q = 0; r < numSubcells; ++r) {
7877     /* TODO Map from coarse to fine cells */
7878     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7879     /* Compress out points not in the section */
7880     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7881     for (p = 0; p < numFPoints*2; p += 2) {
7882       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7883         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7884         if (!dof) continue;
7885         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7886         if (s < q) continue;
7887         ftotpoints[q*2]   = fpoints[p];
7888         ftotpoints[q*2+1] = fpoints[p+1];
7889         ++q;
7890       }
7891     }
7892     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7893   }
7894   numFPoints = q;
7895   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7896     PetscInt fdof;
7897 
7898     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7899     if (!dof) continue;
7900     for (f = 0; f < numFields; ++f) {
7901       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7902       foffsets[f+1] += fdof;
7903     }
7904     numFIndices += dof;
7905   }
7906   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7907 
7908   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7909   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7910   if (numFields) {
7911     const PetscInt **permsF[32] = {NULL};
7912     const PetscInt **permsC[32] = {NULL};
7913 
7914     for (f = 0; f < numFields; f++) {
7915       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7916       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7917     }
7918     for (p = 0; p < numFPoints; p++) {
7919       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7920       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7921     }
7922     for (p = 0; p < numCPoints; p++) {
7923       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7924       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7925     }
7926     for (f = 0; f < numFields; f++) {
7927       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7928       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7929     }
7930   } else {
7931     const PetscInt **permsF = NULL;
7932     const PetscInt **permsC = NULL;
7933 
7934     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7935     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7936     for (p = 0, off = 0; p < numFPoints; p++) {
7937       const PetscInt *perm = permsF ? permsF[p] : NULL;
7938 
7939       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7940       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7941     }
7942     for (p = 0, off = 0; p < numCPoints; p++) {
7943       const PetscInt *perm = permsC ? permsC[p] : NULL;
7944 
7945       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7946       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7947     }
7948     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7949     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7950   }
7951   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7952   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7953   PetscFunctionReturn(0);
7954 }
7955 
7956 /*@C
7957   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7958 
7959   Input Parameter:
7960 . dm   - The DMPlex object
7961 
7962   Output Parameter:
7963 . cellHeight - The height of a cell
7964 
7965   Level: developer
7966 
7967 .seealso `DMPlexSetVTKCellHeight()`
7968 @*/
7969 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7970 {
7971   DM_Plex *mesh = (DM_Plex*) dm->data;
7972 
7973   PetscFunctionBegin;
7974   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7975   PetscValidIntPointer(cellHeight, 2);
7976   *cellHeight = mesh->vtkCellHeight;
7977   PetscFunctionReturn(0);
7978 }
7979 
7980 /*@C
7981   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7982 
7983   Input Parameters:
7984 + dm   - The DMPlex object
7985 - cellHeight - The height of a cell
7986 
7987   Level: developer
7988 
7989 .seealso `DMPlexGetVTKCellHeight()`
7990 @*/
7991 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7992 {
7993   DM_Plex *mesh = (DM_Plex*) dm->data;
7994 
7995   PetscFunctionBegin;
7996   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7997   mesh->vtkCellHeight = cellHeight;
7998   PetscFunctionReturn(0);
7999 }
8000 
8001 /*@
8002   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8003 
8004   Input Parameter:
8005 . dm - The DMPlex object
8006 
8007   Output Parameters:
8008 + gcStart - The first ghost cell, or NULL
8009 - gcEnd   - The upper bound on ghost cells, or NULL
8010 
8011   Level: advanced
8012 
8013 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8014 @*/
8015 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8016 {
8017   DMLabel        ctLabel;
8018 
8019   PetscFunctionBegin;
8020   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8021   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8022   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8023   // Reset label for fast lookup
8024   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8025   PetscFunctionReturn(0);
8026 }
8027 
8028 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8029 {
8030   PetscSection   section, globalSection;
8031   PetscInt      *numbers, p;
8032 
8033   PetscFunctionBegin;
8034   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
8035   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8036   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8037   for (p = pStart; p < pEnd; ++p) {
8038     PetscCall(PetscSectionSetDof(section, p, 1));
8039   }
8040   PetscCall(PetscSectionSetUp(section));
8041   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8042   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8043   for (p = pStart; p < pEnd; ++p) {
8044     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8045     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8046     else                       numbers[p-pStart] += shift;
8047   }
8048   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8049   if (globalSize) {
8050     PetscLayout layout;
8051     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8052     PetscCall(PetscLayoutGetSize(layout, globalSize));
8053     PetscCall(PetscLayoutDestroy(&layout));
8054   }
8055   PetscCall(PetscSectionDestroy(&section));
8056   PetscCall(PetscSectionDestroy(&globalSection));
8057   PetscFunctionReturn(0);
8058 }
8059 
8060 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8061 {
8062   PetscInt       cellHeight, cStart, cEnd;
8063 
8064   PetscFunctionBegin;
8065   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8066   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8067   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8068   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8069   PetscFunctionReturn(0);
8070 }
8071 
8072 /*@
8073   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8074 
8075   Input Parameter:
8076 . dm   - The DMPlex object
8077 
8078   Output Parameter:
8079 . globalCellNumbers - Global cell numbers for all cells on this process
8080 
8081   Level: developer
8082 
8083 .seealso `DMPlexGetVertexNumbering()`
8084 @*/
8085 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8086 {
8087   DM_Plex       *mesh = (DM_Plex*) dm->data;
8088 
8089   PetscFunctionBegin;
8090   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8091   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8092   *globalCellNumbers = mesh->globalCellNumbers;
8093   PetscFunctionReturn(0);
8094 }
8095 
8096 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8097 {
8098   PetscInt       vStart, vEnd;
8099 
8100   PetscFunctionBegin;
8101   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8102   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8103   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8104   PetscFunctionReturn(0);
8105 }
8106 
8107 /*@
8108   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8109 
8110   Input Parameter:
8111 . dm   - The DMPlex object
8112 
8113   Output Parameter:
8114 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8115 
8116   Level: developer
8117 
8118 .seealso `DMPlexGetCellNumbering()`
8119 @*/
8120 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8121 {
8122   DM_Plex       *mesh = (DM_Plex*) dm->data;
8123 
8124   PetscFunctionBegin;
8125   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8126   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8127   *globalVertexNumbers = mesh->globalVertexNumbers;
8128   PetscFunctionReturn(0);
8129 }
8130 
8131 /*@
8132   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
8133 
8134   Input Parameter:
8135 . dm   - The DMPlex object
8136 
8137   Output Parameter:
8138 . globalPointNumbers - Global numbers for all points on this process
8139 
8140   Level: developer
8141 
8142 .seealso `DMPlexGetCellNumbering()`
8143 @*/
8144 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8145 {
8146   IS             nums[4];
8147   PetscInt       depths[4], gdepths[4], starts[4];
8148   PetscInt       depth, d, shift = 0;
8149 
8150   PetscFunctionBegin;
8151   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8152   PetscCall(DMPlexGetDepth(dm, &depth));
8153   /* For unstratified meshes use dim instead of depth */
8154   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8155   for (d = 0; d <= depth; ++d) {
8156     PetscInt end;
8157 
8158     depths[d] = depth-d;
8159     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8160     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8161   }
8162   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8163   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8164   for (d = 0; d <= depth; ++d) {
8165     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]);
8166   }
8167   for (d = 0; d <= depth; ++d) {
8168     PetscInt pStart, pEnd, gsize;
8169 
8170     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8171     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8172     shift += gsize;
8173   }
8174   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8175   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8176   PetscFunctionReturn(0);
8177 }
8178 
8179 /*@
8180   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8181 
8182   Input Parameter:
8183 . dm - The DMPlex object
8184 
8185   Output Parameter:
8186 . ranks - The rank field
8187 
8188   Options Database Keys:
8189 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8190 
8191   Level: intermediate
8192 
8193 .seealso: `DMView()`
8194 @*/
8195 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8196 {
8197   DM             rdm;
8198   PetscFE        fe;
8199   PetscScalar   *r;
8200   PetscMPIInt    rank;
8201   DMPolytopeType ct;
8202   PetscInt       dim, cStart, cEnd, c;
8203   PetscBool      simplex;
8204 
8205   PetscFunctionBeginUser;
8206   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8207   PetscValidPointer(ranks, 2);
8208   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8209   PetscCall(DMClone(dm, &rdm));
8210   PetscCall(DMGetDimension(rdm, &dim));
8211   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8212   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8213   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8214   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8215   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8216   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8217   PetscCall(PetscFEDestroy(&fe));
8218   PetscCall(DMCreateDS(rdm));
8219   PetscCall(DMCreateGlobalVector(rdm, ranks));
8220   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8221   PetscCall(VecGetArray(*ranks, &r));
8222   for (c = cStart; c < cEnd; ++c) {
8223     PetscScalar *lr;
8224 
8225     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8226     if (lr) *lr = rank;
8227   }
8228   PetscCall(VecRestoreArray(*ranks, &r));
8229   PetscCall(DMDestroy(&rdm));
8230   PetscFunctionReturn(0);
8231 }
8232 
8233 /*@
8234   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8235 
8236   Input Parameters:
8237 + dm    - The DMPlex
8238 - label - The DMLabel
8239 
8240   Output Parameter:
8241 . val - The label value field
8242 
8243   Options Database Keys:
8244 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8245 
8246   Level: intermediate
8247 
8248 .seealso: `DMView()`
8249 @*/
8250 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8251 {
8252   DM             rdm;
8253   PetscFE        fe;
8254   PetscScalar   *v;
8255   PetscInt       dim, cStart, cEnd, c;
8256 
8257   PetscFunctionBeginUser;
8258   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8259   PetscValidPointer(label, 2);
8260   PetscValidPointer(val, 3);
8261   PetscCall(DMClone(dm, &rdm));
8262   PetscCall(DMGetDimension(rdm, &dim));
8263   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8264   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8265   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8266   PetscCall(PetscFEDestroy(&fe));
8267   PetscCall(DMCreateDS(rdm));
8268   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8269   PetscCall(DMCreateGlobalVector(rdm, val));
8270   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8271   PetscCall(VecGetArray(*val, &v));
8272   for (c = cStart; c < cEnd; ++c) {
8273     PetscScalar *lv;
8274     PetscInt     cval;
8275 
8276     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8277     PetscCall(DMLabelGetValue(label, c, &cval));
8278     *lv = cval;
8279   }
8280   PetscCall(VecRestoreArray(*val, &v));
8281   PetscCall(DMDestroy(&rdm));
8282   PetscFunctionReturn(0);
8283 }
8284 
8285 /*@
8286   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8287 
8288   Input Parameter:
8289 . dm - The DMPlex object
8290 
8291   Notes:
8292   This is a useful diagnostic when creating meshes programmatically.
8293 
8294   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8295 
8296   Level: developer
8297 
8298 .seealso: `DMCreate()`, `DMSetFromOptions()`
8299 @*/
8300 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8301 {
8302   PetscSection    coneSection, supportSection;
8303   const PetscInt *cone, *support;
8304   PetscInt        coneSize, c, supportSize, s;
8305   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8306   PetscBool       storagecheck = PETSC_TRUE;
8307 
8308   PetscFunctionBegin;
8309   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8310   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8311   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8312   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8313   /* Check that point p is found in the support of its cone points, and vice versa */
8314   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8315   for (p = pStart; p < pEnd; ++p) {
8316     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8317     PetscCall(DMPlexGetCone(dm, p, &cone));
8318     for (c = 0; c < coneSize; ++c) {
8319       PetscBool dup = PETSC_FALSE;
8320       PetscInt  d;
8321       for (d = c-1; d >= 0; --d) {
8322         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8323       }
8324       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8325       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8326       for (s = 0; s < supportSize; ++s) {
8327         if (support[s] == p) break;
8328       }
8329       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8330         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8331         for (s = 0; s < coneSize; ++s) {
8332           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8333         }
8334         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8335         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8336         for (s = 0; s < supportSize; ++s) {
8337           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8338         }
8339         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8340         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]);
8341         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8342       }
8343     }
8344     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8345     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8346     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8347     PetscCall(DMPlexGetSupport(dm, p, &support));
8348     for (s = 0; s < supportSize; ++s) {
8349       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8350       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8351       for (c = 0; c < coneSize; ++c) {
8352         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8353         if (cone[c] != pp) { c = 0; break; }
8354         if (cone[c] == p) break;
8355       }
8356       if (c >= coneSize) {
8357         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8358         for (c = 0; c < supportSize; ++c) {
8359           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8360         }
8361         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8362         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8363         for (c = 0; c < coneSize; ++c) {
8364           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8365         }
8366         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8367         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8368       }
8369     }
8370   }
8371   if (storagecheck) {
8372     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8373     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8374     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8375   }
8376   PetscFunctionReturn(0);
8377 }
8378 
8379 /*
8380   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.
8381 */
8382 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8383 {
8384   DMPolytopeType  cct;
8385   PetscInt        ptpoints[4];
8386   const PetscInt *cone, *ccone, *ptcone;
8387   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8388 
8389   PetscFunctionBegin;
8390   *unsplit = 0;
8391   switch (ct) {
8392     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8393       ptpoints[npt++] = c;
8394       break;
8395     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8396       PetscCall(DMPlexGetCone(dm, c, &cone));
8397       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8398       for (cp = 0; cp < coneSize; ++cp) {
8399         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8400         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8401       }
8402       break;
8403     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8404     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8405       PetscCall(DMPlexGetCone(dm, c, &cone));
8406       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8407       for (cp = 0; cp < coneSize; ++cp) {
8408         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8409         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8410         for (ccp = 0; ccp < cconeSize; ++ccp) {
8411           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8412           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8413             PetscInt p;
8414             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8415             if (p == npt) ptpoints[npt++] = ccone[ccp];
8416           }
8417         }
8418       }
8419       break;
8420     default: break;
8421   }
8422   for (pt = 0; pt < npt; ++pt) {
8423     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8424     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8425   }
8426   PetscFunctionReturn(0);
8427 }
8428 
8429 /*@
8430   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8431 
8432   Input Parameters:
8433 + dm - The DMPlex object
8434 - cellHeight - Normally 0
8435 
8436   Notes:
8437   This is a useful diagnostic when creating meshes programmatically.
8438   Currently applicable only to homogeneous simplex or tensor meshes.
8439 
8440   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8441 
8442   Level: developer
8443 
8444 .seealso: `DMCreate()`, `DMSetFromOptions()`
8445 @*/
8446 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8447 {
8448   DMPlexInterpolatedFlag interp;
8449   DMPolytopeType         ct;
8450   PetscInt               vStart, vEnd, cStart, cEnd, c;
8451 
8452   PetscFunctionBegin;
8453   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8454   PetscCall(DMPlexIsInterpolated(dm, &interp));
8455   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8456   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8457   for (c = cStart; c < cEnd; ++c) {
8458     PetscInt *closure = NULL;
8459     PetscInt  coneSize, closureSize, cl, Nv = 0;
8460 
8461     PetscCall(DMPlexGetCellType(dm, c, &ct));
8462     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8463     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8464     if (interp == DMPLEX_INTERPOLATED_FULL) {
8465       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8466       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));
8467     }
8468     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8469     for (cl = 0; cl < closureSize*2; cl += 2) {
8470       const PetscInt p = closure[cl];
8471       if ((p >= vStart) && (p < vEnd)) ++Nv;
8472     }
8473     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8474     /* Special Case: Tensor faces with identified vertices */
8475     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8476       PetscInt unsplit;
8477 
8478       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8479       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8480     }
8481     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));
8482   }
8483   PetscFunctionReturn(0);
8484 }
8485 
8486 /*@
8487   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8488 
8489   Collective
8490 
8491   Input Parameters:
8492 + dm - The DMPlex object
8493 - cellHeight - Normally 0
8494 
8495   Notes:
8496   This is a useful diagnostic when creating meshes programmatically.
8497   This routine is only relevant for meshes that are fully interpolated across all ranks.
8498   It will error out if a partially interpolated mesh is given on some rank.
8499   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8500 
8501   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8502 
8503   Level: developer
8504 
8505 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8506 @*/
8507 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8508 {
8509   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8510   DMPlexInterpolatedFlag interpEnum;
8511 
8512   PetscFunctionBegin;
8513   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8514   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8515   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8516   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8517     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8518     PetscFunctionReturn(0);
8519   }
8520 
8521   PetscCall(DMGetDimension(dm, &dim));
8522   PetscCall(DMPlexGetDepth(dm, &depth));
8523   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8524   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8525     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8526     for (c = cStart; c < cEnd; ++c) {
8527       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8528       const DMPolytopeType *faceTypes;
8529       DMPolytopeType        ct;
8530       PetscInt              numFaces, coneSize, f;
8531       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8532 
8533       PetscCall(DMPlexGetCellType(dm, c, &ct));
8534       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8535       if (unsplit) continue;
8536       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8537       PetscCall(DMPlexGetCone(dm, c, &cone));
8538       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8539       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8540       for (cl = 0; cl < closureSize*2; cl += 2) {
8541         const PetscInt p = closure[cl];
8542         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8543       }
8544       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8545       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);
8546       for (f = 0; f < numFaces; ++f) {
8547         DMPolytopeType fct;
8548         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8549 
8550         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8551         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8552         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8553           const PetscInt p = fclosure[cl];
8554           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8555         }
8556         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]);
8557         for (v = 0; v < fnumCorners; ++v) {
8558           if (fclosure[v] != faces[fOff+v]) {
8559             PetscInt v1;
8560 
8561             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8562             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8563             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8564             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff+v1]));
8565             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8566             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]);
8567           }
8568         }
8569         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8570         fOff += faceSizes[f];
8571       }
8572       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8573       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8574     }
8575   }
8576   PetscFunctionReturn(0);
8577 }
8578 
8579 /*@
8580   DMPlexCheckGeometry - Check the geometry of mesh cells
8581 
8582   Input Parameter:
8583 . dm - The DMPlex object
8584 
8585   Notes:
8586   This is a useful diagnostic when creating meshes programmatically.
8587 
8588   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8589 
8590   Level: developer
8591 
8592 .seealso: `DMCreate()`, `DMSetFromOptions()`
8593 @*/
8594 PetscErrorCode DMPlexCheckGeometry(DM dm)
8595 {
8596   Vec       coordinates;
8597   PetscReal detJ, J[9], refVol = 1.0;
8598   PetscReal vol;
8599   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8600 
8601   PetscFunctionBegin;
8602   PetscCall(DMGetDimension(dm, &dim));
8603   PetscCall(DMGetCoordinateDim(dm, &dE));
8604   if (dim != dE) PetscFunctionReturn(0);
8605   PetscCall(DMPlexGetDepth(dm, &depth));
8606   for (d = 0; d < dim; ++d) refVol *= 2.0;
8607   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8608   /* Make sure local coordinates are created, because that step is collective */
8609   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8610   for (c = cStart; c < cEnd; ++c) {
8611     DMPolytopeType ct;
8612     PetscInt       unsplit;
8613     PetscBool      ignoreZeroVol = PETSC_FALSE;
8614 
8615     PetscCall(DMPlexGetCellType(dm, c, &ct));
8616     switch (ct) {
8617       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8618       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8619       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8620         ignoreZeroVol = PETSC_TRUE; break;
8621       default: break;
8622     }
8623     switch (ct) {
8624       case DM_POLYTOPE_TRI_PRISM:
8625       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8626       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8627       case DM_POLYTOPE_PYRAMID:
8628         continue;
8629       default: break;
8630     }
8631     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8632     if (unsplit) continue;
8633     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8634     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);
8635     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ*refVol)));
8636     /* This should work with periodicity since DG coordinates should be used */
8637     if (depth > 1) {
8638       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8639       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);
8640       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double) vol));
8641     }
8642   }
8643   PetscFunctionReturn(0);
8644 }
8645 
8646 /*@
8647   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8648 
8649   Collective
8650 
8651   Input Parameters:
8652 + dm - The DMPlex object
8653 - pointSF - The Point SF, or NULL for Point SF attached to DM
8654 
8655   Notes:
8656   This is mainly intended for debugging/testing purposes.
8657 
8658   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8659 
8660   Level: developer
8661 
8662 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8663 @*/
8664 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF)
8665 {
8666   PetscInt        l, nleaves, nroots, overlap;
8667   const PetscInt *locals;
8668   const PetscSFNode *remotes;
8669   PetscBool       distributed;
8670   MPI_Comm        comm;
8671   PetscMPIInt     rank;
8672 
8673   PetscFunctionBegin;
8674   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8675   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8676   else         pointSF = dm->sf;
8677   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8678   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8679   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8680   {
8681     PetscMPIInt    mpiFlag;
8682 
8683     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF),&mpiFlag));
8684     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)",mpiFlag);
8685   }
8686   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8687   PetscCall(DMPlexIsDistributed(dm, &distributed));
8688   if (!distributed) {
8689     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);
8690     PetscFunctionReturn(0);
8691   }
8692   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);
8693   PetscCall(DMPlexGetOverlap(dm, &overlap));
8694 
8695   /* Check SF graph is compatible with DMPlex chart */
8696   {
8697     PetscInt pStart, pEnd, maxLeaf;
8698 
8699     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8700     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8701     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd-pStart, nroots);
8702     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8703   }
8704 
8705   /* Check Point SF has no local points referenced */
8706   for (l = 0; l < nleaves; l++) {
8707     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);
8708   }
8709 
8710   /* Check there are no cells in interface */
8711   if (!overlap) {
8712     PetscInt cellHeight, cStart, cEnd;
8713 
8714     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8715     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8716     for (l = 0; l < nleaves; ++l) {
8717       const PetscInt point = locals ? locals[l] : l;
8718 
8719       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8720     }
8721   }
8722 
8723   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8724   {
8725     const PetscInt *rootdegree;
8726 
8727     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8728     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8729     for (l = 0; l < nleaves; ++l) {
8730       const PetscInt  point = locals ? locals[l] : l;
8731       const PetscInt *cone;
8732       PetscInt        coneSize, c, idx;
8733 
8734       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8735       PetscCall(DMPlexGetCone(dm, point, &cone));
8736       for (c = 0; c < coneSize; ++c) {
8737         if (!rootdegree[cone[c]]) {
8738           if (locals) {
8739             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8740           } else {
8741             idx = (cone[c] < nleaves) ? cone[c] : -1;
8742           }
8743           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8744         }
8745       }
8746     }
8747   }
8748   PetscFunctionReturn(0);
8749 }
8750 
8751 /*@
8752   DMPlexCheck - Perform various checks of Plex sanity
8753 
8754   Input Parameter:
8755 . dm - The DMPlex object
8756 
8757   Notes:
8758   This is a useful diagnostic when creating meshes programmatically.
8759 
8760   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8761 
8762   Currently does not include DMPlexCheckCellShape().
8763 
8764   Level: developer
8765 
8766 .seealso: DMCreate(), DMSetFromOptions()
8767 @*/
8768 PetscErrorCode DMPlexCheck(DM dm)
8769 {
8770   PetscInt cellHeight;
8771 
8772   PetscFunctionBegin;
8773   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8774   PetscCall(DMPlexCheckSymmetry(dm));
8775   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8776   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8777   PetscCall(DMPlexCheckGeometry(dm));
8778   PetscCall(DMPlexCheckPointSF(dm, NULL));
8779   PetscCall(DMPlexCheckInterfaceCones(dm));
8780   PetscFunctionReturn(0);
8781 }
8782 
8783 typedef struct cell_stats
8784 {
8785   PetscReal min, max, sum, squaresum;
8786   PetscInt  count;
8787 } cell_stats_t;
8788 
8789 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8790 {
8791   PetscInt i, N = *len;
8792 
8793   for (i = 0; i < N; i++) {
8794     cell_stats_t *A = (cell_stats_t *) a;
8795     cell_stats_t *B = (cell_stats_t *) b;
8796 
8797     B->min = PetscMin(A->min,B->min);
8798     B->max = PetscMax(A->max,B->max);
8799     B->sum += A->sum;
8800     B->squaresum += A->squaresum;
8801     B->count += A->count;
8802   }
8803 }
8804 
8805 /*@
8806   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8807 
8808   Collective on dm
8809 
8810   Input Parameters:
8811 + dm        - The DMPlex object
8812 . output    - If true, statistics will be displayed on stdout
8813 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8814 
8815   Notes:
8816   This is mainly intended for debugging/testing purposes.
8817 
8818   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8819 
8820   Level: developer
8821 
8822 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8823 @*/
8824 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8825 {
8826   DM             dmCoarse;
8827   cell_stats_t   stats, globalStats;
8828   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8829   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8830   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8831   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8832   PetscMPIInt    rank,size;
8833 
8834   PetscFunctionBegin;
8835   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8836   stats.min   = PETSC_MAX_REAL;
8837   stats.max   = PETSC_MIN_REAL;
8838   stats.sum   = stats.squaresum = 0.;
8839   stats.count = 0;
8840 
8841   PetscCallMPI(MPI_Comm_size(comm, &size));
8842   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8843   PetscCall(DMGetCoordinateDim(dm,&cdim));
8844   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8845   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8846   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8847   for (c = cStart; c < cEnd; c++) {
8848     PetscInt  i;
8849     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8850 
8851     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8852     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8853     for (i = 0; i < PetscSqr(cdim); ++i) {
8854       frobJ    += J[i] * J[i];
8855       frobInvJ += invJ[i] * invJ[i];
8856     }
8857     cond2 = frobJ * frobInvJ;
8858     cond  = PetscSqrtReal(cond2);
8859 
8860     stats.min        = PetscMin(stats.min,cond);
8861     stats.max        = PetscMax(stats.max,cond);
8862     stats.sum       += cond;
8863     stats.squaresum += cond2;
8864     stats.count++;
8865     if (output && cond > limit) {
8866       PetscSection coordSection;
8867       Vec          coordsLocal;
8868       PetscScalar *coords = NULL;
8869       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8870 
8871       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8872       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8873       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8874       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double) cond));
8875       for (i = 0; i < Nv/cdim; ++i) {
8876         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8877         for (d = 0; d < cdim; ++d) {
8878           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8879           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8880         }
8881         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8882       }
8883       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8884       for (cl = 0; cl < clSize*2; cl += 2) {
8885         const PetscInt edge = closure[cl];
8886 
8887         if ((edge >= eStart) && (edge < eEnd)) {
8888           PetscReal len;
8889 
8890           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8891           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double) len));
8892         }
8893       }
8894       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8895       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8896     }
8897   }
8898   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8899 
8900   if (size > 1) {
8901     PetscMPIInt   blockLengths[2] = {4,1};
8902     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8903     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8904     MPI_Op        statReduce;
8905 
8906     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8907     PetscCallMPI(MPI_Type_commit(&statType));
8908     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8909     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8910     PetscCallMPI(MPI_Op_free(&statReduce));
8911     PetscCallMPI(MPI_Type_free(&statType));
8912   } else {
8913     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8914   }
8915   if (rank == 0) {
8916     count = globalStats.count;
8917     min   = globalStats.min;
8918     max   = globalStats.max;
8919     mean  = globalStats.sum / globalStats.count;
8920     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8921   }
8922 
8923   if (output) {
8924     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));
8925   }
8926   PetscCall(PetscFree2(J,invJ));
8927 
8928   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8929   if (dmCoarse) {
8930     PetscBool isplex;
8931 
8932     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8933     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8934   }
8935   PetscFunctionReturn(0);
8936 }
8937 
8938 /*@
8939   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8940   orthogonal quality below given tolerance.
8941 
8942   Collective on dm
8943 
8944   Input Parameters:
8945 + dm   - The DMPlex object
8946 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8947 - atol - [0, 1] Absolute tolerance for tagging cells.
8948 
8949   Output Parameters:
8950 + OrthQual      - Vec containing orthogonal quality per cell
8951 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8952 
8953   Options Database Keys:
8954 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8955 supported.
8956 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8957 
8958   Notes:
8959   Orthogonal quality is given by the following formula:
8960 
8961   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8962 
8963   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
8964   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8965   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8966   calculating the cosine of the angle between these vectors.
8967 
8968   Orthogonal quality ranges from 1 (best) to 0 (worst).
8969 
8970   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8971   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8972 
8973   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8974 
8975   Level: intermediate
8976 
8977 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8978 @*/
8979 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8980 {
8981   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8982   PetscInt                *idx;
8983   PetscScalar             *oqVals;
8984   const PetscScalar       *cellGeomArr, *faceGeomArr;
8985   PetscReal               *ci, *fi, *Ai;
8986   MPI_Comm                comm;
8987   Vec                     cellgeom, facegeom;
8988   DM                      dmFace, dmCell;
8989   IS                      glob;
8990   ISLocalToGlobalMapping  ltog;
8991   PetscViewer             vwr;
8992 
8993   PetscFunctionBegin;
8994   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8995   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
8996   PetscValidPointer(OrthQual, 4);
8997   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
8998   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
8999   PetscCall(DMGetDimension(dm, &nc));
9000   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9001   {
9002     DMPlexInterpolatedFlag interpFlag;
9003 
9004     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9005     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9006       PetscMPIInt rank;
9007 
9008       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9009       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9010     }
9011   }
9012   if (OrthQualLabel) {
9013     PetscValidPointer(OrthQualLabel, 5);
9014     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9015     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9016   } else {*OrthQualLabel = NULL;}
9017   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9018   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9019   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9020   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9021   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9022   PetscCall(VecCreate(comm, OrthQual));
9023   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9024   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
9025   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9026   PetscCall(VecSetUp(*OrthQual));
9027   PetscCall(ISDestroy(&glob));
9028   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9029   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9030   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9031   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9032   PetscCall(VecGetDM(cellgeom, &dmCell));
9033   PetscCall(VecGetDM(facegeom, &dmFace));
9034   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9035   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
9036     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9037     PetscInt           cellarr[2], *adj = NULL;
9038     PetscScalar        *cArr, *fArr;
9039     PetscReal          minvalc = 1.0, minvalf = 1.0;
9040     PetscFVCellGeom    *cg;
9041 
9042     idx[cellIter] = cell-cStart;
9043     cellarr[0] = cell;
9044     /* Make indexing into cellGeom easier */
9045     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9046     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9047     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9048     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9049     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
9050       PetscInt         i;
9051       const PetscInt   neigh = adj[cellneigh];
9052       PetscReal        normci = 0, normfi = 0, normai = 0;
9053       PetscFVCellGeom  *cgneigh;
9054       PetscFVFaceGeom  *fg;
9055 
9056       /* Don't count ourselves in the neighbor list */
9057       if (neigh == cell) continue;
9058       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9059       cellarr[1] = neigh;
9060       {
9061         PetscInt       numcovpts;
9062         const PetscInt *covpts;
9063 
9064         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9065         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9066         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9067       }
9068 
9069       /* Compute c_i, f_i and their norms */
9070       for (i = 0; i < nc; i++) {
9071         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9072         fi[i] = fg->centroid[i] - cg->centroid[i];
9073         Ai[i] = fg->normal[i];
9074         normci += PetscPowReal(ci[i], 2);
9075         normfi += PetscPowReal(fi[i], 2);
9076         normai += PetscPowReal(Ai[i], 2);
9077       }
9078       normci = PetscSqrtReal(normci);
9079       normfi = PetscSqrtReal(normfi);
9080       normai = PetscSqrtReal(normai);
9081 
9082       /* Normalize and compute for each face-cell-normal pair */
9083       for (i = 0; i < nc; i++) {
9084         ci[i] = ci[i]/normci;
9085         fi[i] = fi[i]/normfi;
9086         Ai[i] = Ai[i]/normai;
9087         /* PetscAbs because I don't know if normals are guaranteed to point out */
9088         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9089         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9090       }
9091       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9092         minvalc = PetscRealPart(cArr[cellneighiter]);
9093       }
9094       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9095         minvalf = PetscRealPart(fArr[cellneighiter]);
9096       }
9097     }
9098     PetscCall(PetscFree(adj));
9099     PetscCall(PetscFree2(cArr, fArr));
9100     /* Defer to cell if they're equal */
9101     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9102     if (OrthQualLabel) {
9103       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9104     }
9105   }
9106   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9107   PetscCall(VecAssemblyBegin(*OrthQual));
9108   PetscCall(VecAssemblyEnd(*OrthQual));
9109   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9110   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9111   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9112   if (OrthQualLabel) {
9113     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9114   }
9115   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9116   PetscCall(PetscViewerDestroy(&vwr));
9117   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9118   PetscFunctionReturn(0);
9119 }
9120 
9121 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9122  * interpolator construction */
9123 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9124 {
9125   PetscSection   section, newSection, gsection;
9126   PetscSF        sf;
9127   PetscBool      hasConstraints, ghasConstraints;
9128 
9129   PetscFunctionBegin;
9130   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9131   PetscValidPointer(odm,2);
9132   PetscCall(DMGetLocalSection(dm, &section));
9133   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9134   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9135   if (!ghasConstraints) {
9136     PetscCall(PetscObjectReference((PetscObject)dm));
9137     *odm = dm;
9138     PetscFunctionReturn(0);
9139   }
9140   PetscCall(DMClone(dm, odm));
9141   PetscCall(DMCopyFields(dm, *odm));
9142   PetscCall(DMGetLocalSection(*odm, &newSection));
9143   PetscCall(DMGetPointSF(*odm, &sf));
9144   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9145   PetscCall(DMSetGlobalSection(*odm, gsection));
9146   PetscCall(PetscSectionDestroy(&gsection));
9147   PetscFunctionReturn(0);
9148 }
9149 
9150 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9151 {
9152   DM             dmco, dmfo;
9153   Mat            interpo;
9154   Vec            rscale;
9155   Vec            cglobalo, clocal;
9156   Vec            fglobal, fglobalo, flocal;
9157   PetscBool      regular;
9158 
9159   PetscFunctionBegin;
9160   PetscCall(DMGetFullDM(dmc, &dmco));
9161   PetscCall(DMGetFullDM(dmf, &dmfo));
9162   PetscCall(DMSetCoarseDM(dmfo, dmco));
9163   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9164   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9165   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9166   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9167   PetscCall(DMCreateLocalVector(dmc, &clocal));
9168   PetscCall(VecSet(cglobalo, 0.));
9169   PetscCall(VecSet(clocal, 0.));
9170   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9171   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9172   PetscCall(DMCreateLocalVector(dmf, &flocal));
9173   PetscCall(VecSet(fglobal, 0.));
9174   PetscCall(VecSet(fglobalo, 0.));
9175   PetscCall(VecSet(flocal, 0.));
9176   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9177   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9178   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9179   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9180   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9181   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9182   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9183   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9184   *shift = fglobal;
9185   PetscCall(VecDestroy(&flocal));
9186   PetscCall(VecDestroy(&fglobalo));
9187   PetscCall(VecDestroy(&clocal));
9188   PetscCall(VecDestroy(&cglobalo));
9189   PetscCall(VecDestroy(&rscale));
9190   PetscCall(MatDestroy(&interpo));
9191   PetscCall(DMDestroy(&dmfo));
9192   PetscCall(DMDestroy(&dmco));
9193   PetscFunctionReturn(0);
9194 }
9195 
9196 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9197 {
9198   PetscObject    shifto;
9199   Vec            shift;
9200 
9201   PetscFunctionBegin;
9202   if (!interp) {
9203     Vec rscale;
9204 
9205     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9206     PetscCall(VecDestroy(&rscale));
9207   } else {
9208     PetscCall(PetscObjectReference((PetscObject)interp));
9209   }
9210   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9211   if (!shifto) {
9212     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9213     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9214     shifto = (PetscObject) shift;
9215     PetscCall(VecDestroy(&shift));
9216   }
9217   shift = (Vec) shifto;
9218   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9219   PetscCall(VecAXPY(fineSol, 1.0, shift));
9220   PetscCall(MatDestroy(&interp));
9221   PetscFunctionReturn(0);
9222 }
9223 
9224 /* Pointwise interpolation
9225      Just code FEM for now
9226      u^f = I u^c
9227      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9228      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9229      I_{ij} = psi^f_i phi^c_j
9230 */
9231 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9232 {
9233   PetscSection   gsc, gsf;
9234   PetscInt       m, n;
9235   void          *ctx;
9236   DM             cdm;
9237   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9238 
9239   PetscFunctionBegin;
9240   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9241   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9242   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9243   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9244 
9245   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9246   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9247   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9248   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9249   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9250 
9251   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9252   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9253   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9254   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9255   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9256   if (scaling) {
9257     /* Use naive scaling */
9258     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9259   }
9260   PetscFunctionReturn(0);
9261 }
9262 
9263 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9264 {
9265   VecScatter     ctx;
9266 
9267   PetscFunctionBegin;
9268   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9269   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9270   PetscCall(VecScatterDestroy(&ctx));
9271   PetscFunctionReturn(0);
9272 }
9273 
9274 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9275                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9276                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9277                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9278 {
9279   const PetscInt Nc = uOff[1] - uOff[0];
9280   PetscInt       c;
9281   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9282 }
9283 
9284 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9285 {
9286   DM             dmc;
9287   PetscDS        ds;
9288   Vec            ones, locmass;
9289   IS             cellIS;
9290   PetscFormKey   key;
9291   PetscInt       depth;
9292 
9293   PetscFunctionBegin;
9294   PetscCall(DMClone(dm, &dmc));
9295   PetscCall(DMCopyDisc(dm, dmc));
9296   PetscCall(DMGetDS(dmc, &ds));
9297   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9298   PetscCall(DMCreateGlobalVector(dmc, mass));
9299   PetscCall(DMGetLocalVector(dmc, &ones));
9300   PetscCall(DMGetLocalVector(dmc, &locmass));
9301   PetscCall(DMPlexGetDepth(dmc, &depth));
9302   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9303   PetscCall(VecSet(locmass, 0.0));
9304   PetscCall(VecSet(ones, 1.0));
9305   key.label = NULL;
9306   key.value = 0;
9307   key.field = 0;
9308   key.part  = 0;
9309   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9310   PetscCall(ISDestroy(&cellIS));
9311   PetscCall(VecSet(*mass, 0.0));
9312   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9313   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9314   PetscCall(DMRestoreLocalVector(dmc, &ones));
9315   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9316   PetscCall(DMDestroy(&dmc));
9317   PetscFunctionReturn(0);
9318 }
9319 
9320 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9321 {
9322   PetscSection   gsc, gsf;
9323   PetscInt       m, n;
9324   void          *ctx;
9325   DM             cdm;
9326   PetscBool      regular;
9327 
9328   PetscFunctionBegin;
9329   if (dmFine == dmCoarse) {
9330     DM            dmc;
9331     PetscDS       ds;
9332     PetscWeakForm wf;
9333     Vec           u;
9334     IS            cellIS;
9335     PetscFormKey  key;
9336     PetscInt      depth;
9337 
9338     PetscCall(DMClone(dmFine, &dmc));
9339     PetscCall(DMCopyDisc(dmFine, dmc));
9340     PetscCall(DMGetDS(dmc, &ds));
9341     PetscCall(PetscDSGetWeakForm(ds, &wf));
9342     PetscCall(PetscWeakFormClear(wf));
9343     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9344     PetscCall(DMCreateMatrix(dmc, mass));
9345     PetscCall(DMGetGlobalVector(dmc, &u));
9346     PetscCall(DMPlexGetDepth(dmc, &depth));
9347     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9348     PetscCall(MatZeroEntries(*mass));
9349     key.label = NULL;
9350     key.value = 0;
9351     key.field = 0;
9352     key.part  = 0;
9353     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9354     PetscCall(ISDestroy(&cellIS));
9355     PetscCall(DMRestoreGlobalVector(dmc, &u));
9356     PetscCall(DMDestroy(&dmc));
9357   } else {
9358     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9359     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9360     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9361     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9362 
9363     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9364     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9365     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9366     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9367 
9368     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9369     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9370     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9371     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9372   }
9373   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9374   PetscFunctionReturn(0);
9375 }
9376 
9377 /*@
9378   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9379 
9380   Input Parameter:
9381 . dm - The DMPlex object
9382 
9383   Output Parameter:
9384 . regular - The flag
9385 
9386   Level: intermediate
9387 
9388 .seealso: `DMPlexSetRegularRefinement()`
9389 @*/
9390 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9391 {
9392   PetscFunctionBegin;
9393   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9394   PetscValidBoolPointer(regular, 2);
9395   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9396   PetscFunctionReturn(0);
9397 }
9398 
9399 /*@
9400   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9401 
9402   Input Parameters:
9403 + dm - The DMPlex object
9404 - regular - The flag
9405 
9406   Level: intermediate
9407 
9408 .seealso: `DMPlexGetRegularRefinement()`
9409 @*/
9410 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9411 {
9412   PetscFunctionBegin;
9413   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9414   ((DM_Plex *) dm->data)->regularRefinement = regular;
9415   PetscFunctionReturn(0);
9416 }
9417 
9418 /* anchors */
9419 /*@
9420   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9421   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9422 
9423   not collective
9424 
9425   Input Parameter:
9426 . dm - The DMPlex object
9427 
9428   Output Parameters:
9429 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9430 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9431 
9432   Level: intermediate
9433 
9434 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9435 @*/
9436 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9437 {
9438   DM_Plex *plex = (DM_Plex *)dm->data;
9439 
9440   PetscFunctionBegin;
9441   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9442   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9443   if (anchorSection) *anchorSection = plex->anchorSection;
9444   if (anchorIS) *anchorIS = plex->anchorIS;
9445   PetscFunctionReturn(0);
9446 }
9447 
9448 /*@
9449   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9450   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9451   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9452 
9453   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9454   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9455 
9456   collective on dm
9457 
9458   Input Parameters:
9459 + dm - The DMPlex object
9460 . 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).
9461 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9462 
9463   The reference counts of anchorSection and anchorIS are incremented.
9464 
9465   Level: intermediate
9466 
9467 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9468 @*/
9469 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9470 {
9471   DM_Plex        *plex = (DM_Plex *)dm->data;
9472   PetscMPIInt    result;
9473 
9474   PetscFunctionBegin;
9475   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9476   if (anchorSection) {
9477     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9478     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9479     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9480   }
9481   if (anchorIS) {
9482     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9483     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9484     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9485   }
9486 
9487   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9488   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9489   plex->anchorSection = anchorSection;
9490 
9491   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9492   PetscCall(ISDestroy(&plex->anchorIS));
9493   plex->anchorIS = anchorIS;
9494 
9495   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9496     PetscInt size, a, pStart, pEnd;
9497     const PetscInt *anchors;
9498 
9499     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9500     PetscCall(ISGetLocalSize(anchorIS,&size));
9501     PetscCall(ISGetIndices(anchorIS,&anchors));
9502     for (a = 0; a < size; a++) {
9503       PetscInt p;
9504 
9505       p = anchors[a];
9506       if (p >= pStart && p < pEnd) {
9507         PetscInt dof;
9508 
9509         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9510         if (dof) {
9511 
9512           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9513           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %" PetscInt_FMT " cannot be constrained and an anchor",p);
9514         }
9515       }
9516     }
9517     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9518   }
9519   /* reset the generic constraints */
9520   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9521   PetscFunctionReturn(0);
9522 }
9523 
9524 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9525 {
9526   PetscSection anchorSection;
9527   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9528 
9529   PetscFunctionBegin;
9530   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9531   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9532   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9533   PetscCall(PetscSectionGetNumFields(section,&numFields));
9534   if (numFields) {
9535     PetscInt f;
9536     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9537 
9538     for (f = 0; f < numFields; f++) {
9539       PetscInt numComp;
9540 
9541       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9542       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9543     }
9544   }
9545   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9546   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9547   pStart = PetscMax(pStart,sStart);
9548   pEnd   = PetscMin(pEnd,sEnd);
9549   pEnd   = PetscMax(pStart,pEnd);
9550   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9551   for (p = pStart; p < pEnd; p++) {
9552     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9553     if (dof) {
9554       PetscCall(PetscSectionGetDof(section,p,&dof));
9555       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9556       for (f = 0; f < numFields; f++) {
9557         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9558         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9559       }
9560     }
9561   }
9562   PetscCall(PetscSectionSetUp(*cSec));
9563   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9564   PetscFunctionReturn(0);
9565 }
9566 
9567 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9568 {
9569   PetscSection   aSec;
9570   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9571   const PetscInt *anchors;
9572   PetscInt       numFields, f;
9573   IS             aIS;
9574   MatType        mtype;
9575   PetscBool      iscuda,iskokkos;
9576 
9577   PetscFunctionBegin;
9578   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9579   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9580   PetscCall(PetscSectionGetStorageSize(section, &n));
9581   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9582   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9583   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9584   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9585   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9586   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9587   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9588   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9589   else mtype = MATSEQAIJ;
9590   PetscCall(MatSetType(*cMat,mtype));
9591   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9592   PetscCall(ISGetIndices(aIS,&anchors));
9593   /* cSec will be a subset of aSec and section */
9594   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9595   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9596   PetscCall(PetscMalloc1(m+1,&i));
9597   i[0] = 0;
9598   PetscCall(PetscSectionGetNumFields(section,&numFields));
9599   for (p = pStart; p < pEnd; p++) {
9600     PetscInt rDof, rOff, r;
9601 
9602     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9603     if (!rDof) continue;
9604     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9605     if (numFields) {
9606       for (f = 0; f < numFields; f++) {
9607         annz = 0;
9608         for (r = 0; r < rDof; r++) {
9609           a = anchors[rOff + r];
9610           if (a < sStart || a >= sEnd) continue;
9611           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9612           annz += aDof;
9613         }
9614         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9615         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9616         for (q = 0; q < dof; q++) {
9617           i[off + q + 1] = i[off + q] + annz;
9618         }
9619       }
9620     } else {
9621       annz = 0;
9622       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9623       for (q = 0; q < dof; q++) {
9624         a = anchors[rOff + q];
9625         if (a < sStart || a >= sEnd) continue;
9626         PetscCall(PetscSectionGetDof(section,a,&aDof));
9627         annz += aDof;
9628       }
9629       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9630       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9631       for (q = 0; q < dof; q++) {
9632         i[off + q + 1] = i[off + q] + annz;
9633       }
9634     }
9635   }
9636   nnz = i[m];
9637   PetscCall(PetscMalloc1(nnz,&j));
9638   offset = 0;
9639   for (p = pStart; p < pEnd; p++) {
9640     if (numFields) {
9641       for (f = 0; f < numFields; f++) {
9642         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9643         for (q = 0; q < dof; q++) {
9644           PetscInt rDof, rOff, r;
9645           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9646           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9647           for (r = 0; r < rDof; r++) {
9648             PetscInt s;
9649 
9650             a = anchors[rOff + r];
9651             if (a < sStart || a >= sEnd) continue;
9652             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9653             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9654             for (s = 0; s < aDof; s++) {
9655               j[offset++] = aOff + s;
9656             }
9657           }
9658         }
9659       }
9660     } else {
9661       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9662       for (q = 0; q < dof; q++) {
9663         PetscInt rDof, rOff, r;
9664         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9665         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9666         for (r = 0; r < rDof; r++) {
9667           PetscInt s;
9668 
9669           a = anchors[rOff + r];
9670           if (a < sStart || a >= sEnd) continue;
9671           PetscCall(PetscSectionGetDof(section,a,&aDof));
9672           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9673           for (s = 0; s < aDof; s++) {
9674             j[offset++] = aOff + s;
9675           }
9676         }
9677       }
9678     }
9679   }
9680   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9681   PetscCall(PetscFree(i));
9682   PetscCall(PetscFree(j));
9683   PetscCall(ISRestoreIndices(aIS,&anchors));
9684   PetscFunctionReturn(0);
9685 }
9686 
9687 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9688 {
9689   DM_Plex        *plex = (DM_Plex *)dm->data;
9690   PetscSection   anchorSection, section, cSec;
9691   Mat            cMat;
9692 
9693   PetscFunctionBegin;
9694   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9695   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9696   if (anchorSection) {
9697     PetscInt Nf;
9698 
9699     PetscCall(DMGetLocalSection(dm,&section));
9700     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9701     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9702     PetscCall(DMGetNumFields(dm,&Nf));
9703     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9704     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9705     PetscCall(PetscSectionDestroy(&cSec));
9706     PetscCall(MatDestroy(&cMat));
9707   }
9708   PetscFunctionReturn(0);
9709 }
9710 
9711 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9712 {
9713   IS             subis;
9714   PetscSection   section, subsection;
9715 
9716   PetscFunctionBegin;
9717   PetscCall(DMGetLocalSection(dm, &section));
9718   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9719   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9720   /* Create subdomain */
9721   PetscCall(DMPlexFilter(dm, label, value, subdm));
9722   /* Create submodel */
9723   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9724   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9725   PetscCall(DMSetLocalSection(*subdm, subsection));
9726   PetscCall(PetscSectionDestroy(&subsection));
9727   PetscCall(DMCopyDisc(dm, *subdm));
9728   /* Create map from submodel to global model */
9729   if (is) {
9730     PetscSection    sectionGlobal, subsectionGlobal;
9731     IS              spIS;
9732     const PetscInt *spmap;
9733     PetscInt       *subIndices;
9734     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9735     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9736 
9737     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9738     PetscCall(ISGetIndices(spIS, &spmap));
9739     PetscCall(PetscSectionGetNumFields(section, &Nf));
9740     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9741     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9742     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9743     for (p = pStart; p < pEnd; ++p) {
9744       PetscInt gdof, pSubSize  = 0;
9745 
9746       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9747       if (gdof > 0) {
9748         for (f = 0; f < Nf; ++f) {
9749           PetscInt fdof, fcdof;
9750 
9751           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9752           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9753           pSubSize += fdof-fcdof;
9754         }
9755         subSize += pSubSize;
9756         if (pSubSize) {
9757           if (bs < 0) {
9758             bs = pSubSize;
9759           } else if (bs != pSubSize) {
9760             /* Layout does not admit a pointwise block size */
9761             bs = 1;
9762           }
9763         }
9764       }
9765     }
9766     /* Must have same blocksize on all procs (some might have no points) */
9767     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9768     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9769     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9770     else                            {bs = bsMinMax[0];}
9771     PetscCall(PetscMalloc1(subSize, &subIndices));
9772     for (p = pStart; p < pEnd; ++p) {
9773       PetscInt gdof, goff;
9774 
9775       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9776       if (gdof > 0) {
9777         const PetscInt point = spmap[p];
9778 
9779         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9780         for (f = 0; f < Nf; ++f) {
9781           PetscInt fdof, fcdof, fc, f2, poff = 0;
9782 
9783           /* Can get rid of this loop by storing field information in the global section */
9784           for (f2 = 0; f2 < f; ++f2) {
9785             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9786             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9787             poff += fdof-fcdof;
9788           }
9789           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9790           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9791           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9792             subIndices[subOff] = goff+poff+fc;
9793           }
9794         }
9795       }
9796     }
9797     PetscCall(ISRestoreIndices(spIS, &spmap));
9798     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9799     if (bs > 1) {
9800       /* We need to check that the block size does not come from non-contiguous fields */
9801       PetscInt i, j, set = 1;
9802       for (i = 0; i < subSize; i += bs) {
9803         for (j = 0; j < bs; ++j) {
9804           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9805         }
9806       }
9807       if (set) PetscCall(ISSetBlockSize(*is, bs));
9808     }
9809     /* Attach nullspace */
9810     for (f = 0; f < Nf; ++f) {
9811       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9812       if ((*subdm)->nullspaceConstructors[f]) break;
9813     }
9814     if (f < Nf) {
9815       MatNullSpace nullSpace;
9816       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9817 
9818       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9819       PetscCall(MatNullSpaceDestroy(&nullSpace));
9820     }
9821   }
9822   PetscFunctionReturn(0);
9823 }
9824 
9825 /*@
9826   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9827 
9828   Input Parameter:
9829 - dm - The DM
9830 
9831   Level: developer
9832 
9833   Options Database Keys:
9834 . -dm_plex_monitor_throughput - Activate the monitor
9835 
9836 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9837 @*/
9838 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9839 {
9840 #if defined(PETSC_USE_LOG)
9841   PetscStageLog      stageLog;
9842   PetscLogEvent      event;
9843   PetscLogStage      stage;
9844   PetscEventPerfInfo eventInfo;
9845   PetscReal          cellRate, flopRate;
9846   PetscInt           cStart, cEnd, Nf, N;
9847   const char        *name;
9848 #endif
9849 
9850   PetscFunctionBegin;
9851   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9852 #if defined(PETSC_USE_LOG)
9853   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9854   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9855   PetscCall(DMGetNumFields(dm, &Nf));
9856   PetscCall(PetscLogGetStageLog(&stageLog));
9857   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9858   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9859   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9860   N        = (cEnd - cStart)*Nf*eventInfo.count;
9861   flopRate = eventInfo.flops/eventInfo.time;
9862   cellRate = N/eventInfo.time;
9863   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)));
9864 #else
9865   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9866 #endif
9867   PetscFunctionReturn(0);
9868 }
9869