xref: /petsc/src/dm/impls/plex/plex.c (revision e08b1d6d0faae6eca507e20c9d3498f81719d047)
1 #include <petsc/private/dmpleximpl.h>   /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF,DMPLEX_LocatePoints,DMPLEX_TopologyView,DMPLEX_LabelsView,DMPLEX_CoordinatesView,DMPLEX_SectionView,DMPLEX_GlobalVectorView,DMPLEX_LocalVectorView,DMPLEX_TopologyLoad,DMPLEX_LabelsLoad,DMPLEX_CoordinatesLoad,DMPLEX_SectionLoad,DMPLEX_GlobalVectorLoad,DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph,DMPLEX_RebalRewriteSF,DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm      - The DMPlex object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
28   If the mesh has no cells, this returns PETSC_FALSE.
29 
30   Level: intermediate
31 
32 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
33 @*/
34 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
35 {
36   DMPolytopeType ct;
37   PetscInt       cStart, cEnd;
38 
39   PetscFunctionBegin;
40   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
41   if (cEnd <= cStart) {*simplex = PETSC_FALSE; PetscFunctionReturn(0);}
42   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
43   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
44   PetscFunctionReturn(0);
45 }
46 
47 /*@
48   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
49 
50   Input Parameters:
51 + dm     - The DMPlex object
52 - height - The cell height in the Plex, 0 is the default
53 
54   Output Parameters:
55 + cStart - The first "normal" cell
56 - cEnd   - The upper bound on "normal"" cells
57 
58   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
59 
60   Level: developer
61 
62 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
63 @*/
64 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
65 {
66   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
67   PetscInt       cS, cE, c;
68 
69   PetscFunctionBegin;
70   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
71   for (c = cS; c < cE; ++c) {
72     DMPolytopeType cct;
73 
74     PetscCall(DMPlexGetCellType(dm, c, &cct));
75     if ((PetscInt) cct < 0) break;
76     switch (cct) {
77       case DM_POLYTOPE_POINT:
78       case DM_POLYTOPE_SEGMENT:
79       case DM_POLYTOPE_TRIANGLE:
80       case DM_POLYTOPE_QUADRILATERAL:
81       case DM_POLYTOPE_TETRAHEDRON:
82       case DM_POLYTOPE_HEXAHEDRON:
83         ct = cct;
84         break;
85       default: break;
86     }
87     if (ct != DM_POLYTOPE_UNKNOWN) break;
88   }
89   if (ct != DM_POLYTOPE_UNKNOWN) {
90     DMLabel ctLabel;
91 
92     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
93     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
94     // Reset label for fast lookup
95     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
96   }
97   if (cStart) *cStart = cS;
98   if (cEnd)   *cEnd   = cE;
99   PetscFunctionReturn(0);
100 }
101 
102 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
103 {
104   PetscInt       cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
105   PetscInt       vcdof[2] = {0,0}, globalvcdof[2];
106 
107   PetscFunctionBegin;
108   *ft  = PETSC_VTK_INVALID;
109   PetscCall(DMGetCoordinateDim(dm, &cdim));
110   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
111   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
112   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
113   if (field >= 0) {
114     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
115     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
116   } else {
117     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
118     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
119   }
120   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
121   if (globalvcdof[0]) {
122     *sStart = vStart;
123     *sEnd   = vEnd;
124     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
125     else                        *ft = PETSC_VTK_POINT_FIELD;
126   } else if (globalvcdof[1]) {
127     *sStart = cStart;
128     *sEnd   = cEnd;
129     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
130     else                        *ft = PETSC_VTK_CELL_FIELD;
131   } else {
132     if (field >= 0) {
133       const char *fieldname;
134 
135       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
136       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
137     } else {
138       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section\n"));
139     }
140   }
141   PetscFunctionReturn(0);
142 }
143 
144 /*@
145   DMPlexVecView1D - Plot many 1D solutions on the same line graph
146 
147   Collective on dm
148 
149   Input Parameters:
150 + dm - The DMPlex
151 . n  - The number of vectors
152 . u  - The array of local vectors
153 - viewer - The Draw viewer
154 
155   Level: advanced
156 
157 .seealso: `VecViewFromOptions()`, `VecView()`
158 @*/
159 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
160 {
161   PetscDS            ds;
162   PetscDraw          draw = NULL;
163   PetscDrawLG        lg;
164   Vec                coordinates;
165   const PetscScalar *coords, **sol;
166   PetscReal         *vals;
167   PetscInt          *Nc;
168   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
169   char             **names;
170 
171   PetscFunctionBegin;
172   PetscCall(DMGetDS(dm, &ds));
173   PetscCall(PetscDSGetNumFields(ds, &Nf));
174   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
175   PetscCall(PetscDSGetComponents(ds, &Nc));
176 
177   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
178   if (!draw) PetscFunctionReturn(0);
179   PetscCall(PetscDrawLGCreate(draw, n*Nl, &lg));
180 
181   PetscCall(PetscMalloc3(n, &sol, n*Nl, &names, n*Nl, &vals));
182   for (i = 0, l = 0; i < n; ++i) {
183     const char *vname;
184 
185     PetscCall(PetscObjectGetName((PetscObject) u[i], &vname));
186     for (f = 0; f < Nf; ++f) {
187       PetscObject disc;
188       const char *fname;
189       char        tmpname[PETSC_MAX_PATH_LEN];
190 
191       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
192       /* TODO Create names for components */
193       for (c = 0; c < Nc[f]; ++c, ++l) {
194         PetscCall(PetscObjectGetName(disc, &fname));
195         PetscCall(PetscStrcpy(tmpname, vname));
196         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
197         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
198         PetscCall(PetscStrallocpy(tmpname, &names[l]));
199       }
200     }
201   }
202   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *) names));
203   /* Just add P_1 support for now */
204   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
205   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
206   PetscCall(VecGetArrayRead(coordinates, &coords));
207   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
208   for (v = vStart; v < vEnd; ++v) {
209     PetscScalar *x, *svals;
210 
211     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
212     for (i = 0; i < n; ++i) {
213       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
214       for (l = 0; l < Nl; ++l) vals[i*Nl + l] = PetscRealPart(svals[l]);
215     }
216     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
217   }
218   PetscCall(VecRestoreArrayRead(coordinates, &coords));
219   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
220   for (l = 0; l < n*Nl; ++l) PetscCall(PetscFree(names[l]));
221   PetscCall(PetscFree3(sol, names, vals));
222 
223   PetscCall(PetscDrawLGDraw(lg));
224   PetscCall(PetscDrawLGDestroy(&lg));
225   PetscFunctionReturn(0);
226 }
227 
228 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
229 {
230   DM             dm;
231 
232   PetscFunctionBegin;
233   PetscCall(VecGetDM(u, &dm));
234   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
235   PetscFunctionReturn(0);
236 }
237 
238 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
239 {
240   DM                 dm;
241   PetscSection       s;
242   PetscDraw          draw, popup;
243   DM                 cdm;
244   PetscSection       coordSection;
245   Vec                coordinates;
246   const PetscScalar *coords, *array;
247   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
248   PetscReal          vbound[2], time;
249   PetscBool          flg;
250   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
251   const char        *name;
252   char               title[PETSC_MAX_PATH_LEN];
253 
254   PetscFunctionBegin;
255   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
256   PetscCall(VecGetDM(v, &dm));
257   PetscCall(DMGetCoordinateDim(dm, &dim));
258   PetscCall(DMGetLocalSection(dm, &s));
259   PetscCall(PetscSectionGetNumFields(s, &Nf));
260   PetscCall(DMGetCoarsenLevel(dm, &level));
261   PetscCall(DMGetCoordinateDM(dm, &cdm));
262   PetscCall(DMGetLocalSection(cdm, &coordSection));
263   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
264   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
265   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
266 
267   PetscCall(PetscObjectGetName((PetscObject) v, &name));
268   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
269 
270   PetscCall(VecGetLocalSize(coordinates, &N));
271   PetscCall(VecGetArrayRead(coordinates, &coords));
272   for (c = 0; c < N; c += dim) {
273     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
274     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
275   }
276   PetscCall(VecRestoreArrayRead(coordinates, &coords));
277   PetscCall(PetscDrawClear(draw));
278 
279   /* Could implement something like DMDASelectFields() */
280   for (f = 0; f < Nf; ++f) {
281     DM   fdm = dm;
282     Vec  fv  = v;
283     IS   fis;
284     char prefix[PETSC_MAX_PATH_LEN];
285     const char *fname;
286 
287     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
288     PetscCall(PetscSectionGetFieldName(s, f, &fname));
289 
290     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix,sizeof(prefix)));
291     else               {prefix[0] = '\0';}
292     if (Nf > 1) {
293       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
294       PetscCall(VecGetSubVector(v, fis, &fv));
295       PetscCall(PetscStrlcat(prefix, fname,sizeof(prefix)));
296       PetscCall(PetscStrlcat(prefix, "_",sizeof(prefix)));
297     }
298     for (comp = 0; comp < Nc; ++comp, ++w) {
299       PetscInt nmax = 2;
300 
301       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
302       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
303       else        PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
304       PetscCall(PetscDrawSetTitle(draw, title));
305 
306       /* TODO Get max and min only for this component */
307       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
308       if (!flg) {
309         PetscCall(VecMin(fv, NULL, &vbound[0]));
310         PetscCall(VecMax(fv, NULL, &vbound[1]));
311         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
312       }
313       PetscCall(PetscDrawGetPopup(draw, &popup));
314       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
315       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
316 
317       PetscCall(VecGetArrayRead(fv, &array));
318       for (c = cStart; c < cEnd; ++c) {
319         PetscScalar *coords = NULL, *a = NULL;
320         PetscInt     numCoords, color[4] = {-1,-1,-1,-1};
321 
322         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
323         if (a) {
324           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
325           color[1] = color[2] = color[3] = color[0];
326         } else {
327           PetscScalar *vals = NULL;
328           PetscInt     numVals, va;
329 
330           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
331           PetscCheck(numVals % Nc == 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
332           switch (numVals/Nc) {
333           case 3: /* P1 Triangle */
334           case 4: /* P1 Quadrangle */
335             for (va = 0; va < numVals/Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp]), vbound[0], vbound[1]);
336             break;
337           case 6: /* P2 Triangle */
338           case 8: /* P2 Quadrangle */
339             for (va = 0; va < numVals/(Nc*2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp + numVals/(Nc*2)]), vbound[0], vbound[1]);
340             break;
341           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals/Nc);
342           }
343           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
344         }
345         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
346         switch (numCoords) {
347         case 6:
348         case 12: /* Localized triangle */
349           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
350           break;
351         case 8:
352         case 16: /* Localized quadrilateral */
353           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
354           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
355           break;
356         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
357         }
358         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
359       }
360       PetscCall(VecRestoreArrayRead(fv, &array));
361       PetscCall(PetscDrawFlush(draw));
362       PetscCall(PetscDrawPause(draw));
363       PetscCall(PetscDrawSave(draw));
364     }
365     if (Nf > 1) {
366       PetscCall(VecRestoreSubVector(v, fis, &fv));
367       PetscCall(ISDestroy(&fis));
368       PetscCall(DMDestroy(&fdm));
369     }
370   }
371   PetscFunctionReturn(0);
372 }
373 
374 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
375 {
376   DM        dm;
377   PetscDraw draw;
378   PetscInt  dim;
379   PetscBool isnull;
380 
381   PetscFunctionBegin;
382   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
383   PetscCall(PetscDrawIsNull(draw, &isnull));
384   if (isnull) PetscFunctionReturn(0);
385 
386   PetscCall(VecGetDM(v, &dm));
387   PetscCall(DMGetCoordinateDim(dm, &dim));
388   switch (dim) {
389   case 1: PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));break;
390   case 2: PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));break;
391   default: SETERRQ(PetscObjectComm((PetscObject) v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
392   }
393   PetscFunctionReturn(0);
394 }
395 
396 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
397 {
398   DM                      dm;
399   Vec                     locv;
400   const char              *name;
401   PetscSection            section;
402   PetscInt                pStart, pEnd;
403   PetscInt                numFields;
404   PetscViewerVTKFieldType ft;
405 
406   PetscFunctionBegin;
407   PetscCall(VecGetDM(v, &dm));
408   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
409   PetscCall(PetscObjectGetName((PetscObject) v, &name));
410   PetscCall(PetscObjectSetName((PetscObject) locv, name));
411   PetscCall(VecCopy(v, locv));
412   PetscCall(DMGetLocalSection(dm, &section));
413   PetscCall(PetscSectionGetNumFields(section, &numFields));
414   if (!numFields) {
415     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
416     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE,(PetscObject) locv));
417   } else {
418     PetscInt f;
419 
420     for (f = 0; f < numFields; f++) {
421       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
422       if (ft == PETSC_VTK_INVALID) continue;
423       PetscCall(PetscObjectReference((PetscObject)locv));
424       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE,(PetscObject) locv));
425     }
426     PetscCall(VecDestroy(&locv));
427   }
428   PetscFunctionReturn(0);
429 }
430 
431 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
432 {
433   DM             dm;
434   PetscBool      isvtk, ishdf5, isdraw, isglvis, iscgns;
435 
436   PetscFunctionBegin;
437   PetscCall(VecGetDM(v, &dm));
438   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
439   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk));
440   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5));
441   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw));
442   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis));
443   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERCGNS,  &iscgns));
444   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
445     PetscInt    i,numFields;
446     PetscObject fe;
447     PetscBool   fem = PETSC_FALSE;
448     Vec         locv = v;
449     const char  *name;
450     PetscInt    step;
451     PetscReal   time;
452 
453     PetscCall(DMGetNumFields(dm, &numFields));
454     for (i=0; i<numFields; i++) {
455       PetscCall(DMGetField(dm, i, NULL, &fe));
456       if (fe->classid == PETSCFE_CLASSID) { fem = PETSC_TRUE; break; }
457     }
458     if (fem) {
459       PetscObject isZero;
460 
461       PetscCall(DMGetLocalVector(dm, &locv));
462       PetscCall(PetscObjectGetName((PetscObject) v, &name));
463       PetscCall(PetscObjectSetName((PetscObject) locv, name));
464       PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
465       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
466       PetscCall(VecCopy(v, locv));
467       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
468       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
469     }
470     if (isvtk) {
471       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
472     } else if (ishdf5) {
473 #if defined(PETSC_HAVE_HDF5)
474       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
475 #else
476       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
477 #endif
478     } else if (isdraw) {
479       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
480     } else if (isglvis) {
481       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
482       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
483       PetscCall(VecView_GLVis(locv, viewer));
484     } else if (iscgns) {
485 #if defined(PETSC_HAVE_CGNS)
486       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
487 #else
488       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
489 #endif
490     }
491     if (fem) {
492       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
493       PetscCall(DMRestoreLocalVector(dm, &locv));
494     }
495   } else {
496     PetscBool isseq;
497 
498     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
499     if (isseq) PetscCall(VecView_Seq(v, viewer));
500     else       PetscCall(VecView_MPI(v, viewer));
501   }
502   PetscFunctionReturn(0);
503 }
504 
505 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
506 {
507   DM        dm;
508   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
509 
510   PetscFunctionBegin;
511   PetscCall(VecGetDM(v, &dm));
512   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
513   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
514   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
515   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
516   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
517   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERCGNS,     &iscgns));
518   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
519   if (isvtk || isdraw || isglvis || iscgns) {
520     Vec         locv;
521     PetscObject isZero;
522     const char *name;
523 
524     PetscCall(DMGetLocalVector(dm, &locv));
525     PetscCall(PetscObjectGetName((PetscObject) v, &name));
526     PetscCall(PetscObjectSetName((PetscObject) locv, name));
527     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
528     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
529     PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
530     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
531     PetscCall(VecView_Plex_Local(locv, viewer));
532     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
533     PetscCall(DMRestoreLocalVector(dm, &locv));
534   } else if (ishdf5) {
535 #if defined(PETSC_HAVE_HDF5)
536     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
537 #else
538     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
539 #endif
540   } else if (isexodusii) {
541 #if defined(PETSC_HAVE_EXODUSII)
542     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
543 #else
544     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
545 #endif
546   } else {
547     PetscBool isseq;
548 
549     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
550     if (isseq) PetscCall(VecView_Seq(v, viewer));
551     else       PetscCall(VecView_MPI(v, viewer));
552   }
553   PetscFunctionReturn(0);
554 }
555 
556 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
557 {
558   DM                dm;
559   MPI_Comm          comm;
560   PetscViewerFormat format;
561   Vec               v;
562   PetscBool         isvtk, ishdf5;
563 
564   PetscFunctionBegin;
565   PetscCall(VecGetDM(originalv, &dm));
566   PetscCall(PetscObjectGetComm((PetscObject) originalv, &comm));
567   PetscCheck(dm,comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
568   PetscCall(PetscViewerGetFormat(viewer, &format));
569   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
570   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,  &isvtk));
571   if (format == PETSC_VIEWER_NATIVE) {
572     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
573     /* this need a better fix */
574     if (dm->useNatural) {
575       if (dm->sfNatural) {
576         const char *vecname;
577         PetscInt    n, nroots;
578 
579         PetscCall(VecGetLocalSize(originalv, &n));
580         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
581         if (n == nroots) {
582           PetscCall(DMGetGlobalVector(dm, &v));
583           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
584           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
585           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
586           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
587         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
588       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
589     } else v = originalv;
590   } else v = originalv;
591 
592   if (ishdf5) {
593 #if defined(PETSC_HAVE_HDF5)
594     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
595 #else
596     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
597 #endif
598   } else if (isvtk) {
599     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
600   } else {
601     PetscBool isseq;
602 
603     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
604     if (isseq) PetscCall(VecView_Seq(v, viewer));
605     else       PetscCall(VecView_MPI(v, viewer));
606   }
607   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
608   PetscFunctionReturn(0);
609 }
610 
611 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
612 {
613   DM             dm;
614   PetscBool      ishdf5;
615 
616   PetscFunctionBegin;
617   PetscCall(VecGetDM(v, &dm));
618   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
619   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
620   if (ishdf5) {
621     DM          dmBC;
622     Vec         gv;
623     const char *name;
624 
625     PetscCall(DMGetOutputDM(dm, &dmBC));
626     PetscCall(DMGetGlobalVector(dmBC, &gv));
627     PetscCall(PetscObjectGetName((PetscObject) v, &name));
628     PetscCall(PetscObjectSetName((PetscObject) gv, name));
629     PetscCall(VecLoad_Default(gv, viewer));
630     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
631     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
632     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
633   } else PetscCall(VecLoad_Default(v, viewer));
634   PetscFunctionReturn(0);
635 }
636 
637 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
638 {
639   DM             dm;
640   PetscBool      ishdf5,isexodusii;
641 
642   PetscFunctionBegin;
643   PetscCall(VecGetDM(v, &dm));
644   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
645   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
646   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
647   if (ishdf5) {
648 #if defined(PETSC_HAVE_HDF5)
649     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
650 #else
651     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
652 #endif
653   } else if (isexodusii) {
654 #if defined(PETSC_HAVE_EXODUSII)
655     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
656 #else
657     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
658 #endif
659   } else PetscCall(VecLoad_Default(v, viewer));
660   PetscFunctionReturn(0);
661 }
662 
663 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
664 {
665   DM                dm;
666   PetscViewerFormat format;
667   PetscBool         ishdf5;
668 
669   PetscFunctionBegin;
670   PetscCall(VecGetDM(originalv, &dm));
671   PetscCheck(dm,PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
672   PetscCall(PetscViewerGetFormat(viewer, &format));
673   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
674   if (format == PETSC_VIEWER_NATIVE) {
675     if (dm->useNatural) {
676       if (dm->sfNatural) {
677         if (ishdf5) {
678 #if defined(PETSC_HAVE_HDF5)
679           Vec         v;
680           const char *vecname;
681 
682           PetscCall(DMGetGlobalVector(dm, &v));
683           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
684           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
685           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
686           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
687           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
688           PetscCall(DMRestoreGlobalVector(dm, &v));
689 #else
690           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
691 #endif
692         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
693       }
694     } else PetscCall(VecLoad_Default(originalv, viewer));
695   }
696   PetscFunctionReturn(0);
697 }
698 
699 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
700 {
701   PetscSection       coordSection;
702   Vec                coordinates;
703   DMLabel            depthLabel, celltypeLabel;
704   const char        *name[4];
705   const PetscScalar *a;
706   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
707 
708   PetscFunctionBegin;
709   PetscCall(DMGetDimension(dm, &dim));
710   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
711   PetscCall(DMGetCoordinateSection(dm, &coordSection));
712   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
713   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
714   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
715   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
716   PetscCall(VecGetArrayRead(coordinates, &a));
717   name[0]     = "vertex";
718   name[1]     = "edge";
719   name[dim-1] = "face";
720   name[dim]   = "cell";
721   for (c = cStart; c < cEnd; ++c) {
722     PetscInt *closure = NULL;
723     PetscInt  closureSize, cl, ct;
724 
725     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
726     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
727     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
728     PetscCall(PetscViewerASCIIPushTab(viewer));
729     for (cl = 0; cl < closureSize*2; cl += 2) {
730       PetscInt point = closure[cl], depth, dof, off, d, p;
731 
732       if ((point < pStart) || (point >= pEnd)) continue;
733       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
734       if (!dof) continue;
735       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
736       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
737       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
738       for (p = 0; p < dof/dim; ++p) {
739         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
740         for (d = 0; d < dim; ++d) {
741           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
742           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d])));
743         }
744         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
745       }
746       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
747     }
748     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
749     PetscCall(PetscViewerASCIIPopTab(viewer));
750   }
751   PetscCall(VecRestoreArrayRead(coordinates, &a));
752   PetscFunctionReturn(0);
753 }
754 
755 typedef enum {CS_CARTESIAN, CS_POLAR, CS_CYLINDRICAL, CS_SPHERICAL} CoordSystem;
756 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
757 
758 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
759 {
760   PetscInt       i;
761 
762   PetscFunctionBegin;
763   if (dim > 3) {
764     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) PetscRealPart(x[i])));
765   } else {
766     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
767 
768     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
769     switch (cs) {
770       case CS_CARTESIAN: for (i = 0; i < dim; ++i) trcoords[i] = coords[i];break;
771       case CS_POLAR:
772         PetscCheck(dim == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
773         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
774         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
775         break;
776       case CS_CYLINDRICAL:
777         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
778         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
779         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
780         trcoords[2] = coords[2];
781         break;
782       case CS_SPHERICAL:
783         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
784         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
785         trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
786         trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
787         break;
788     }
789     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) trcoords[i]));
790   }
791   PetscFunctionReturn(0);
792 }
793 
794 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
795 {
796   DM_Plex          *mesh = (DM_Plex*) dm->data;
797   DM                cdm, cdmCell;
798   PetscSection      coordSection, coordSectionCell;
799   Vec               coordinates, coordinatesCell;
800   PetscViewerFormat format;
801 
802   PetscFunctionBegin;
803   PetscCall(DMGetCoordinateDM(dm, &cdm));
804   PetscCall(DMGetCoordinateSection(dm, &coordSection));
805   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
806   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
807   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
808   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
809   PetscCall(PetscViewerGetFormat(viewer, &format));
810   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
811     const char *name;
812     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
813     PetscInt    pStart, pEnd, p, numLabels, l;
814     PetscMPIInt rank, size;
815 
816     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
817     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
818     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
819     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
820     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
821     PetscCall(DMGetDimension(dm, &dim));
822     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
823     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
824     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
825     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
826     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
827     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
828     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
829     for (p = pStart; p < pEnd; ++p) {
830       PetscInt dof, off, s;
831 
832       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
833       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
834       for (s = off; s < off+dof; ++s) {
835         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
836       }
837     }
838     PetscCall(PetscViewerFlush(viewer));
839     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
840     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
841     for (p = pStart; p < pEnd; ++p) {
842       PetscInt dof, off, c;
843 
844       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
845       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
846       for (c = off; c < off+dof; ++c) {
847         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
848       }
849     }
850     PetscCall(PetscViewerFlush(viewer));
851     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
852     if (coordSection && coordinates) {
853       CoordSystem        cs = CS_CARTESIAN;
854       const PetscScalar *array, *arrayCell = NULL;
855       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
856       PetscMPIInt        rank;
857       const char        *name;
858 
859       PetscCall(PetscOptionsGetEnum(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *) &cs, NULL));
860       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
861       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
862       PetscCheck(Nf == 1,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
863       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
864       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
865       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
866       pStart =  PetscMin(pvStart, pcStart);
867       pEnd   =  PetscMax(pvEnd,   pcEnd);
868       PetscCall(PetscObjectGetName((PetscObject) coordinates, &name));
869       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
870       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
871       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
872 
873       PetscCall(VecGetArrayRead(coordinates, &array));
874       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
875       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
876       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
877       for (p = pStart; p < pEnd; ++p) {
878         PetscInt dof, off;
879 
880         if (p >= pvStart && p < pvEnd) {
881           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
882           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
883           if (dof) {
884             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
885             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
886             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
887           }
888         }
889         if (cdmCell && p >= pcStart && p < pcEnd) {
890           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
891           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
892           if (dof) {
893             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
894             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
895             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
896           }
897         }
898       }
899       PetscCall(PetscViewerFlush(viewer));
900       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
901       PetscCall(VecRestoreArrayRead(coordinates, &array));
902       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
903     }
904     PetscCall(DMGetNumLabels(dm, &numLabels));
905     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
906     for (l = 0; l < numLabels; ++l) {
907       DMLabel     label;
908       PetscBool   isdepth;
909       const char *name;
910 
911       PetscCall(DMGetLabelName(dm, l, &name));
912       PetscCall(PetscStrcmp(name, "depth", &isdepth));
913       if (isdepth) continue;
914       PetscCall(DMGetLabel(dm, name, &label));
915       PetscCall(DMLabelView(label, viewer));
916     }
917     if (size > 1) {
918       PetscSF sf;
919 
920       PetscCall(DMGetPointSF(dm, &sf));
921       PetscCall(PetscSFView(sf, viewer));
922     }
923     PetscCall(PetscViewerFlush(viewer));
924   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
925     const char  *name, *color;
926     const char  *defcolors[3]  = {"gray", "orange", "green"};
927     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
928     char         lname[PETSC_MAX_PATH_LEN];
929     PetscReal    scale         = 2.0;
930     PetscReal    tikzscale     = 1.0;
931     PetscBool    useNumbers    = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
932     double       tcoords[3];
933     PetscScalar *coords;
934     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
935     PetscMPIInt  rank, size;
936     char         **names, **colors, **lcolors;
937     PetscBool    flg, lflg;
938     PetscBT      wp = NULL;
939     PetscInt     pEnd, pStart;
940 
941     PetscCall(DMGetDimension(dm, &dim));
942     PetscCall(DMPlexGetDepth(dm, &depth));
943     PetscCall(DMGetNumLabels(dm, &numLabels));
944     numLabels  = PetscMax(numLabels, 10);
945     numColors  = 10;
946     numLColors = 10;
947     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
948     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
949     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
950     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
951     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
952     for (d = 0; d < 4; ++d) drawColors[d]  = PETSC_TRUE;
953     n = 4;
954     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
955     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
956     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
957     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
958     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
959     if (!useLabels) numLabels = 0;
960     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
961     if (!useColors) {
962       numColors = 3;
963       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
964     }
965     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
966     if (!useColors) {
967       numLColors = 4;
968       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
969     }
970     PetscCall(PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
971     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
972     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
973     PetscCheck(!flg || !plotEdges || depth >= dim,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
974     if (depth < dim) plotEdges = PETSC_FALSE;
975     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
976 
977     /* filter points with labelvalue != labeldefaultvalue */
978     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
979     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
980     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
981     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
982     if (lflg) {
983       DMLabel lbl;
984 
985       PetscCall(DMGetLabel(dm, lname, &lbl));
986       if (lbl) {
987         PetscInt val, defval;
988 
989         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
990         PetscCall(PetscBTCreate(pEnd-pStart, &wp));
991         for (c = pStart;  c < pEnd; c++) {
992           PetscInt *closure = NULL;
993           PetscInt  closureSize;
994 
995           PetscCall(DMLabelGetValue(lbl, c, &val));
996           if (val == defval) continue;
997 
998           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
999           for (p = 0; p < closureSize*2; p += 2) {
1000             PetscCall(PetscBTSet(wp, closure[p] - pStart));
1001           }
1002           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1003         }
1004       }
1005     }
1006 
1007     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1008     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1009     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1010     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1011 \\documentclass[tikz]{standalone}\n\n\
1012 \\usepackage{pgflibraryshapes}\n\
1013 \\usetikzlibrary{backgrounds}\n\
1014 \\usetikzlibrary{arrows}\n\
1015 \\begin{document}\n"));
1016     if (size > 1) {
1017       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1018       for (p = 0; p < size; ++p) {
1019         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size-1) ? ", and " :  ", "));
1020         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p%numColors], p));
1021       }
1022       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1023     }
1024     if (drawHasse) {
1025       PetscInt maxStratum = PetscMax(vEnd-vStart, PetscMax(eEnd-eStart, cEnd-cStart));
1026 
1027       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1028       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd-1));
1029       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd-vStart));
1030       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum-(vEnd-vStart))/2.));
1031       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1032       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd-1));
1033       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum-(eEnd-eStart))/2.));
1034       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd-eStart));
1035       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1036       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd-1));
1037       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd-cStart));
1038       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum-(cEnd-cStart))/2.));
1039     }
1040     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale));
1041 
1042     /* Plot vertices */
1043     PetscCall(VecGetArray(coordinates, &coords));
1044     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1045     for (v = vStart; v < vEnd; ++v) {
1046       PetscInt  off, dof, d;
1047       PetscBool isLabeled = PETSC_FALSE;
1048 
1049       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
1050       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1051       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1052       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1053       PetscCheck(dof <= 3,PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3",v,dof);
1054       for (d = 0; d < dof; ++d) {
1055         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1056         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1057       }
1058       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1059       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1060       for (d = 0; d < dof; ++d) {
1061         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1062         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]));
1063       }
1064       if (drawHasse) color = colors[0%numColors];
1065       else           color = colors[rank%numColors];
1066       for (l = 0; l < numLabels; ++l) {
1067         PetscInt val;
1068         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1069         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1070       }
1071       if (drawNumbers[0]) {
1072         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1073       } else if (drawColors[0]) {
1074         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1075       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1076     }
1077     PetscCall(VecRestoreArray(coordinates, &coords));
1078     PetscCall(PetscViewerFlush(viewer));
1079     /* Plot edges */
1080     if (plotEdges) {
1081       PetscCall(VecGetArray(coordinates, &coords));
1082       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1083       for (e = eStart; e < eEnd; ++e) {
1084         const PetscInt *cone;
1085         PetscInt        coneSize, offA, offB, dof, d;
1086 
1087         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1088         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1089         PetscCheck(coneSize == 2,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1090         PetscCall(DMPlexGetCone(dm, e, &cone));
1091         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1092         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1093         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1094         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1095         for (d = 0; d < dof; ++d) {
1096           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
1097           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1098         }
1099         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1100         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1101         for (d = 0; d < dof; ++d) {
1102           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1103           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1104         }
1105         if (drawHasse) color = colors[1%numColors];
1106         else           color = colors[rank%numColors];
1107         for (l = 0; l < numLabels; ++l) {
1108           PetscInt val;
1109           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1110           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1111         }
1112         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1113       }
1114       PetscCall(VecRestoreArray(coordinates, &coords));
1115       PetscCall(PetscViewerFlush(viewer));
1116       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1117     }
1118     /* Plot cells */
1119     if (dim == 3 || !drawNumbers[1]) {
1120       for (e = eStart; e < eEnd; ++e) {
1121         const PetscInt *cone;
1122 
1123         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1124         color = colors[rank%numColors];
1125         for (l = 0; l < numLabels; ++l) {
1126           PetscInt val;
1127           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1128           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1129         }
1130         PetscCall(DMPlexGetCone(dm, e, &cone));
1131         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1132       }
1133     } else {
1134        DMPolytopeType ct;
1135 
1136       /* Drawing a 2D polygon */
1137       for (c = cStart; c < cEnd; ++c) {
1138         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1139         PetscCall(DMPlexGetCellType(dm, c, &ct));
1140         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR ||
1141             ct == DM_POLYTOPE_TRI_PRISM_TENSOR ||
1142             ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1143           const PetscInt *cone;
1144           PetscInt        coneSize, e;
1145 
1146           PetscCall(DMPlexGetCone(dm, c, &cone));
1147           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1148           for (e = 0; e < coneSize; ++e) {
1149             const PetscInt *econe;
1150 
1151             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1152             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank%numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1153           }
1154         } else {
1155           PetscInt *closure = NULL;
1156           PetscInt  closureSize, Nv = 0, v;
1157 
1158           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1159           for (p = 0; p < closureSize*2; p += 2) {
1160             const PetscInt point = closure[p];
1161 
1162             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1163           }
1164           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]));
1165           for (v = 0; v <= Nv; ++v) {
1166             const PetscInt vertex = closure[v%Nv];
1167 
1168             if (v > 0) {
1169               if (plotEdges) {
1170                 const PetscInt *edge;
1171                 PetscInt        endpoints[2], ne;
1172 
1173                 endpoints[0] = closure[v-1]; endpoints[1] = vertex;
1174                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1175                 PetscCheck(ne == 1,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1176                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1177                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1178               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1179             }
1180             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1181           }
1182           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1183           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1184         }
1185       }
1186     }
1187     for (c = cStart; c < cEnd; ++c) {
1188       double             ccoords[3] = {0.0, 0.0, 0.0};
1189       PetscBool          isLabeled  = PETSC_FALSE;
1190       PetscScalar       *cellCoords = NULL;
1191       const PetscScalar *array;
1192       PetscInt           numCoords, cdim, d;
1193       PetscBool          isDG;
1194 
1195       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
1196       PetscCall(DMGetCoordinateDim(dm, &cdim));
1197       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1198       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1199       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1200       for (p = 0; p < numCoords/cdim; ++p) {
1201         for (d = 0; d < cdim; ++d) {
1202           tcoords[d] = (double) (scale*PetscRealPart(cellCoords[p*cdim+d]));
1203           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1204         }
1205         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1206         if (cdim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1207         for (d = 0; d < dim; ++d) {ccoords[d] += tcoords[d];}
1208       }
1209       for (d = 0; d < cdim; ++d) {ccoords[d] /= (numCoords/cdim);}
1210       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1211       for (d = 0; d < cdim; ++d) {
1212         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1213         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]));
1214       }
1215       if (drawHasse) color = colors[depth%numColors];
1216       else           color = colors[rank%numColors];
1217       for (l = 0; l < numLabels; ++l) {
1218         PetscInt val;
1219         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1220         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1221       }
1222       if (drawNumbers[dim]) {
1223         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1224       } else if (drawColors[dim]) {
1225         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1226       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1227     }
1228     if (drawHasse) {
1229       color = colors[depth%numColors];
1230       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1231       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1232       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1233       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1234       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1235 
1236       color = colors[1%numColors];
1237       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1238       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1239       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1241       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1242 
1243       color = colors[0%numColors];
1244       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1245       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1246       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1247       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1248       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1249 
1250       for (p = pStart; p < pEnd; ++p) {
1251         const PetscInt *cone;
1252         PetscInt        coneSize, cp;
1253 
1254         PetscCall(DMPlexGetCone(dm, p, &cone));
1255         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1256         for (cp = 0; cp < coneSize; ++cp) {
1257           PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1258         }
1259       }
1260     }
1261     PetscCall(PetscViewerFlush(viewer));
1262     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1263     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1264     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1265     for (l = 0; l < numLabels;  ++l) PetscCall(PetscFree(names[l]));
1266     for (c = 0; c < numColors;  ++c) PetscCall(PetscFree(colors[c]));
1267     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1268     PetscCall(PetscFree3(names, colors, lcolors));
1269     PetscCall(PetscBTDestroy(&wp));
1270   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1271     Vec                    cown,acown;
1272     VecScatter             sct;
1273     ISLocalToGlobalMapping g2l;
1274     IS                     gid,acis;
1275     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
1276     MPI_Group              ggroup,ngroup;
1277     PetscScalar            *array,nid;
1278     const PetscInt         *idxs;
1279     PetscInt               *idxs2,*start,*adjacency,*work;
1280     PetscInt64             lm[3],gm[3];
1281     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
1282     PetscMPIInt            d1,d2,rank;
1283 
1284     PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
1285     PetscCallMPI(MPI_Comm_rank(comm,&rank));
1286 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1287     PetscCallMPI(MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm));
1288 #endif
1289     if (ncomm != MPI_COMM_NULL) {
1290       PetscCallMPI(MPI_Comm_group(comm,&ggroup));
1291       PetscCallMPI(MPI_Comm_group(ncomm,&ngroup));
1292       d1   = 0;
1293       PetscCallMPI(MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2));
1294       nid  = d2;
1295       PetscCallMPI(MPI_Group_free(&ggroup));
1296       PetscCallMPI(MPI_Group_free(&ngroup));
1297       PetscCallMPI(MPI_Comm_free(&ncomm));
1298     } else nid = 0.0;
1299 
1300     /* Get connectivity */
1301     PetscCall(DMPlexGetVTKCellHeight(dm,&cellHeight));
1302     PetscCall(DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid));
1303 
1304     /* filter overlapped local cells */
1305     PetscCall(DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd));
1306     PetscCall(ISGetIndices(gid,&idxs));
1307     PetscCall(ISGetLocalSize(gid,&cum));
1308     PetscCall(PetscMalloc1(cum,&idxs2));
1309     for (c = cStart, cum = 0; c < cEnd; c++) {
1310       if (idxs[c-cStart] < 0) continue;
1311       idxs2[cum++] = idxs[c-cStart];
1312     }
1313     PetscCall(ISRestoreIndices(gid,&idxs));
1314     PetscCheck(numVertices == cum,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %" PetscInt_FMT " != %" PetscInt_FMT,numVertices,cum);
1315     PetscCall(ISDestroy(&gid));
1316     PetscCall(ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid));
1317 
1318     /* support for node-aware cell locality */
1319     PetscCall(ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis));
1320     PetscCall(VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown));
1321     PetscCall(VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown));
1322     PetscCall(VecGetArray(cown,&array));
1323     for (c = 0; c < numVertices; c++) array[c] = nid;
1324     PetscCall(VecRestoreArray(cown,&array));
1325     PetscCall(VecScatterCreate(cown,acis,acown,NULL,&sct));
1326     PetscCall(VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1327     PetscCall(VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1328     PetscCall(ISDestroy(&acis));
1329     PetscCall(VecScatterDestroy(&sct));
1330     PetscCall(VecDestroy(&cown));
1331 
1332     /* compute edgeCut */
1333     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
1334     PetscCall(PetscMalloc1(cum,&work));
1335     PetscCall(ISLocalToGlobalMappingCreateIS(gid,&g2l));
1336     PetscCall(ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH));
1337     PetscCall(ISDestroy(&gid));
1338     PetscCall(VecGetArray(acown,&array));
1339     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1340       PetscInt totl;
1341 
1342       totl = start[c+1]-start[c];
1343       PetscCall(ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work));
1344       for (i = 0; i < totl; i++) {
1345         if (work[i] < 0) {
1346           ect  += 1;
1347           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1348         }
1349       }
1350     }
1351     PetscCall(PetscFree(work));
1352     PetscCall(VecRestoreArray(acown,&array));
1353     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1354     lm[1] = -numVertices;
1355     PetscCall(MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm));
1356     PetscCall(PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT,-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]));
1357     lm[0] = ect; /* edgeCut */
1358     lm[1] = ectn; /* node-aware edgeCut */
1359     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1360     PetscCall(MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm));
1361     PetscCall(PetscViewerASCIIPrintf(viewer,", empty %" PetscInt_FMT ")\n",(PetscInt)gm[2]));
1362 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1363     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),gm[0] ? ((double)(gm[1]))/((double)gm[0]) : 1.));
1364 #else
1365     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0));
1366 #endif
1367     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1368     PetscCall(PetscFree(start));
1369     PetscCall(PetscFree(adjacency));
1370     PetscCall(VecDestroy(&acown));
1371   } else {
1372     const char    *name;
1373     PetscInt      *sizes, *hybsizes, *ghostsizes;
1374     PetscInt       locDepth, depth, cellHeight, dim, d;
1375     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1376     PetscInt       numLabels, l, maxSize = 17;
1377     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1378     MPI_Comm       comm;
1379     PetscMPIInt    size, rank;
1380 
1381     PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
1382     PetscCallMPI(MPI_Comm_size(comm, &size));
1383     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1384     PetscCall(DMGetDimension(dm, &dim));
1385     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1386     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1387     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1388     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1389     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1390     PetscCall(DMPlexGetDepth(dm, &locDepth));
1391     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1392     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1393     gcNum = gcEnd - gcStart;
1394     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1395     else                PetscCall(PetscCalloc3(3,    &sizes, 3,    &hybsizes, 3,    &ghostsizes));
1396     for (d = 0; d <= depth; d++) {
1397       PetscInt Nc[2] = {0, 0}, ict;
1398 
1399       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1400       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1401       ict  = ct0;
1402       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1403       ct0  = (DMPolytopeType) ict;
1404       for (p = pStart; p < pEnd; ++p) {
1405         DMPolytopeType ct;
1406 
1407         PetscCall(DMPlexGetCellType(dm, p, &ct));
1408         if (ct == ct0) ++Nc[0];
1409         else           ++Nc[1];
1410       }
1411       if (size < maxSize) {
1412         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm));
1413         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1414         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1415         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1416         for (p = 0; p < size; ++p) {
1417           if (rank == 0) {
1418             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p]+hybsizes[p]));
1419             if (hybsizes[p]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1420             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1421           }
1422         }
1423       } else {
1424         PetscInt locMinMax[2];
1425 
1426         locMinMax[0] = Nc[0]+Nc[1]; locMinMax[1] = Nc[0]+Nc[1];
1427         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1428         locMinMax[0] = Nc[1]; locMinMax[1] = Nc[1];
1429         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1430         if (d == depth) {
1431           locMinMax[0] = gcNum; locMinMax[1] = gcNum;
1432           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1433         }
1434         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1435         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1436         if (hybsizes[0]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1437         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1438       }
1439       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1440     }
1441     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1442     {
1443       const PetscReal *maxCell;
1444       const PetscReal *L;
1445       PetscBool        localized;
1446 
1447       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1448       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1449       if (L || localized) {
1450         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1451         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1452         if (L) {
1453           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1454           for (d = 0; d < dim; ++d) {
1455             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1456             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1457           }
1458           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1459         }
1460         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1461         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1462       }
1463     }
1464     PetscCall(DMGetNumLabels(dm, &numLabels));
1465     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1466     for (l = 0; l < numLabels; ++l) {
1467       DMLabel         label;
1468       const char     *name;
1469       IS              valueIS;
1470       const PetscInt *values;
1471       PetscInt        numValues, v;
1472 
1473       PetscCall(DMGetLabelName(dm, l, &name));
1474       PetscCall(DMGetLabel(dm, name, &label));
1475       PetscCall(DMLabelGetNumValues(label, &numValues));
1476       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1477       PetscCall(DMLabelGetValueIS(label, &valueIS));
1478       PetscCall(ISGetIndices(valueIS, &values));
1479       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1480       for (v = 0; v < numValues; ++v) {
1481         PetscInt size;
1482 
1483         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1484         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1485         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1486       }
1487       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1488       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1489       PetscCall(ISRestoreIndices(valueIS, &values));
1490       PetscCall(ISDestroy(&valueIS));
1491     }
1492     {
1493       char    **labelNames;
1494       PetscInt  Nl = numLabels;
1495       PetscBool flg;
1496 
1497       PetscCall(PetscMalloc1(Nl, &labelNames));
1498       PetscCall(PetscOptionsGetStringArray(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1499       for (l = 0; l < Nl; ++l) {
1500         DMLabel label;
1501 
1502         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1503         if (flg) {
1504           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1505           PetscCall(DMLabelView(label, viewer));
1506         }
1507         PetscCall(PetscFree(labelNames[l]));
1508       }
1509       PetscCall(PetscFree(labelNames));
1510     }
1511     /* If no fields are specified, people do not want to see adjacency */
1512     if (dm->Nf) {
1513       PetscInt f;
1514 
1515       for (f = 0; f < dm->Nf; ++f) {
1516         const char *name;
1517 
1518         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1519         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1520         PetscCall(PetscViewerASCIIPushTab(viewer));
1521         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1522         if (dm->fields[f].adjacency[0]) {
1523           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1524           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1525         } else {
1526           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1527           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1528         }
1529         PetscCall(PetscViewerASCIIPopTab(viewer));
1530       }
1531     }
1532     PetscCall(DMGetCoarseDM(dm, &cdm));
1533     if (cdm) {
1534       PetscCall(PetscViewerASCIIPushTab(viewer));
1535       PetscCall(DMPlexView_Ascii(cdm, viewer));
1536       PetscCall(PetscViewerASCIIPopTab(viewer));
1537     }
1538   }
1539   PetscFunctionReturn(0);
1540 }
1541 
1542 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1543 {
1544   DMPolytopeType ct;
1545   PetscMPIInt    rank;
1546   PetscInt       cdim;
1547 
1548   PetscFunctionBegin;
1549   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1550   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1551   PetscCall(DMGetCoordinateDim(dm, &cdim));
1552   switch (ct) {
1553   case DM_POLYTOPE_SEGMENT:
1554   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1555     switch (cdim) {
1556     case 1:
1557     {
1558       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1559       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1560 
1561       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y,    PetscRealPart(coords[1]), y,    PETSC_DRAW_BLACK));
1562       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y+dy, PetscRealPart(coords[0]), y-dy, PETSC_DRAW_BLACK));
1563       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y+dy, PetscRealPart(coords[1]), y-dy, PETSC_DRAW_BLACK));
1564     }
1565     break;
1566     case 2:
1567     {
1568       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1569       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1570       const PetscReal l  = 0.1/PetscSqrtReal(dx*dx + dy*dy);
1571 
1572       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1573       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0])+l*dx, PetscRealPart(coords[1])+l*dy, PetscRealPart(coords[0])-l*dx, PetscRealPart(coords[1])-l*dy, PETSC_DRAW_BLACK));
1574       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2])+l*dx, PetscRealPart(coords[3])+l*dy, PetscRealPart(coords[2])-l*dx, PetscRealPart(coords[3])-l*dy, PETSC_DRAW_BLACK));
1575     }
1576     break;
1577     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1578     }
1579     break;
1580   case DM_POLYTOPE_TRIANGLE:
1581     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1582                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1583                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1584                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1585     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1586     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1587     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1588     break;
1589   case DM_POLYTOPE_QUADRILATERAL:
1590     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1591                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1592                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1593                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1594     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1595                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1596                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1597                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1598     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1599     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1600     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1601     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1602     break;
1603   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1604   }
1605   PetscFunctionReturn(0);
1606 }
1607 
1608 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1609 {
1610   DMPolytopeType ct;
1611   PetscReal      centroid[2] = {0., 0.};
1612   PetscMPIInt    rank;
1613   PetscInt       fillColor, v, e, d;
1614 
1615   PetscFunctionBegin;
1616   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1617   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1618   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2;
1619   switch (ct) {
1620   case DM_POLYTOPE_TRIANGLE:
1621     {
1622       PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1623 
1624       for (v = 0; v < 3; ++v) {centroid[0] += PetscRealPart(coords[v*2+0])/3.;centroid[1] += PetscRealPart(coords[v*2+1])/3.;}
1625       for (e = 0; e < 3; ++e) {
1626         refCoords[0] = refVertices[e*2+0];
1627         refCoords[1] = refVertices[e*2+1];
1628         for (d = 1; d <= edgeDiv; ++d) {
1629           refCoords[d*2+0] = refCoords[0] + (refVertices[(e+1)%3 * 2 + 0] - refCoords[0])*d/edgeDiv;
1630           refCoords[d*2+1] = refCoords[1] + (refVertices[(e+1)%3 * 2 + 1] - refCoords[1])*d/edgeDiv;
1631         }
1632         PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv+1, refCoords, edgeCoords));
1633         for (d = 0; d < edgeDiv; ++d) {
1634           PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], fillColor, fillColor, fillColor));
1635           PetscCall(PetscDrawLine(draw, edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], PETSC_DRAW_BLACK));
1636         }
1637       }
1638     }
1639     break;
1640   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1641   }
1642   PetscFunctionReturn(0);
1643 }
1644 
1645 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1646 {
1647   PetscDraw          draw;
1648   DM                 cdm;
1649   PetscSection       coordSection;
1650   Vec                coordinates;
1651   const PetscScalar *coords;
1652   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1653   PetscReal         *refCoords, *edgeCoords;
1654   PetscBool          isnull, drawAffine = PETSC_TRUE;
1655   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1656 
1657   PetscFunctionBegin;
1658   PetscCall(DMGetCoordinateDim(dm, &dim));
1659   PetscCheck(dim <= 2,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1660   PetscCall(PetscOptionsGetBool(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1661   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv+1)*dim, &refCoords, (edgeDiv+1)*dim, &edgeCoords));
1662   PetscCall(DMGetCoordinateDM(dm, &cdm));
1663   PetscCall(DMGetLocalSection(cdm, &coordSection));
1664   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1665   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1666   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1667 
1668   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1669   PetscCall(PetscDrawIsNull(draw, &isnull));
1670   if (isnull) PetscFunctionReturn(0);
1671   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1672 
1673   PetscCall(VecGetLocalSize(coordinates, &N));
1674   PetscCall(VecGetArrayRead(coordinates, &coords));
1675   for (c = 0; c < N; c += dim) {
1676     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1677     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1678   }
1679   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1680   PetscCall(MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm)));
1681   PetscCall(MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm)));
1682   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1683   PetscCall(PetscDrawClear(draw));
1684 
1685   for (c = cStart; c < cEnd; ++c) {
1686     PetscScalar *coords = NULL;
1687     PetscInt     numCoords;
1688 
1689     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1690     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1691     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1692     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1693   }
1694   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1695   PetscCall(PetscDrawFlush(draw));
1696   PetscCall(PetscDrawPause(draw));
1697   PetscCall(PetscDrawSave(draw));
1698   PetscFunctionReturn(0);
1699 }
1700 
1701 #if defined(PETSC_HAVE_EXODUSII)
1702 #include <exodusII.h>
1703 #include <petscviewerexodusii.h>
1704 #endif
1705 
1706 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1707 {
1708   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1709   char           name[PETSC_MAX_PATH_LEN];
1710 
1711   PetscFunctionBegin;
1712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1713   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1714   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii));
1715   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
1716   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
1717   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
1718   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
1719   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus));
1720   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERCGNS,     &iscgns));
1721   if (iascii) {
1722     PetscViewerFormat format;
1723     PetscCall(PetscViewerGetFormat(viewer, &format));
1724     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1725     else PetscCall(DMPlexView_Ascii(dm, viewer));
1726   } else if (ishdf5) {
1727 #if defined(PETSC_HAVE_HDF5)
1728     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1729 #else
1730     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1731 #endif
1732   } else if (isvtk) {
1733     PetscCall(DMPlexVTKWriteAll((PetscObject) dm,viewer));
1734   } else if (isdraw) {
1735     PetscCall(DMPlexView_Draw(dm, viewer));
1736   } else if (isglvis) {
1737     PetscCall(DMPlexView_GLVis(dm, viewer));
1738 #if defined(PETSC_HAVE_EXODUSII)
1739   } else if (isexodus) {
1740 /*
1741       exodusII requires that all sets be part of exactly one cell set.
1742       If the dm does not have a "Cell Sets" label defined, we create one
1743       with ID 1, containig all cells.
1744       Note that if the Cell Sets label is defined but does not cover all cells,
1745       we may still have a problem. This should probably be checked here or in the viewer;
1746     */
1747     PetscInt numCS;
1748     PetscCall(DMGetLabelSize(dm,"Cell Sets",&numCS));
1749     if (!numCS) {
1750       PetscInt cStart, cEnd, c;
1751       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1752       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1753       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1754     }
1755     PetscCall(DMView_PlexExodusII(dm, viewer));
1756 #endif
1757 #if defined(PETSC_HAVE_CGNS)
1758   } else if (iscgns) {
1759     PetscCall(DMView_PlexCGNS(dm, viewer));
1760 #endif
1761   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1762   /* Optionally view the partition */
1763   PetscCall(PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg));
1764   if (flg) {
1765     Vec ranks;
1766     PetscCall(DMPlexCreateRankField(dm, &ranks));
1767     PetscCall(VecView(ranks, viewer));
1768     PetscCall(VecDestroy(&ranks));
1769   }
1770   /* Optionally view a label */
1771   PetscCall(PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1772   if (flg) {
1773     DMLabel label;
1774     Vec     val;
1775 
1776     PetscCall(DMGetLabel(dm, name, &label));
1777     PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1778     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1779     PetscCall(VecView(val, viewer));
1780     PetscCall(VecDestroy(&val));
1781   }
1782   PetscFunctionReturn(0);
1783 }
1784 
1785 /*@
1786   DMPlexTopologyView - Saves a DMPlex topology into a file
1787 
1788   Collective on DM
1789 
1790   Input Parameters:
1791 + dm     - The DM whose topology is to be saved
1792 - viewer - The PetscViewer for saving
1793 
1794   Level: advanced
1795 
1796 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1797 @*/
1798 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1799 {
1800   PetscBool      ishdf5;
1801 
1802   PetscFunctionBegin;
1803   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1804   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1805   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1806   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView,viewer,0,0,0));
1807   if (ishdf5) {
1808 #if defined(PETSC_HAVE_HDF5)
1809     PetscViewerFormat format;
1810     PetscCall(PetscViewerGetFormat(viewer, &format));
1811     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1812       IS globalPointNumbering;
1813 
1814       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1815       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1816       PetscCall(ISDestroy(&globalPointNumbering));
1817     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1818 #else
1819     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1820 #endif
1821   }
1822   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView,viewer,0,0,0));
1823   PetscFunctionReturn(0);
1824 }
1825 
1826 /*@
1827   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1828 
1829   Collective on DM
1830 
1831   Input Parameters:
1832 + dm     - The DM whose coordinates are to be saved
1833 - viewer - The PetscViewer for saving
1834 
1835   Level: advanced
1836 
1837 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1838 @*/
1839 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1840 {
1841   PetscBool      ishdf5;
1842 
1843   PetscFunctionBegin;
1844   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1845   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1846   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1847   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView,viewer,0,0,0));
1848   if (ishdf5) {
1849 #if defined(PETSC_HAVE_HDF5)
1850     PetscViewerFormat format;
1851     PetscCall(PetscViewerGetFormat(viewer, &format));
1852     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1853       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1854     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1855 #else
1856     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1857 #endif
1858   }
1859   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView,viewer,0,0,0));
1860   PetscFunctionReturn(0);
1861 }
1862 
1863 /*@
1864   DMPlexLabelsView - Saves DMPlex labels into a file
1865 
1866   Collective on DM
1867 
1868   Input Parameters:
1869 + dm     - The DM whose labels are to be saved
1870 - viewer - The PetscViewer for saving
1871 
1872   Level: advanced
1873 
1874 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1875 @*/
1876 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1877 {
1878   PetscBool      ishdf5;
1879 
1880   PetscFunctionBegin;
1881   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1882   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1883   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1884   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView,viewer,0,0,0));
1885   if (ishdf5) {
1886 #if defined(PETSC_HAVE_HDF5)
1887     IS                globalPointNumbering;
1888     PetscViewerFormat format;
1889 
1890     PetscCall(PetscViewerGetFormat(viewer, &format));
1891     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1892       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1893       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1894       PetscCall(ISDestroy(&globalPointNumbering));
1895     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1896 #else
1897     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1898 #endif
1899   }
1900   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView,viewer,0,0,0));
1901   PetscFunctionReturn(0);
1902 }
1903 
1904 /*@
1905   DMPlexSectionView - Saves a section associated with a DMPlex
1906 
1907   Collective on DM
1908 
1909   Input Parameters:
1910 + dm         - The DM that contains the topology on which the section to be saved is defined
1911 . viewer     - The PetscViewer for saving
1912 - sectiondm  - The DM that contains the section to be saved
1913 
1914   Level: advanced
1915 
1916   Notes:
1917   This function is a wrapper around PetscSectionView(); in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with DMPlexTopologyLoad() and DMPlexSectionLoad(), respectively, this information is used to match section points with topology points.
1918 
1919   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1920 
1921 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1922 @*/
1923 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1924 {
1925   PetscBool      ishdf5;
1926 
1927   PetscFunctionBegin;
1928   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1929   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1930   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1931   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
1932   PetscCall(PetscLogEventBegin(DMPLEX_SectionView,viewer,0,0,0));
1933   if (ishdf5) {
1934 #if defined(PETSC_HAVE_HDF5)
1935     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1936 #else
1937     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1938 #endif
1939   }
1940   PetscCall(PetscLogEventEnd(DMPLEX_SectionView,viewer,0,0,0));
1941   PetscFunctionReturn(0);
1942 }
1943 
1944 /*@
1945   DMPlexGlobalVectorView - Saves a global vector
1946 
1947   Collective on DM
1948 
1949   Input Parameters:
1950 + dm        - The DM that represents the topology
1951 . viewer    - The PetscViewer to save data with
1952 . sectiondm - The DM that contains the global section on which vec is defined
1953 - vec       - The global vector to be saved
1954 
1955   Level: advanced
1956 
1957   Notes:
1958   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1959 
1960   Typical calling sequence
1961 $       DMCreate(PETSC_COMM_WORLD, &dm);
1962 $       DMSetType(dm, DMPLEX);
1963 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1964 $       DMClone(dm, &sectiondm);
1965 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1966 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1967 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1968 $       PetscSectionSetChart(section, pStart, pEnd);
1969 $       PetscSectionSetUp(section);
1970 $       DMSetLocalSection(sectiondm, section);
1971 $       PetscSectionDestroy(&section);
1972 $       DMGetGlobalVector(sectiondm, &vec);
1973 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1974 $       DMPlexTopologyView(dm, viewer);
1975 $       DMPlexSectionView(dm, viewer, sectiondm);
1976 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1977 $       DMRestoreGlobalVector(sectiondm, &vec);
1978 $       DMDestroy(&sectiondm);
1979 $       DMDestroy(&dm);
1980 
1981 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1982 @*/
1983 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
1984 {
1985   PetscBool       ishdf5;
1986 
1987   PetscFunctionBegin;
1988   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1989   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1990   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1991   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1992   /* Check consistency */
1993   {
1994     PetscSection  section;
1995     PetscBool     includesConstraints;
1996     PetscInt      m, m1;
1997 
1998     PetscCall(VecGetLocalSize(vec, &m1));
1999     PetscCall(DMGetGlobalSection(sectiondm, &section));
2000     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2001     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2002     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2003     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2004   }
2005   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2006   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView,viewer,0,0,0));
2007   if (ishdf5) {
2008 #if defined(PETSC_HAVE_HDF5)
2009     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2010 #else
2011     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2012 #endif
2013   }
2014   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView,viewer,0,0,0));
2015   PetscFunctionReturn(0);
2016 }
2017 
2018 /*@
2019   DMPlexLocalVectorView - Saves a local vector
2020 
2021   Collective on DM
2022 
2023   Input Parameters:
2024 + dm        - The DM that represents the topology
2025 . viewer    - The PetscViewer to save data with
2026 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2027 - vec       - The local vector to be saved
2028 
2029   Level: advanced
2030 
2031   Notes:
2032   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2033 
2034   Typical calling sequence
2035 $       DMCreate(PETSC_COMM_WORLD, &dm);
2036 $       DMSetType(dm, DMPLEX);
2037 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2038 $       DMClone(dm, &sectiondm);
2039 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2040 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2041 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2042 $       PetscSectionSetChart(section, pStart, pEnd);
2043 $       PetscSectionSetUp(section);
2044 $       DMSetLocalSection(sectiondm, section);
2045 $       DMGetLocalVector(sectiondm, &vec);
2046 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2047 $       DMPlexTopologyView(dm, viewer);
2048 $       DMPlexSectionView(dm, viewer, sectiondm);
2049 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2050 $       DMRestoreLocalVector(sectiondm, &vec);
2051 $       DMDestroy(&sectiondm);
2052 $       DMDestroy(&dm);
2053 
2054 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2055 @*/
2056 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2057 {
2058   PetscBool       ishdf5;
2059 
2060   PetscFunctionBegin;
2061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2062   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2063   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2064   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2065   /* Check consistency */
2066   {
2067     PetscSection  section;
2068     PetscBool     includesConstraints;
2069     PetscInt      m, m1;
2070 
2071     PetscCall(VecGetLocalSize(vec, &m1));
2072     PetscCall(DMGetLocalSection(sectiondm, &section));
2073     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2074     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2075     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2076     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2077   }
2078   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2079   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView,viewer,0,0,0));
2080   if (ishdf5) {
2081 #if defined(PETSC_HAVE_HDF5)
2082     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2083 #else
2084     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2085 #endif
2086   }
2087   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView,viewer,0,0,0));
2088   PetscFunctionReturn(0);
2089 }
2090 
2091 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2092 {
2093   PetscBool      ishdf5;
2094 
2095   PetscFunctionBegin;
2096   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2097   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2098   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5));
2099   if (ishdf5) {
2100 #if defined(PETSC_HAVE_HDF5)
2101     PetscViewerFormat format;
2102     PetscCall(PetscViewerGetFormat(viewer, &format));
2103     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2104       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2105     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2106       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2107     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2108     PetscFunctionReturn(0);
2109 #else
2110     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2111 #endif
2112   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2113 }
2114 
2115 /*@
2116   DMPlexTopologyLoad - Loads a topology into a DMPlex
2117 
2118   Collective on DM
2119 
2120   Input Parameters:
2121 + dm     - The DM into which the topology is loaded
2122 - viewer - The PetscViewer for the saved topology
2123 
2124   Output Parameters:
2125 . globalToLocalPointSF - The PetscSF that pushes points in [0, N) to the associated points in the loaded plex, where N is the global number of points; NULL if unneeded
2126 
2127   Level: advanced
2128 
2129 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2130 @*/
2131 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2132 {
2133   PetscBool      ishdf5;
2134 
2135   PetscFunctionBegin;
2136   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2137   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2138   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2139   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2140   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad,viewer,0,0,0));
2141   if (ishdf5) {
2142 #if defined(PETSC_HAVE_HDF5)
2143     PetscViewerFormat format;
2144     PetscCall(PetscViewerGetFormat(viewer, &format));
2145     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2146       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2147     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2148 #else
2149     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2150 #endif
2151   }
2152   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad,viewer,0,0,0));
2153   PetscFunctionReturn(0);
2154 }
2155 
2156 /*@
2157   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2158 
2159   Collective on DM
2160 
2161   Input Parameters:
2162 + dm     - The DM into which the coordinates are loaded
2163 . viewer - The PetscViewer for the saved coordinates
2164 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2165 
2166   Level: advanced
2167 
2168 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2169 @*/
2170 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2171 {
2172   PetscBool      ishdf5;
2173 
2174   PetscFunctionBegin;
2175   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2176   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2177   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2178   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2179   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2180   if (ishdf5) {
2181 #if defined(PETSC_HAVE_HDF5)
2182     PetscViewerFormat format;
2183     PetscCall(PetscViewerGetFormat(viewer, &format));
2184     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2185       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2186     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2187 #else
2188     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2189 #endif
2190   }
2191   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2192   PetscFunctionReturn(0);
2193 }
2194 
2195 /*@
2196   DMPlexLabelsLoad - Loads labels into a DMPlex
2197 
2198   Collective on DM
2199 
2200   Input Parameters:
2201 + dm     - The DM into which the labels are loaded
2202 . viewer - The PetscViewer for the saved labels
2203 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2204 
2205   Level: advanced
2206 
2207   Notes:
2208   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2209 
2210 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2211 @*/
2212 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2213 {
2214   PetscBool      ishdf5;
2215 
2216   PetscFunctionBegin;
2217   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2218   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2219   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2220   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2221   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad,viewer,0,0,0));
2222   if (ishdf5) {
2223 #if defined(PETSC_HAVE_HDF5)
2224     PetscViewerFormat format;
2225 
2226     PetscCall(PetscViewerGetFormat(viewer, &format));
2227     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2228       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2229     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2230 #else
2231     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2232 #endif
2233   }
2234   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad,viewer,0,0,0));
2235   PetscFunctionReturn(0);
2236 }
2237 
2238 /*@
2239   DMPlexSectionLoad - Loads section into a DMPlex
2240 
2241   Collective on DM
2242 
2243   Input Parameters:
2244 + dm          - The DM that represents the topology
2245 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2246 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2247 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2248 
2249   Output Parameters
2250 + globalDofSF - The SF that migrates any on-disk Vec data associated with sectionA into a global Vec associated with the sectiondm's global section (NULL if not needed)
2251 - localDofSF  - The SF that migrates any on-disk Vec data associated with sectionA into a local Vec associated with the sectiondm's local section (NULL if not needed)
2252 
2253   Level: advanced
2254 
2255   Notes:
2256   This function is a wrapper around PetscSectionLoad(); it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in dm. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2257 
2258   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2259 
2260   The output parameter, globalDofSF (localDofSF), can later be used with DMPlexGlobalVectorLoad() (DMPlexLocalVectorLoad()) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2261 
2262   Example using 2 processes:
2263 $  NX (number of points on dm): 4
2264 $  sectionA                   : the on-disk section
2265 $  vecA                       : a vector associated with sectionA
2266 $  sectionB                   : sectiondm's local section constructed in this function
2267 $  vecB (local)               : a vector associated with sectiondm's local section
2268 $  vecB (global)              : a vector associated with sectiondm's global section
2269 $
2270 $                                     rank 0    rank 1
2271 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2272 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2273 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2274 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2275 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2276 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2277 $  sectionB->atlasDof             :     1 0 1 | 1 3
2278 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2279 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2280 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2281 $
2282 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2283 
2284 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2285 @*/
2286 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2287 {
2288   PetscBool      ishdf5;
2289 
2290   PetscFunctionBegin;
2291   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2292   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2293   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2294   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2295   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2296   if (localDofSF) PetscValidPointer(localDofSF, 6);
2297   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2298   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad,viewer,0,0,0));
2299   if (ishdf5) {
2300 #if defined(PETSC_HAVE_HDF5)
2301     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2302 #else
2303     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2304 #endif
2305   }
2306   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad,viewer,0,0,0));
2307   PetscFunctionReturn(0);
2308 }
2309 
2310 /*@
2311   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2312 
2313   Collective on DM
2314 
2315   Input Parameters:
2316 + dm        - The DM that represents the topology
2317 . viewer    - The PetscViewer that represents the on-disk vector data
2318 . sectiondm - The DM that contains the global section on which vec is defined
2319 . sf        - The SF that migrates the on-disk vector data into vec
2320 - vec       - The global vector to set values of
2321 
2322   Level: advanced
2323 
2324   Notes:
2325   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2326 
2327   Typical calling sequence
2328 $       DMCreate(PETSC_COMM_WORLD, &dm);
2329 $       DMSetType(dm, DMPLEX);
2330 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2331 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2332 $       DMClone(dm, &sectiondm);
2333 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2334 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2335 $       DMGetGlobalVector(sectiondm, &vec);
2336 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2337 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2338 $       DMRestoreGlobalVector(sectiondm, &vec);
2339 $       PetscSFDestroy(&gsf);
2340 $       PetscSFDestroy(&sfX);
2341 $       DMDestroy(&sectiondm);
2342 $       DMDestroy(&dm);
2343 
2344 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2345 @*/
2346 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2347 {
2348   PetscBool       ishdf5;
2349 
2350   PetscFunctionBegin;
2351   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2352   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2353   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2354   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2355   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2356   /* Check consistency */
2357   {
2358     PetscSection  section;
2359     PetscBool     includesConstraints;
2360     PetscInt      m, m1;
2361 
2362     PetscCall(VecGetLocalSize(vec, &m1));
2363     PetscCall(DMGetGlobalSection(sectiondm, &section));
2364     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2365     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2366     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2367     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2368   }
2369   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2370   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2371   if (ishdf5) {
2372 #if defined(PETSC_HAVE_HDF5)
2373     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2374 #else
2375     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2376 #endif
2377   }
2378   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2379   PetscFunctionReturn(0);
2380 }
2381 
2382 /*@
2383   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2384 
2385   Collective on DM
2386 
2387   Input Parameters:
2388 + dm        - The DM that represents the topology
2389 . viewer    - The PetscViewer that represents the on-disk vector data
2390 . sectiondm - The DM that contains the local section on which vec is defined
2391 . sf        - The SF that migrates the on-disk vector data into vec
2392 - vec       - The local vector to set values of
2393 
2394   Level: advanced
2395 
2396   Notes:
2397   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2398 
2399   Typical calling sequence
2400 $       DMCreate(PETSC_COMM_WORLD, &dm);
2401 $       DMSetType(dm, DMPLEX);
2402 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2403 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2404 $       DMClone(dm, &sectiondm);
2405 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2406 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2407 $       DMGetLocalVector(sectiondm, &vec);
2408 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2409 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2410 $       DMRestoreLocalVector(sectiondm, &vec);
2411 $       PetscSFDestroy(&lsf);
2412 $       PetscSFDestroy(&sfX);
2413 $       DMDestroy(&sectiondm);
2414 $       DMDestroy(&dm);
2415 
2416 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2417 @*/
2418 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2419 {
2420   PetscBool       ishdf5;
2421 
2422   PetscFunctionBegin;
2423   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2424   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2425   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2426   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2427   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2428   /* Check consistency */
2429   {
2430     PetscSection  section;
2431     PetscBool     includesConstraints;
2432     PetscInt      m, m1;
2433 
2434     PetscCall(VecGetLocalSize(vec, &m1));
2435     PetscCall(DMGetLocalSection(sectiondm, &section));
2436     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2437     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2438     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2439     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2440   }
2441   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2442   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2443   if (ishdf5) {
2444 #if defined(PETSC_HAVE_HDF5)
2445     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2446 #else
2447     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2448 #endif
2449   }
2450   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2451   PetscFunctionReturn(0);
2452 }
2453 
2454 PetscErrorCode DMDestroy_Plex(DM dm)
2455 {
2456   DM_Plex       *mesh = (DM_Plex*) dm->data;
2457 
2458   PetscFunctionBegin;
2459   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL));
2460   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL));
2461   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL));
2462   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMInterpolateSolution_C", NULL));
2463   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2464   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C", NULL));
2465   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeGetDefault_C", NULL));
2466   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeSetDefault_C", NULL));
2467   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"MatComputeNeumannOverlap_C",NULL));
2468   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderGetDefault_C", NULL));
2469   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderSetDefault_C", NULL));
2470   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C",NULL));
2471   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexSetOverlap_C",NULL));
2472   if (--mesh->refct > 0) PetscFunctionReturn(0);
2473   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2474   PetscCall(PetscFree(mesh->cones));
2475   PetscCall(PetscFree(mesh->coneOrientations));
2476   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2477   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2478   PetscCall(PetscFree(mesh->supports));
2479   PetscCall(PetscFree(mesh->facesTmp));
2480   PetscCall(PetscFree(mesh->tetgenOpts));
2481   PetscCall(PetscFree(mesh->triangleOpts));
2482   PetscCall(PetscFree(mesh->transformType));
2483   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2484   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2485   PetscCall(ISDestroy(&mesh->subpointIS));
2486   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2487   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2488   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2489   PetscCall(ISDestroy(&mesh->anchorIS));
2490   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2491   PetscCall(PetscFree(mesh->parents));
2492   PetscCall(PetscFree(mesh->childIDs));
2493   PetscCall(PetscSectionDestroy(&mesh->childSection));
2494   PetscCall(PetscFree(mesh->children));
2495   PetscCall(DMDestroy(&mesh->referenceTree));
2496   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2497   PetscCall(PetscFree(mesh->neighbors));
2498   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2499   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2500   PetscCall(PetscFree(mesh));
2501   PetscFunctionReturn(0);
2502 }
2503 
2504 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2505 {
2506   PetscSection           sectionGlobal;
2507   PetscInt               bs = -1, mbs;
2508   PetscInt               localSize, localStart = 0;
2509   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2510   MatType                mtype;
2511   ISLocalToGlobalMapping ltog;
2512 
2513   PetscFunctionBegin;
2514   PetscCall(MatInitializePackage());
2515   mtype = dm->mattype;
2516   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2517   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2518   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2519   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject) dm)));
2520   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2521   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2522   PetscCall(MatSetType(*J, mtype));
2523   PetscCall(MatSetFromOptions(*J));
2524   PetscCall(MatGetBlockSize(*J, &mbs));
2525   if (mbs > 1) bs = mbs;
2526   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2527   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2528   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2529   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2530   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2531   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2532   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2533   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2534   if (!isShell) {
2535     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2536     PetscInt  *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2537     PetscInt  pStart, pEnd, p, dof, cdof;
2538 
2539     PetscCall(DMGetLocalToGlobalMapping(dm,&ltog));
2540 
2541     PetscCall(PetscCalloc1(localSize, &pblocks));
2542     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2543     for (p = pStart; p < pEnd; ++p) {
2544       PetscInt bdof, offset;
2545 
2546       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2547       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2548       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2549       for (PetscInt i=0; i < dof - cdof; i++)
2550         pblocks[offset - localStart + i] = dof - cdof;
2551       dof  = dof < 0 ? -(dof+1) : dof;
2552       bdof = cdof && (dof-cdof) ? 1 : dof;
2553       if (dof) {
2554         if (bs < 0)          {bs = bdof;}
2555         else if (bs != bdof) {bs = 1;}
2556       }
2557     }
2558     /* Must have same blocksize on all procs (some might have no points) */
2559     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2560     bsLocal[1] = bs;
2561     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
2562     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2563     else bs = bsMinMax[0];
2564     bs = PetscMax(1,bs);
2565     PetscCall(MatSetLocalToGlobalMapping(*J,ltog,ltog));
2566     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2567       PetscCall(MatSetBlockSize(*J, bs));
2568       PetscCall(MatSetUp(*J));
2569     } else {
2570       PetscCall(PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu));
2571       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2572       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2573     }
2574     { // Consolidate blocks
2575       PetscInt nblocks = 0;
2576       for (PetscInt i=0; i<localSize; i += PetscMax(1, pblocks[i])) {
2577         if (pblocks[i] == 0) continue;
2578         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2579         for (PetscInt j=1; j<pblocks[i]; j++) {
2580            PetscCheck(pblocks[i+j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i+j]);
2581         }
2582       }
2583       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2584     }
2585     PetscCall(PetscFree(pblocks));
2586   }
2587   PetscCall(MatSetDM(*J, dm));
2588   PetscFunctionReturn(0);
2589 }
2590 
2591 /*@
2592   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2593 
2594   Not collective
2595 
2596   Input Parameter:
2597 . mesh - The DMPlex
2598 
2599   Output Parameters:
2600 . subsection - The subdomain section
2601 
2602   Level: developer
2603 
2604 .seealso:
2605 @*/
2606 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2607 {
2608   DM_Plex       *mesh = (DM_Plex*) dm->data;
2609 
2610   PetscFunctionBegin;
2611   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2612   if (!mesh->subdomainSection) {
2613     PetscSection section;
2614     PetscSF      sf;
2615 
2616     PetscCall(PetscSFCreate(PETSC_COMM_SELF,&sf));
2617     PetscCall(DMGetLocalSection(dm,&section));
2618     PetscCall(PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection));
2619     PetscCall(PetscSFDestroy(&sf));
2620   }
2621   *subsection = mesh->subdomainSection;
2622   PetscFunctionReturn(0);
2623 }
2624 
2625 /*@
2626   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2627 
2628   Not collective
2629 
2630   Input Parameter:
2631 . mesh - The DMPlex
2632 
2633   Output Parameters:
2634 + pStart - The first mesh point
2635 - pEnd   - The upper bound for mesh points
2636 
2637   Level: beginner
2638 
2639 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2640 @*/
2641 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2642 {
2643   DM_Plex       *mesh = (DM_Plex*) dm->data;
2644 
2645   PetscFunctionBegin;
2646   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2647   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2648   PetscFunctionReturn(0);
2649 }
2650 
2651 /*@
2652   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2653 
2654   Not collective
2655 
2656   Input Parameters:
2657 + mesh - The DMPlex
2658 . pStart - The first mesh point
2659 - pEnd   - The upper bound for mesh points
2660 
2661   Output Parameters:
2662 
2663   Level: beginner
2664 
2665 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2666 @*/
2667 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2668 {
2669   DM_Plex       *mesh = (DM_Plex*) dm->data;
2670 
2671   PetscFunctionBegin;
2672   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2673   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2674   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2675   PetscFunctionReturn(0);
2676 }
2677 
2678 /*@
2679   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2680 
2681   Not collective
2682 
2683   Input Parameters:
2684 + mesh - The DMPlex
2685 - p - The point, which must lie in the chart set with DMPlexSetChart()
2686 
2687   Output Parameter:
2688 . size - The cone size for point p
2689 
2690   Level: beginner
2691 
2692 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2693 @*/
2694 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2695 {
2696   DM_Plex       *mesh = (DM_Plex*) dm->data;
2697 
2698   PetscFunctionBegin;
2699   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2700   PetscValidIntPointer(size, 3);
2701   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2702   PetscFunctionReturn(0);
2703 }
2704 
2705 /*@
2706   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2707 
2708   Not collective
2709 
2710   Input Parameters:
2711 + mesh - The DMPlex
2712 . p - The point, which must lie in the chart set with DMPlexSetChart()
2713 - size - The cone size for point p
2714 
2715   Output Parameter:
2716 
2717   Note:
2718   This should be called after DMPlexSetChart().
2719 
2720   Level: beginner
2721 
2722 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2723 @*/
2724 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2725 {
2726   DM_Plex       *mesh = (DM_Plex*) dm->data;
2727 
2728   PetscFunctionBegin;
2729   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2730   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2731   PetscFunctionReturn(0);
2732 }
2733 
2734 /*@
2735   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2736 
2737   Not collective
2738 
2739   Input Parameters:
2740 + mesh - The DMPlex
2741 . p - The point, which must lie in the chart set with DMPlexSetChart()
2742 - size - The additional cone size for point p
2743 
2744   Output Parameter:
2745 
2746   Note:
2747   This should be called after DMPlexSetChart().
2748 
2749   Level: beginner
2750 
2751 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2752 @*/
2753 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2754 {
2755   DM_Plex       *mesh = (DM_Plex*) dm->data;
2756   PetscFunctionBegin;
2757   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2758   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2759   PetscFunctionReturn(0);
2760 }
2761 
2762 /*@C
2763   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2764 
2765   Not collective
2766 
2767   Input Parameters:
2768 + dm - The DMPlex
2769 - p - The point, which must lie in the chart set with DMPlexSetChart()
2770 
2771   Output Parameter:
2772 . cone - An array of points which are on the in-edges for point p
2773 
2774   Level: beginner
2775 
2776   Fortran Notes:
2777   Since it returns an array, this routine is only available in Fortran 90, and you must
2778   include petsc.h90 in your code.
2779   You must also call DMPlexRestoreCone() after you finish using the returned array.
2780   DMPlexRestoreCone() is not needed/available in C.
2781 
2782 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2783 @*/
2784 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2785 {
2786   DM_Plex       *mesh = (DM_Plex*) dm->data;
2787   PetscInt       off;
2788 
2789   PetscFunctionBegin;
2790   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2791   PetscValidPointer(cone, 3);
2792   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2793   *cone = &mesh->cones[off];
2794   PetscFunctionReturn(0);
2795 }
2796 
2797 /*@C
2798   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2799 
2800   Not collective
2801 
2802   Input Parameters:
2803 + dm - The DMPlex
2804 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2805 
2806   Output Parameters:
2807 + pConesSection - PetscSection describing the layout of pCones
2808 - pCones - An array of points which are on the in-edges for the point set p
2809 
2810   Level: intermediate
2811 
2812 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2813 @*/
2814 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2815 {
2816   PetscSection        cs, newcs;
2817   PetscInt            *cones;
2818   PetscInt            *newarr=NULL;
2819   PetscInt            n;
2820 
2821   PetscFunctionBegin;
2822   PetscCall(DMPlexGetCones(dm, &cones));
2823   PetscCall(DMPlexGetConeSection(dm, &cs));
2824   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL));
2825   if (pConesSection) *pConesSection = newcs;
2826   if (pCones) {
2827     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2828     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2829   }
2830   PetscFunctionReturn(0);
2831 }
2832 
2833 /*@
2834   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2835 
2836   Not collective
2837 
2838   Input Parameters:
2839 + dm - The DMPlex
2840 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2841 
2842   Output Parameter:
2843 . expandedPoints - An array of vertices recursively expanded from input points
2844 
2845   Level: advanced
2846 
2847   Notes:
2848   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2849   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2850 
2851 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2852 @*/
2853 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2854 {
2855   IS                  *expandedPointsAll;
2856   PetscInt            depth;
2857 
2858   PetscFunctionBegin;
2859   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2860   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2861   PetscValidPointer(expandedPoints, 3);
2862   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2863   *expandedPoints = expandedPointsAll[0];
2864   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2865   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2866   PetscFunctionReturn(0);
2867 }
2868 
2869 /*@
2870   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
2871 
2872   Not collective
2873 
2874   Input Parameters:
2875 + dm - The DMPlex
2876 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2877 
2878   Output Parameters:
2879 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2880 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2881 - sections - (optional) An array of sections which describe mappings from points to their cone points
2882 
2883   Level: advanced
2884 
2885   Notes:
2886   Like DMPlexGetConeTuple() but recursive.
2887 
2888   Array expandedPoints has size equal to depth. Each expandedPoints[d] contains DAG points with maximum depth d, recursively cone-wise expanded from the input points.
2889   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2890 
2891   Array section has size equal to depth.  Each PetscSection sections[d] realizes mapping from expandedPoints[d+1] (section points) to expandedPoints[d] (section dofs) as follows:
2892   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2893   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2894 
2895 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2896 @*/
2897 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2898 {
2899   const PetscInt      *arr0=NULL, *cone=NULL;
2900   PetscInt            *arr=NULL, *newarr=NULL;
2901   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
2902   IS                  *expandedPoints_;
2903   PetscSection        *sections_;
2904 
2905   PetscFunctionBegin;
2906   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2907   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2908   if (depth) PetscValidIntPointer(depth, 3);
2909   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2910   if (sections) PetscValidPointer(sections, 5);
2911   PetscCall(ISGetLocalSize(points, &n));
2912   PetscCall(ISGetIndices(points, &arr0));
2913   PetscCall(DMPlexGetDepth(dm, &depth_));
2914   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2915   PetscCall(PetscCalloc1(depth_, &sections_));
2916   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
2917   for (d=depth_-1; d>=0; d--) {
2918     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2919     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2920     for (i=0; i<n; i++) {
2921       PetscCall(DMPlexGetDepthStratum(dm, d+1, &start, &end));
2922       if (arr[i] >= start && arr[i] < end) {
2923         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2924         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2925       } else {
2926         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2927       }
2928     }
2929     PetscCall(PetscSectionSetUp(sections_[d]));
2930     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2931     PetscCall(PetscMalloc1(newn, &newarr));
2932     for (i=0; i<n; i++) {
2933       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2934       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2935       if (cn > 1) {
2936         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2937         PetscCall(PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt)));
2938       } else {
2939         newarr[co] = arr[i];
2940       }
2941     }
2942     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2943     arr = newarr;
2944     n = newn;
2945   }
2946   PetscCall(ISRestoreIndices(points, &arr0));
2947   *depth = depth_;
2948   if (expandedPoints) *expandedPoints = expandedPoints_;
2949   else {
2950     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2951     PetscCall(PetscFree(expandedPoints_));
2952   }
2953   if (sections) *sections = sections_;
2954   else {
2955     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2956     PetscCall(PetscFree(sections_));
2957   }
2958   PetscFunctionReturn(0);
2959 }
2960 
2961 /*@
2962   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2963 
2964   Not collective
2965 
2966   Input Parameters:
2967 + dm - The DMPlex
2968 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2969 
2970   Output Parameters:
2971 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2972 . expandedPoints - (optional) An array of recursively expanded cones
2973 - sections - (optional) An array of sections which describe mappings from points to their cone points
2974 
2975   Level: advanced
2976 
2977   Notes:
2978   See DMPlexGetConeRecursive() for details.
2979 
2980 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2981 @*/
2982 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2983 {
2984   PetscInt            d, depth_;
2985 
2986   PetscFunctionBegin;
2987   PetscCall(DMPlexGetDepth(dm, &depth_));
2988   PetscCheck(!depth || *depth == depth_,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2989   if (depth) *depth = 0;
2990   if (expandedPoints) {
2991     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2992     PetscCall(PetscFree(*expandedPoints));
2993   }
2994   if (sections)  {
2995     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2996     PetscCall(PetscFree(*sections));
2997   }
2998   PetscFunctionReturn(0);
2999 }
3000 
3001 /*@
3002   DMPlexSetCone - Set the points on the in-edges for this point in the DAG; that is these are the points that cover the specific point
3003 
3004   Not collective
3005 
3006   Input Parameters:
3007 + mesh - The DMPlex
3008 . p - The point, which must lie in the chart set with DMPlexSetChart()
3009 - cone - An array of points which are on the in-edges for point p
3010 
3011   Output Parameter:
3012 
3013   Note:
3014   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3015 
3016   Level: beginner
3017 
3018 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3019 @*/
3020 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3021 {
3022   DM_Plex       *mesh = (DM_Plex*) dm->data;
3023   PetscInt       pStart, pEnd;
3024   PetscInt       dof, off, c;
3025 
3026   PetscFunctionBegin;
3027   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3028   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3029   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3030   if (dof) PetscValidIntPointer(cone, 3);
3031   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3032   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3033   for (c = 0; c < dof; ++c) {
3034     PetscCheck(!(cone[c] < pStart) && !(cone[c] >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", cone[c], pStart, pEnd);
3035     mesh->cones[off+c] = cone[c];
3036   }
3037   PetscFunctionReturn(0);
3038 }
3039 
3040 /*@C
3041   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3042 
3043   Not collective
3044 
3045   Input Parameters:
3046 + mesh - The DMPlex
3047 - p - The point, which must lie in the chart set with DMPlexSetChart()
3048 
3049   Output Parameter:
3050 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3051                     integer giving the prescription for cone traversal.
3052 
3053   Level: beginner
3054 
3055   Notes:
3056   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3057   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3058   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3059   with the identity.
3060 
3061   Fortran Notes:
3062   Since it returns an array, this routine is only available in Fortran 90, and you must
3063   include petsc.h90 in your code.
3064   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3065   DMPlexRestoreConeOrientation() is not needed/available in C.
3066 
3067 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3068 @*/
3069 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3070 {
3071   DM_Plex       *mesh = (DM_Plex*) dm->data;
3072   PetscInt       off;
3073 
3074   PetscFunctionBegin;
3075   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3076   if (PetscDefined(USE_DEBUG)) {
3077     PetscInt dof;
3078     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3079     if (dof) PetscValidPointer(coneOrientation, 3);
3080   }
3081   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3082 
3083   *coneOrientation = &mesh->coneOrientations[off];
3084   PetscFunctionReturn(0);
3085 }
3086 
3087 /*@
3088   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3089 
3090   Not collective
3091 
3092   Input Parameters:
3093 + mesh - The DMPlex
3094 . p - The point, which must lie in the chart set with DMPlexSetChart()
3095 - coneOrientation - An array of orientations
3096   Output Parameter:
3097 
3098   Notes:
3099   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3100 
3101   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3102 
3103   Level: beginner
3104 
3105 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3106 @*/
3107 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3108 {
3109   DM_Plex       *mesh = (DM_Plex*) dm->data;
3110   PetscInt       pStart, pEnd;
3111   PetscInt       dof, off, c;
3112 
3113   PetscFunctionBegin;
3114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3115   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3116   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3117   if (dof) PetscValidIntPointer(coneOrientation, 3);
3118   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3119   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3120   for (c = 0; c < dof; ++c) {
3121     PetscInt cdof, o = coneOrientation[c];
3122 
3123     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof));
3124     PetscCheck(!o || (o >= -(cdof+1) && o < cdof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ". %" PetscInt_FMT ")", o, -(cdof+1), cdof);
3125     mesh->coneOrientations[off+c] = o;
3126   }
3127   PetscFunctionReturn(0);
3128 }
3129 
3130 /*@
3131   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3132 
3133   Not collective
3134 
3135   Input Parameters:
3136 + mesh - The DMPlex
3137 . p - The point, which must lie in the chart set with DMPlexSetChart()
3138 . conePos - The local index in the cone where the point should be put
3139 - conePoint - The mesh point to insert
3140 
3141   Level: beginner
3142 
3143 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3144 @*/
3145 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3146 {
3147   DM_Plex       *mesh = (DM_Plex*) dm->data;
3148   PetscInt       pStart, pEnd;
3149   PetscInt       dof, off;
3150 
3151   PetscFunctionBegin;
3152   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3153   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3154   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3155   PetscCheck(!(conePoint < pStart) && !(conePoint >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", conePoint, pStart, pEnd);
3156   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3157   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3158   PetscCheck(!(conePos < 0) && !(conePos >= dof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3159   mesh->cones[off+conePos] = conePoint;
3160   PetscFunctionReturn(0);
3161 }
3162 
3163 /*@
3164   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3165 
3166   Not collective
3167 
3168   Input Parameters:
3169 + mesh - The DMPlex
3170 . p - The point, which must lie in the chart set with DMPlexSetChart()
3171 . conePos - The local index in the cone where the point should be put
3172 - coneOrientation - The point orientation to insert
3173 
3174   Level: beginner
3175 
3176   Notes:
3177   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3178 
3179 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3180 @*/
3181 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3182 {
3183   DM_Plex       *mesh = (DM_Plex*) dm->data;
3184   PetscInt       pStart, pEnd;
3185   PetscInt       dof, off;
3186 
3187   PetscFunctionBegin;
3188   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3189   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3190   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3191   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3192   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3193   PetscCheck(!(conePos < 0) && !(conePos >= dof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3194   mesh->coneOrientations[off+conePos] = coneOrientation;
3195   PetscFunctionReturn(0);
3196 }
3197 
3198 /*@
3199   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3200 
3201   Not collective
3202 
3203   Input Parameters:
3204 + mesh - The DMPlex
3205 - p - The point, which must lie in the chart set with DMPlexSetChart()
3206 
3207   Output Parameter:
3208 . size - The support size for point p
3209 
3210   Level: beginner
3211 
3212 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3213 @*/
3214 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3215 {
3216   DM_Plex       *mesh = (DM_Plex*) dm->data;
3217 
3218   PetscFunctionBegin;
3219   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3220   PetscValidIntPointer(size, 3);
3221   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3222   PetscFunctionReturn(0);
3223 }
3224 
3225 /*@
3226   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3227 
3228   Not collective
3229 
3230   Input Parameters:
3231 + mesh - The DMPlex
3232 . p - The point, which must lie in the chart set with DMPlexSetChart()
3233 - size - The support size for point p
3234 
3235   Output Parameter:
3236 
3237   Note:
3238   This should be called after DMPlexSetChart().
3239 
3240   Level: beginner
3241 
3242 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3243 @*/
3244 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3245 {
3246   DM_Plex       *mesh = (DM_Plex*) dm->data;
3247 
3248   PetscFunctionBegin;
3249   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3250   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3251   PetscFunctionReturn(0);
3252 }
3253 
3254 /*@C
3255   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3256 
3257   Not collective
3258 
3259   Input Parameters:
3260 + mesh - The DMPlex
3261 - p - The point, which must lie in the chart set with DMPlexSetChart()
3262 
3263   Output Parameter:
3264 . support - An array of points which are on the out-edges for point p
3265 
3266   Level: beginner
3267 
3268   Fortran Notes:
3269   Since it returns an array, this routine is only available in Fortran 90, and you must
3270   include petsc.h90 in your code.
3271   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3272   DMPlexRestoreSupport() is not needed/available in C.
3273 
3274 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3275 @*/
3276 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3277 {
3278   DM_Plex       *mesh = (DM_Plex*) dm->data;
3279   PetscInt       off;
3280 
3281   PetscFunctionBegin;
3282   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3283   PetscValidPointer(support, 3);
3284   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3285   *support = &mesh->supports[off];
3286   PetscFunctionReturn(0);
3287 }
3288 
3289 /*@
3290   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3291 
3292   Not collective
3293 
3294   Input Parameters:
3295 + mesh - The DMPlex
3296 . p - The point, which must lie in the chart set with DMPlexSetChart()
3297 - support - An array of points which are on the out-edges for point p
3298 
3299   Output Parameter:
3300 
3301   Note:
3302   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3303 
3304   Level: beginner
3305 
3306 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3307 @*/
3308 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3309 {
3310   DM_Plex       *mesh = (DM_Plex*) dm->data;
3311   PetscInt       pStart, pEnd;
3312   PetscInt       dof, off, c;
3313 
3314   PetscFunctionBegin;
3315   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3316   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3317   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3318   if (dof) PetscValidIntPointer(support, 3);
3319   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3320   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3321   for (c = 0; c < dof; ++c) {
3322     PetscCheck(!(support[c] < pStart) && !(support[c] >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", support[c], pStart, pEnd);
3323     mesh->supports[off+c] = support[c];
3324   }
3325   PetscFunctionReturn(0);
3326 }
3327 
3328 /*@
3329   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3330 
3331   Not collective
3332 
3333   Input Parameters:
3334 + mesh - The DMPlex
3335 . p - The point, which must lie in the chart set with DMPlexSetChart()
3336 . supportPos - The local index in the cone where the point should be put
3337 - supportPoint - The mesh point to insert
3338 
3339   Level: beginner
3340 
3341 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3342 @*/
3343 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3344 {
3345   DM_Plex       *mesh = (DM_Plex*) dm->data;
3346   PetscInt       pStart, pEnd;
3347   PetscInt       dof, off;
3348 
3349   PetscFunctionBegin;
3350   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3351   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3352   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3353   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3354   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3355   PetscCheck(!(supportPoint < pStart) && !(supportPoint >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", supportPoint, pStart, pEnd);
3356   PetscCheck(supportPos < dof,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", supportPos, p, dof);
3357   mesh->supports[off+supportPos] = supportPoint;
3358   PetscFunctionReturn(0);
3359 }
3360 
3361 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3362 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3363 {
3364   switch (ct) {
3365     case DM_POLYTOPE_SEGMENT:
3366       if (o == -1) return -2;
3367       break;
3368     case DM_POLYTOPE_TRIANGLE:
3369       if (o == -3) return -1;
3370       if (o == -2) return -3;
3371       if (o == -1) return -2;
3372       break;
3373     case DM_POLYTOPE_QUADRILATERAL:
3374       if (o == -4) return -2;
3375       if (o == -3) return -1;
3376       if (o == -2) return -4;
3377       if (o == -1) return -3;
3378       break;
3379     default: return o;
3380   }
3381   return o;
3382 }
3383 
3384 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3385 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3386 {
3387   switch (ct) {
3388     case DM_POLYTOPE_SEGMENT:
3389       if ((o == -2) || (o == 1)) return -1;
3390       if (o == -1) return 0;
3391       break;
3392     case DM_POLYTOPE_TRIANGLE:
3393       if (o == -3) return -2;
3394       if (o == -2) return -1;
3395       if (o == -1) return -3;
3396       break;
3397     case DM_POLYTOPE_QUADRILATERAL:
3398       if (o == -4) return -2;
3399       if (o == -3) return -1;
3400       if (o == -2) return -4;
3401       if (o == -1) return -3;
3402       break;
3403     default: return o;
3404   }
3405   return o;
3406 }
3407 
3408 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3409 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3410 {
3411   PetscInt       pStart, pEnd, p;
3412 
3413   PetscFunctionBegin;
3414   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3415   for (p = pStart; p < pEnd; ++p) {
3416     const PetscInt *cone, *ornt;
3417     PetscInt        coneSize, c;
3418 
3419     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3420     PetscCall(DMPlexGetCone(dm, p, &cone));
3421     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3422     for (c = 0; c < coneSize; ++c) {
3423       DMPolytopeType ct;
3424       const PetscInt o = ornt[c];
3425 
3426       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3427       switch (ct) {
3428         case DM_POLYTOPE_SEGMENT:
3429           if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3430           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3431           break;
3432         case DM_POLYTOPE_TRIANGLE:
3433           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3434           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3435           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3436           break;
3437         case DM_POLYTOPE_QUADRILATERAL:
3438           if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3439           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3440           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3441           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3442           break;
3443         default: break;
3444       }
3445     }
3446   }
3447   PetscFunctionReturn(0);
3448 }
3449 
3450 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3451 {
3452   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3453   PetscInt       *closure;
3454   const PetscInt *tmp = NULL, *tmpO = NULL;
3455   PetscInt        off = 0, tmpSize, t;
3456 
3457   PetscFunctionBeginHot;
3458   if (ornt) {
3459     PetscCall(DMPlexGetCellType(dm, p, &ct));
3460     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3461   }
3462   if (*points) {
3463     closure = *points;
3464   } else {
3465     PetscInt maxConeSize, maxSupportSize;
3466     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3467     PetscCall(DMGetWorkArray(dm, 2*(PetscMax(maxConeSize, maxSupportSize)+1), MPIU_INT, &closure));
3468   }
3469   if (useCone) {
3470     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3471     PetscCall(DMPlexGetCone(dm, p, &tmp));
3472     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3473   } else {
3474     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3475     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3476   }
3477   if (ct == DM_POLYTOPE_UNKNOWN) {
3478     closure[off++] = p;
3479     closure[off++] = 0;
3480     for (t = 0; t < tmpSize; ++t) {
3481       closure[off++] = tmp[t];
3482       closure[off++] = tmpO ? tmpO[t] : 0;
3483     }
3484   } else {
3485     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3486 
3487     /* We assume that cells with a valid type have faces with a valid type */
3488     closure[off++] = p;
3489     closure[off++] = ornt;
3490     for (t = 0; t < tmpSize; ++t) {
3491       DMPolytopeType ft;
3492 
3493       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3494       closure[off++] = tmp[arr[t]];
3495       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3496     }
3497   }
3498   if (numPoints) *numPoints = tmpSize+1;
3499   if (points)    *points    = closure;
3500   PetscFunctionReturn(0);
3501 }
3502 
3503 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3504 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3505 {
3506   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3507   const PetscInt *cone, *ornt;
3508   PetscInt       *pts,  *closure = NULL;
3509   DMPolytopeType  ft;
3510   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3511   PetscInt        dim, coneSize, c, d, clSize, cl;
3512 
3513   PetscFunctionBeginHot;
3514   PetscCall(DMGetDimension(dm, &dim));
3515   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3516   PetscCall(DMPlexGetCone(dm, point, &cone));
3517   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3518   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3519   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    dim+1)-1)/(maxConeSize-1))    : dim+1;
3520   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim+1)-1)/(maxSupportSize-1)) : dim+1;
3521   maxSize       = PetscMax(coneSeries, supportSeries);
3522   if (*points) {pts  = *points;}
3523   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &pts));
3524   c    = 0;
3525   pts[c++] = point;
3526   pts[c++] = o;
3527   PetscCall(DMPlexGetCellType(dm, cone[arr[0*2+0]], &ft));
3528   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[0*2+1], ornt[0]), useCone, &clSize, &closure));
3529   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3530   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[1*2+1], ornt[1]), useCone, &clSize, &closure));
3531   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3532   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3533   for (d = 2; d < coneSize; ++d) {
3534     PetscCall(DMPlexGetCellType(dm, cone[arr[d*2+0]], &ft));
3535     pts[c++] = cone[arr[d*2+0]];
3536     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]);
3537   }
3538   if (dim >= 3) {
3539     for (d = 2; d < coneSize; ++d) {
3540       const PetscInt  fpoint = cone[arr[d*2+0]];
3541       const PetscInt *fcone, *fornt;
3542       PetscInt        fconeSize, fc, i;
3543 
3544       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3545       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]));
3546       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3547       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3548       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3549       for (fc = 0; fc < fconeSize; ++fc) {
3550         const PetscInt cp = fcone[farr[fc*2+0]];
3551         const PetscInt co = farr[fc*2+1];
3552 
3553         for (i = 0; i < c; i += 2) if (pts[i] == cp) break;
3554         if (i == c) {
3555           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3556           pts[c++] = cp;
3557           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc*2+0]]);
3558         }
3559       }
3560     }
3561   }
3562   *numPoints = c/2;
3563   *points    = pts;
3564   PetscFunctionReturn(0);
3565 }
3566 
3567 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3568 {
3569   DMPolytopeType ct;
3570   PetscInt      *closure, *fifo;
3571   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3572   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3573   PetscInt       depth, maxSize;
3574 
3575   PetscFunctionBeginHot;
3576   PetscCall(DMPlexGetDepth(dm, &depth));
3577   if (depth == 1) {
3578     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3579     PetscFunctionReturn(0);
3580   }
3581   PetscCall(DMPlexGetCellType(dm, p, &ct));
3582   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3583   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3584     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3585     PetscFunctionReturn(0);
3586   }
3587   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3588   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    depth+1)-1)/(maxConeSize-1))    : depth+1;
3589   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth+1)-1)/(maxSupportSize-1)) : depth+1;
3590   maxSize       = PetscMax(coneSeries, supportSeries);
3591   PetscCall(DMGetWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3592   if (*points) {closure = *points;}
3593   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &closure));
3594   closure[closureSize++] = p;
3595   closure[closureSize++] = ornt;
3596   fifo[fifoSize++]       = p;
3597   fifo[fifoSize++]       = ornt;
3598   fifo[fifoSize++]       = ct;
3599   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3600   while (fifoSize - fifoStart) {
3601     const PetscInt       q    = fifo[fifoStart++];
3602     const PetscInt       o    = fifo[fifoStart++];
3603     const DMPolytopeType qt   = (DMPolytopeType) fifo[fifoStart++];
3604     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3605     const PetscInt      *tmp, *tmpO;
3606     PetscInt             tmpSize, t;
3607 
3608     if (PetscDefined(USE_DEBUG)) {
3609       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt)/2;
3610       PetscCheck(!o || !(o >= nO || o < -nO),PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid orientation %" PetscInt_FMT " not in [%" PetscInt_FMT ",%" PetscInt_FMT ") for %s %" PetscInt_FMT, o, -nO, nO, DMPolytopeTypes[qt], q);
3611     }
3612     if (useCone) {
3613       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3614       PetscCall(DMPlexGetCone(dm, q, &tmp));
3615       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3616     } else {
3617       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3618       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3619       tmpO = NULL;
3620     }
3621     for (t = 0; t < tmpSize; ++t) {
3622       const PetscInt ip = useCone && qarr ? qarr[t*2]   : t;
3623       const PetscInt io = useCone && qarr ? qarr[t*2+1] : 0;
3624       const PetscInt cp = tmp[ip];
3625       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3626       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3627       PetscInt       c;
3628 
3629       /* Check for duplicate */
3630       for (c = 0; c < closureSize; c += 2) {
3631         if (closure[c] == cp) break;
3632       }
3633       if (c == closureSize) {
3634         closure[closureSize++] = cp;
3635         closure[closureSize++] = co;
3636         fifo[fifoSize++]       = cp;
3637         fifo[fifoSize++]       = co;
3638         fifo[fifoSize++]       = ct;
3639       }
3640     }
3641   }
3642   PetscCall(DMRestoreWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3643   if (numPoints) *numPoints = closureSize/2;
3644   if (points)    *points    = closure;
3645   PetscFunctionReturn(0);
3646 }
3647 
3648 /*@C
3649   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3650 
3651   Not collective
3652 
3653   Input Parameters:
3654 + dm      - The DMPlex
3655 . p       - The mesh point
3656 - useCone - PETSC_TRUE for the closure, otherwise return the star
3657 
3658   Input/Output Parameter:
3659 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3660            if NULL on input, internal storage will be returned, otherwise the provided array is used
3661 
3662   Output Parameter:
3663 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3664 
3665   Note:
3666   If using internal storage (points is NULL on input), each call overwrites the last output.
3667 
3668   Fortran Note:
3669   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3670 
3671   Level: beginner
3672 
3673 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3674 @*/
3675 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3676 {
3677   PetscFunctionBeginHot;
3678   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3679   if (numPoints) PetscValidIntPointer(numPoints, 4);
3680   if (points)    PetscValidPointer(points, 5);
3681   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3682   PetscFunctionReturn(0);
3683 }
3684 
3685 /*@C
3686   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3687 
3688   Not collective
3689 
3690   Input Parameters:
3691 + dm        - The DMPlex
3692 . p         - The mesh point
3693 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3694 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3695 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3696 
3697   Note:
3698   If not using internal storage (points is not NULL on input), this call is unnecessary
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        sfNatural;
3770 
3771     (*subdm)->sfMigration = dm->sfMigration;
3772     PetscCall(PetscObjectReference((PetscObject) dm->sfMigration));
3773     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3774     (*subdm)->sfNatural = sfNatural;
3775   }
3776   PetscFunctionReturn(0);
3777 }
3778 
3779 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3780 {
3781   PetscInt       i = 0;
3782 
3783   PetscFunctionBegin;
3784   PetscCall(DMClone(dms[0], superdm));
3785   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3786   (*superdm)->useNatural = PETSC_FALSE;
3787   for (i = 0; i < len; i++) {
3788     if (dms[i]->useNatural && dms[i]->sfMigration) {
3789       PetscSF        sfNatural;
3790 
3791       (*superdm)->sfMigration = dms[i]->sfMigration;
3792       PetscCall(PetscObjectReference((PetscObject) dms[i]->sfMigration));
3793       (*superdm)->useNatural = PETSC_TRUE;
3794       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3795       (*superdm)->sfNatural = sfNatural;
3796       break;
3797     }
3798   }
3799   PetscFunctionReturn(0);
3800 }
3801 
3802 /*@
3803   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3804 
3805   Not collective
3806 
3807   Input Parameter:
3808 . mesh - The DMPlex
3809 
3810   Output Parameter:
3811 
3812   Note:
3813   This should be called after all calls to DMPlexSetCone()
3814 
3815   Level: beginner
3816 
3817 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3818 @*/
3819 PetscErrorCode DMPlexSymmetrize(DM dm)
3820 {
3821   DM_Plex       *mesh = (DM_Plex*) dm->data;
3822   PetscInt      *offsets;
3823   PetscInt       supportSize;
3824   PetscInt       pStart, pEnd, p;
3825 
3826   PetscFunctionBegin;
3827   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3828   PetscCheck(!mesh->supports,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3829   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0));
3830   /* Calculate support sizes */
3831   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3832   for (p = pStart; p < pEnd; ++p) {
3833     PetscInt dof, off, c;
3834 
3835     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3836     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3837     for (c = off; c < off+dof; ++c) {
3838       PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3839     }
3840   }
3841   PetscCall(PetscSectionSetUp(mesh->supportSection));
3842   /* Calculate supports */
3843   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3844   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3845   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3846   for (p = pStart; p < pEnd; ++p) {
3847     PetscInt dof, off, c;
3848 
3849     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3850     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3851     for (c = off; c < off+dof; ++c) {
3852       const PetscInt q = mesh->cones[c];
3853       PetscInt       offS;
3854 
3855       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3856 
3857       mesh->supports[offS+offsets[q]] = p;
3858       ++offsets[q];
3859     }
3860   }
3861   PetscCall(PetscFree(offsets));
3862   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0));
3863   PetscFunctionReturn(0);
3864 }
3865 
3866 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3867 {
3868   IS             stratumIS;
3869 
3870   PetscFunctionBegin;
3871   if (pStart >= pEnd) PetscFunctionReturn(0);
3872   if (PetscDefined(USE_DEBUG)) {
3873     PetscInt  qStart, qEnd, numLevels, level;
3874     PetscBool overlap = PETSC_FALSE;
3875     PetscCall(DMLabelGetNumValues(label, &numLevels));
3876     for (level = 0; level < numLevels; level++) {
3877       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3878       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
3879     }
3880     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);
3881   }
3882   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS));
3883   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3884   PetscCall(ISDestroy(&stratumIS));
3885   PetscFunctionReturn(0);
3886 }
3887 
3888 /*@
3889   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3890   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3891   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3892   the DAG.
3893 
3894   Collective on dm
3895 
3896   Input Parameter:
3897 . mesh - The DMPlex
3898 
3899   Output Parameter:
3900 
3901   Notes:
3902   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3903   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3904   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3905   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3906   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3907 
3908   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3909   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3910   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
3911   to interpolate only that one (e0), so that
3912 $  cone(c0) = {e0, v2}
3913 $  cone(e0) = {v0, v1}
3914   If DMPlexStratify() is run on this mesh, it will give depths
3915 $  depth 0 = {v0, v1, v2}
3916 $  depth 1 = {e0, c0}
3917   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3918 
3919   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3920 
3921   Level: beginner
3922 
3923 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3924 @*/
3925 PetscErrorCode DMPlexStratify(DM dm)
3926 {
3927   DM_Plex       *mesh = (DM_Plex*) dm->data;
3928   DMLabel        label;
3929   PetscInt       pStart, pEnd, p;
3930   PetscInt       numRoots = 0, numLeaves = 0;
3931 
3932   PetscFunctionBegin;
3933   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3934   PetscCall(PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0));
3935 
3936   /* Create depth label */
3937   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3938   PetscCall(DMCreateLabel(dm, "depth"));
3939   PetscCall(DMPlexGetDepthLabel(dm, &label));
3940 
3941   {
3942     /* Initialize roots and count leaves */
3943     PetscInt sMin = PETSC_MAX_INT;
3944     PetscInt sMax = PETSC_MIN_INT;
3945     PetscInt coneSize, supportSize;
3946 
3947     for (p = pStart; p < pEnd; ++p) {
3948       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3949       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3950       if (!coneSize && supportSize) {
3951         sMin = PetscMin(p, sMin);
3952         sMax = PetscMax(p, sMax);
3953         ++numRoots;
3954       } else if (!supportSize && coneSize) {
3955         ++numLeaves;
3956       } else if (!supportSize && !coneSize) {
3957         /* Isolated points */
3958         sMin = PetscMin(p, sMin);
3959         sMax = PetscMax(p, sMax);
3960       }
3961     }
3962     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1));
3963   }
3964 
3965   if (numRoots + numLeaves == (pEnd - pStart)) {
3966     PetscInt sMin = PETSC_MAX_INT;
3967     PetscInt sMax = PETSC_MIN_INT;
3968     PetscInt coneSize, supportSize;
3969 
3970     for (p = pStart; p < pEnd; ++p) {
3971       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3972       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3973       if (!supportSize && coneSize) {
3974         sMin = PetscMin(p, sMin);
3975         sMax = PetscMax(p, sMax);
3976       }
3977     }
3978     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1));
3979   } else {
3980     PetscInt level = 0;
3981     PetscInt qStart, qEnd, q;
3982 
3983     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3984     while (qEnd > qStart) {
3985       PetscInt sMin = PETSC_MAX_INT;
3986       PetscInt sMax = PETSC_MIN_INT;
3987 
3988       for (q = qStart; q < qEnd; ++q) {
3989         const PetscInt *support;
3990         PetscInt        supportSize, s;
3991 
3992         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
3993         PetscCall(DMPlexGetSupport(dm, q, &support));
3994         for (s = 0; s < supportSize; ++s) {
3995           sMin = PetscMin(support[s], sMin);
3996           sMax = PetscMax(support[s], sMax);
3997         }
3998       }
3999       PetscCall(DMLabelGetNumValues(label, &level));
4000       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1));
4001       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4002     }
4003   }
4004   { /* just in case there is an empty process */
4005     PetscInt numValues, maxValues = 0, v;
4006 
4007     PetscCall(DMLabelGetNumValues(label, &numValues));
4008     PetscCallMPI(MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm)));
4009     for (v = numValues; v < maxValues; v++) {
4010       PetscCall(DMLabelAddStratum(label, v));
4011     }
4012   }
4013   PetscCall(PetscObjectStateGet((PetscObject) label, &mesh->depthState));
4014   PetscCall(PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0));
4015   PetscFunctionReturn(0);
4016 }
4017 
4018 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4019 {
4020   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4021   PetscInt       dim, depth, pheight, coneSize;
4022 
4023   PetscFunctionBeginHot;
4024   PetscCall(DMGetDimension(dm, &dim));
4025   PetscCall(DMPlexGetDepth(dm, &depth));
4026   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4027   pheight = depth - pdepth;
4028   if (depth <= 1) {
4029     switch (pdepth) {
4030       case 0: ct = DM_POLYTOPE_POINT;break;
4031       case 1:
4032         switch (coneSize) {
4033           case 2: ct = DM_POLYTOPE_SEGMENT;break;
4034           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4035           case 4:
4036           switch (dim) {
4037             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
4038             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
4039             default: break;
4040           }
4041           break;
4042         case 5: ct = DM_POLYTOPE_PYRAMID;break;
4043         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4044         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
4045         default: break;
4046       }
4047     }
4048   } else {
4049     if (pdepth == 0) {
4050       ct = DM_POLYTOPE_POINT;
4051     } else if (pheight == 0) {
4052       switch (dim) {
4053         case 1:
4054           switch (coneSize) {
4055             case 2: ct = DM_POLYTOPE_SEGMENT;break;
4056             default: break;
4057           }
4058           break;
4059         case 2:
4060           switch (coneSize) {
4061             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4062             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4063             default: break;
4064           }
4065           break;
4066         case 3:
4067           switch (coneSize) {
4068             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
4069             case 5:
4070             {
4071               const PetscInt *cone;
4072               PetscInt        faceConeSize;
4073 
4074               PetscCall(DMPlexGetCone(dm, p, &cone));
4075               PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4076               switch (faceConeSize) {
4077                 case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4078                 case 4: ct = DM_POLYTOPE_PYRAMID;break;
4079               }
4080             }
4081             break;
4082             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
4083             default: break;
4084           }
4085           break;
4086         default: break;
4087       }
4088     } else if (pheight > 0) {
4089       switch (coneSize) {
4090         case 2: ct = DM_POLYTOPE_SEGMENT;break;
4091         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4092         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4093         default: break;
4094       }
4095     }
4096   }
4097   *pt = ct;
4098   PetscFunctionReturn(0);
4099 }
4100 
4101 /*@
4102   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4103 
4104   Collective on dm
4105 
4106   Input Parameter:
4107 . mesh - The DMPlex
4108 
4109   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4110 
4111   Level: developer
4112 
4113   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4114   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4115   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4116 
4117 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4118 @*/
4119 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4120 {
4121   DM_Plex       *mesh;
4122   DMLabel        ctLabel;
4123   PetscInt       pStart, pEnd, p;
4124 
4125   PetscFunctionBegin;
4126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4127   mesh = (DM_Plex *) dm->data;
4128   PetscCall(DMCreateLabel(dm, "celltype"));
4129   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4130   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4131   for (p = pStart; p < pEnd; ++p) {
4132     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4133     PetscInt       pdepth;
4134 
4135     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4136     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4137     PetscCheck(ct != DM_POLYTOPE_UNKNOWN,PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4138     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4139   }
4140   PetscCall(PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState));
4141   PetscCall(PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view"));
4142   PetscFunctionReturn(0);
4143 }
4144 
4145 /*@C
4146   DMPlexGetJoin - Get an array for the join of the set of points
4147 
4148   Not Collective
4149 
4150   Input Parameters:
4151 + dm - The DMPlex object
4152 . numPoints - The number of input points for the join
4153 - points - The input points
4154 
4155   Output Parameters:
4156 + numCoveredPoints - The number of points in the join
4157 - coveredPoints - The points in the join
4158 
4159   Level: intermediate
4160 
4161   Note: Currently, this is restricted to a single level join
4162 
4163   Fortran Notes:
4164   Since it returns an array, this routine is only available in Fortran 90, and you must
4165   include petsc.h90 in your code.
4166 
4167   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4168 
4169 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4170 @*/
4171 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4172 {
4173   DM_Plex       *mesh = (DM_Plex*) dm->data;
4174   PetscInt      *join[2];
4175   PetscInt       joinSize, i = 0;
4176   PetscInt       dof, off, p, c, m;
4177   PetscInt       maxSupportSize;
4178 
4179   PetscFunctionBegin;
4180   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4181   PetscValidIntPointer(points, 3);
4182   PetscValidIntPointer(numCoveredPoints, 4);
4183   PetscValidPointer(coveredPoints, 5);
4184   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4185   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4186   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4187   /* Copy in support of first point */
4188   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4189   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4190   for (joinSize = 0; joinSize < dof; ++joinSize) {
4191     join[i][joinSize] = mesh->supports[off+joinSize];
4192   }
4193   /* Check each successive support */
4194   for (p = 1; p < numPoints; ++p) {
4195     PetscInt newJoinSize = 0;
4196 
4197     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4198     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4199     for (c = 0; c < dof; ++c) {
4200       const PetscInt point = mesh->supports[off+c];
4201 
4202       for (m = 0; m < joinSize; ++m) {
4203         if (point == join[i][m]) {
4204           join[1-i][newJoinSize++] = point;
4205           break;
4206         }
4207       }
4208     }
4209     joinSize = newJoinSize;
4210     i        = 1-i;
4211   }
4212   *numCoveredPoints = joinSize;
4213   *coveredPoints    = join[i];
4214   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1-i]));
4215   PetscFunctionReturn(0);
4216 }
4217 
4218 /*@C
4219   DMPlexRestoreJoin - Restore an array for the join of the set of points
4220 
4221   Not Collective
4222 
4223   Input Parameters:
4224 + dm - The DMPlex object
4225 . numPoints - The number of input points for the join
4226 - points - The input points
4227 
4228   Output Parameters:
4229 + numCoveredPoints - The number of points in the join
4230 - coveredPoints - The points in the join
4231 
4232   Fortran Notes:
4233   Since it returns an array, this routine is only available in Fortran 90, and you must
4234   include petsc.h90 in your code.
4235 
4236   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4237 
4238   Level: intermediate
4239 
4240 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4241 @*/
4242 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4243 {
4244   PetscFunctionBegin;
4245   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4246   if (points) PetscValidIntPointer(points,3);
4247   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4248   PetscValidPointer(coveredPoints, 5);
4249   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4250   if (numCoveredPoints) *numCoveredPoints = 0;
4251   PetscFunctionReturn(0);
4252 }
4253 
4254 /*@C
4255   DMPlexGetFullJoin - Get an array for the join of the set of points
4256 
4257   Not Collective
4258 
4259   Input Parameters:
4260 + dm - The DMPlex object
4261 . numPoints - The number of input points for the join
4262 - points - The input points
4263 
4264   Output Parameters:
4265 + numCoveredPoints - The number of points in the join
4266 - coveredPoints - The points in the join
4267 
4268   Fortran Notes:
4269   Since it returns an array, this routine is only available in Fortran 90, and you must
4270   include petsc.h90 in your code.
4271 
4272   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4273 
4274   Level: intermediate
4275 
4276 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4277 @*/
4278 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4279 {
4280   PetscInt      *offsets, **closures;
4281   PetscInt      *join[2];
4282   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
4283   PetscInt       p, d, c, m, ms;
4284 
4285   PetscFunctionBegin;
4286   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4287   PetscValidIntPointer(points, 3);
4288   PetscValidIntPointer(numCoveredPoints, 4);
4289   PetscValidPointer(coveredPoints, 5);
4290 
4291   PetscCall(DMPlexGetDepth(dm, &depth));
4292   PetscCall(PetscCalloc1(numPoints, &closures));
4293   PetscCall(DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4294   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4295   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
4296   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4297   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4298 
4299   for (p = 0; p < numPoints; ++p) {
4300     PetscInt closureSize;
4301 
4302     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4303 
4304     offsets[p*(depth+2)+0] = 0;
4305     for (d = 0; d < depth+1; ++d) {
4306       PetscInt pStart, pEnd, i;
4307 
4308       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4309       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
4310         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4311           offsets[p*(depth+2)+d+1] = i;
4312           break;
4313         }
4314       }
4315       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
4316     }
4317     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);
4318   }
4319   for (d = 0; d < depth+1; ++d) {
4320     PetscInt dof;
4321 
4322     /* Copy in support of first point */
4323     dof = offsets[d+1] - offsets[d];
4324     for (joinSize = 0; joinSize < dof; ++joinSize) {
4325       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
4326     }
4327     /* Check each successive cone */
4328     for (p = 1; p < numPoints && joinSize; ++p) {
4329       PetscInt newJoinSize = 0;
4330 
4331       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
4332       for (c = 0; c < dof; ++c) {
4333         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
4334 
4335         for (m = 0; m < joinSize; ++m) {
4336           if (point == join[i][m]) {
4337             join[1-i][newJoinSize++] = point;
4338             break;
4339           }
4340         }
4341       }
4342       joinSize = newJoinSize;
4343       i        = 1-i;
4344     }
4345     if (joinSize) break;
4346   }
4347   *numCoveredPoints = joinSize;
4348   *coveredPoints    = join[i];
4349   for (p = 0; p < numPoints; ++p) {
4350     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4351   }
4352   PetscCall(PetscFree(closures));
4353   PetscCall(DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4354   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1-i]));
4355   PetscFunctionReturn(0);
4356 }
4357 
4358 /*@C
4359   DMPlexGetMeet - Get an array for the meet of the set of points
4360 
4361   Not Collective
4362 
4363   Input Parameters:
4364 + dm - The DMPlex object
4365 . numPoints - The number of input points for the meet
4366 - points - The input points
4367 
4368   Output Parameters:
4369 + numCoveredPoints - The number of points in the meet
4370 - coveredPoints - The points in the meet
4371 
4372   Level: intermediate
4373 
4374   Note: Currently, this is restricted to a single level meet
4375 
4376   Fortran Notes:
4377   Since it returns an array, this routine is only available in Fortran 90, and you must
4378   include petsc.h90 in your code.
4379 
4380   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4381 
4382 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4383 @*/
4384 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4385 {
4386   DM_Plex       *mesh = (DM_Plex*) dm->data;
4387   PetscInt      *meet[2];
4388   PetscInt       meetSize, i = 0;
4389   PetscInt       dof, off, p, c, m;
4390   PetscInt       maxConeSize;
4391 
4392   PetscFunctionBegin;
4393   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4394   PetscValidIntPointer(points, 3);
4395   PetscValidIntPointer(numCoveringPoints, 4);
4396   PetscValidPointer(coveringPoints, 5);
4397   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4398   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4399   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4400   /* Copy in cone of first point */
4401   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4402   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4403   for (meetSize = 0; meetSize < dof; ++meetSize) {
4404     meet[i][meetSize] = mesh->cones[off+meetSize];
4405   }
4406   /* Check each successive cone */
4407   for (p = 1; p < numPoints; ++p) {
4408     PetscInt newMeetSize = 0;
4409 
4410     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4411     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4412     for (c = 0; c < dof; ++c) {
4413       const PetscInt point = mesh->cones[off+c];
4414 
4415       for (m = 0; m < meetSize; ++m) {
4416         if (point == meet[i][m]) {
4417           meet[1-i][newMeetSize++] = point;
4418           break;
4419         }
4420       }
4421     }
4422     meetSize = newMeetSize;
4423     i        = 1-i;
4424   }
4425   *numCoveringPoints = meetSize;
4426   *coveringPoints    = meet[i];
4427   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1-i]));
4428   PetscFunctionReturn(0);
4429 }
4430 
4431 /*@C
4432   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4433 
4434   Not Collective
4435 
4436   Input Parameters:
4437 + dm - The DMPlex object
4438 . numPoints - The number of input points for the meet
4439 - points - The input points
4440 
4441   Output Parameters:
4442 + numCoveredPoints - The number of points in the meet
4443 - coveredPoints - The points in the meet
4444 
4445   Level: intermediate
4446 
4447   Fortran Notes:
4448   Since it returns an array, this routine is only available in Fortran 90, and you must
4449   include petsc.h90 in your code.
4450 
4451   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4452 
4453 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4454 @*/
4455 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4456 {
4457   PetscFunctionBegin;
4458   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4459   if (points) PetscValidIntPointer(points,3);
4460   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4461   PetscValidPointer(coveredPoints,5);
4462   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4463   if (numCoveredPoints) *numCoveredPoints = 0;
4464   PetscFunctionReturn(0);
4465 }
4466 
4467 /*@C
4468   DMPlexGetFullMeet - Get an array for the meet of the set of points
4469 
4470   Not Collective
4471 
4472   Input Parameters:
4473 + dm - The DMPlex object
4474 . numPoints - The number of input points for the meet
4475 - points - The input points
4476 
4477   Output Parameters:
4478 + numCoveredPoints - The number of points in the meet
4479 - coveredPoints - The points in the meet
4480 
4481   Level: intermediate
4482 
4483   Fortran Notes:
4484   Since it returns an array, this routine is only available in Fortran 90, and you must
4485   include petsc.h90 in your code.
4486 
4487   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4488 
4489 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4490 @*/
4491 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4492 {
4493   PetscInt      *offsets, **closures;
4494   PetscInt      *meet[2];
4495   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
4496   PetscInt       p, h, c, m, mc;
4497 
4498   PetscFunctionBegin;
4499   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4500   PetscValidIntPointer(points, 3);
4501   PetscValidIntPointer(numCoveredPoints, 4);
4502   PetscValidPointer(coveredPoints, 5);
4503 
4504   PetscCall(DMPlexGetDepth(dm, &height));
4505   PetscCall(PetscMalloc1(numPoints, &closures));
4506   PetscCall(DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4507   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4508   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
4509   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4510   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4511 
4512   for (p = 0; p < numPoints; ++p) {
4513     PetscInt closureSize;
4514 
4515     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4516 
4517     offsets[p*(height+2)+0] = 0;
4518     for (h = 0; h < height+1; ++h) {
4519       PetscInt pStart, pEnd, i;
4520 
4521       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4522       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
4523         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4524           offsets[p*(height+2)+h+1] = i;
4525           break;
4526         }
4527       }
4528       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
4529     }
4530     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);
4531   }
4532   for (h = 0; h < height+1; ++h) {
4533     PetscInt dof;
4534 
4535     /* Copy in cone of first point */
4536     dof = offsets[h+1] - offsets[h];
4537     for (meetSize = 0; meetSize < dof; ++meetSize) {
4538       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
4539     }
4540     /* Check each successive cone */
4541     for (p = 1; p < numPoints && meetSize; ++p) {
4542       PetscInt newMeetSize = 0;
4543 
4544       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
4545       for (c = 0; c < dof; ++c) {
4546         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
4547 
4548         for (m = 0; m < meetSize; ++m) {
4549           if (point == meet[i][m]) {
4550             meet[1-i][newMeetSize++] = point;
4551             break;
4552           }
4553         }
4554       }
4555       meetSize = newMeetSize;
4556       i        = 1-i;
4557     }
4558     if (meetSize) break;
4559   }
4560   *numCoveredPoints = meetSize;
4561   *coveredPoints    = meet[i];
4562   for (p = 0; p < numPoints; ++p) {
4563     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4564   }
4565   PetscCall(PetscFree(closures));
4566   PetscCall(DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4567   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1-i]));
4568   PetscFunctionReturn(0);
4569 }
4570 
4571 /*@C
4572   DMPlexEqual - Determine if two DMs have the same topology
4573 
4574   Not Collective
4575 
4576   Input Parameters:
4577 + dmA - A DMPlex object
4578 - dmB - A DMPlex object
4579 
4580   Output Parameters:
4581 . equal - PETSC_TRUE if the topologies are identical
4582 
4583   Level: intermediate
4584 
4585   Notes:
4586   We are not solving graph isomorphism, so we do not permutation.
4587 
4588 .seealso: `DMPlexGetCone()`
4589 @*/
4590 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4591 {
4592   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4593 
4594   PetscFunctionBegin;
4595   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4596   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4597   PetscValidBoolPointer(equal, 3);
4598 
4599   *equal = PETSC_FALSE;
4600   PetscCall(DMPlexGetDepth(dmA, &depth));
4601   PetscCall(DMPlexGetDepth(dmB, &depthB));
4602   if (depth != depthB) PetscFunctionReturn(0);
4603   PetscCall(DMPlexGetChart(dmA, &pStart,  &pEnd));
4604   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4605   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4606   for (p = pStart; p < pEnd; ++p) {
4607     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4608     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4609 
4610     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4611     PetscCall(DMPlexGetCone(dmA, p, &cone));
4612     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4613     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4614     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4615     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4616     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4617     for (c = 0; c < coneSize; ++c) {
4618       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4619       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4620     }
4621     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4622     PetscCall(DMPlexGetSupport(dmA, p, &support));
4623     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4624     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4625     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4626     for (s = 0; s < supportSize; ++s) {
4627       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4628     }
4629   }
4630   *equal = PETSC_TRUE;
4631   PetscFunctionReturn(0);
4632 }
4633 
4634 /*@C
4635   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4636 
4637   Not Collective
4638 
4639   Input Parameters:
4640 + dm         - The DMPlex
4641 . cellDim    - The cell dimension
4642 - numCorners - The number of vertices on a cell
4643 
4644   Output Parameters:
4645 . numFaceVertices - The number of vertices on a face
4646 
4647   Level: developer
4648 
4649   Notes:
4650   Of course this can only work for a restricted set of symmetric shapes
4651 
4652 .seealso: `DMPlexGetCone()`
4653 @*/
4654 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4655 {
4656   MPI_Comm       comm;
4657 
4658   PetscFunctionBegin;
4659   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
4660   PetscValidIntPointer(numFaceVertices,4);
4661   switch (cellDim) {
4662   case 0:
4663     *numFaceVertices = 0;
4664     break;
4665   case 1:
4666     *numFaceVertices = 1;
4667     break;
4668   case 2:
4669     switch (numCorners) {
4670     case 3: /* triangle */
4671       *numFaceVertices = 2; /* Edge has 2 vertices */
4672       break;
4673     case 4: /* quadrilateral */
4674       *numFaceVertices = 2; /* Edge has 2 vertices */
4675       break;
4676     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
4677       *numFaceVertices = 3; /* Edge has 3 vertices */
4678       break;
4679     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4680       *numFaceVertices = 3; /* Edge has 3 vertices */
4681       break;
4682     default:
4683       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4684     }
4685     break;
4686   case 3:
4687     switch (numCorners) {
4688     case 4: /* tetradehdron */
4689       *numFaceVertices = 3; /* Face has 3 vertices */
4690       break;
4691     case 6: /* tet cohesive cells */
4692       *numFaceVertices = 4; /* Face has 4 vertices */
4693       break;
4694     case 8: /* hexahedron */
4695       *numFaceVertices = 4; /* Face has 4 vertices */
4696       break;
4697     case 9: /* tet cohesive Lagrange cells */
4698       *numFaceVertices = 6; /* Face has 6 vertices */
4699       break;
4700     case 10: /* quadratic tetrahedron */
4701       *numFaceVertices = 6; /* Face has 6 vertices */
4702       break;
4703     case 12: /* hex cohesive Lagrange cells */
4704       *numFaceVertices = 6; /* Face has 6 vertices */
4705       break;
4706     case 18: /* quadratic tet cohesive Lagrange cells */
4707       *numFaceVertices = 6; /* Face has 6 vertices */
4708       break;
4709     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4710       *numFaceVertices = 9; /* Face has 9 vertices */
4711       break;
4712     default:
4713       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4714     }
4715     break;
4716   default:
4717     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4718   }
4719   PetscFunctionReturn(0);
4720 }
4721 
4722 /*@
4723   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4724 
4725   Not Collective
4726 
4727   Input Parameter:
4728 . dm    - The DMPlex object
4729 
4730   Output Parameter:
4731 . depthLabel - The DMLabel recording point depth
4732 
4733   Level: developer
4734 
4735 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4736 @*/
4737 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4738 {
4739   PetscFunctionBegin;
4740   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4741   PetscValidPointer(depthLabel, 2);
4742   *depthLabel = dm->depthLabel;
4743   PetscFunctionReturn(0);
4744 }
4745 
4746 /*@
4747   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4748 
4749   Not Collective
4750 
4751   Input Parameter:
4752 . dm    - The DMPlex object
4753 
4754   Output Parameter:
4755 . depth - The number of strata (breadth first levels) in the DAG
4756 
4757   Level: developer
4758 
4759   Notes:
4760   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4761   The point depth is described more in detail in DMPlexGetDepthStratum().
4762   An empty mesh gives -1.
4763 
4764 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4765 @*/
4766 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4767 {
4768   DMLabel        label;
4769   PetscInt       d = 0;
4770 
4771   PetscFunctionBegin;
4772   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4773   PetscValidIntPointer(depth, 2);
4774   PetscCall(DMPlexGetDepthLabel(dm, &label));
4775   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4776   *depth = d-1;
4777   PetscFunctionReturn(0);
4778 }
4779 
4780 /*@
4781   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4782 
4783   Not Collective
4784 
4785   Input Parameters:
4786 + dm    - The DMPlex object
4787 - depth - The requested depth
4788 
4789   Output Parameters:
4790 + start - The first point at this depth
4791 - end   - One beyond the last point at this depth
4792 
4793   Notes:
4794   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4795   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4796   higher dimension, e.g., "edges".
4797 
4798   Level: developer
4799 
4800 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4801 @*/
4802 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4803 {
4804   DMLabel        label;
4805   PetscInt       pStart, pEnd;
4806 
4807   PetscFunctionBegin;
4808   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4809   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4810   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4811   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4812   if (pStart == pEnd) PetscFunctionReturn(0);
4813   if (depth < 0) {
4814     if (start) *start = pStart;
4815     if (end)   *end   = pEnd;
4816     PetscFunctionReturn(0);
4817   }
4818   PetscCall(DMPlexGetDepthLabel(dm, &label));
4819   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4820   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4821   PetscFunctionReturn(0);
4822 }
4823 
4824 /*@
4825   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4826 
4827   Not Collective
4828 
4829   Input Parameters:
4830 + dm     - The DMPlex object
4831 - height - The requested height
4832 
4833   Output Parameters:
4834 + start - The first point at this height
4835 - end   - One beyond the last point at this height
4836 
4837   Notes:
4838   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4839   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4840   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4841 
4842   Level: developer
4843 
4844 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4845 @*/
4846 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4847 {
4848   DMLabel        label;
4849   PetscInt       depth, pStart, pEnd;
4850 
4851   PetscFunctionBegin;
4852   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4853   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4854   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4855   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4856   if (pStart == pEnd) PetscFunctionReturn(0);
4857   if (height < 0) {
4858     if (start) *start = pStart;
4859     if (end)   *end   = pEnd;
4860     PetscFunctionReturn(0);
4861   }
4862   PetscCall(DMPlexGetDepthLabel(dm, &label));
4863   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4864   PetscCall(DMLabelGetNumValues(label, &depth));
4865   PetscCall(DMLabelGetStratumBounds(label, depth-1-height, start, end));
4866   PetscFunctionReturn(0);
4867 }
4868 
4869 /*@
4870   DMPlexGetPointDepth - Get the depth of a given point
4871 
4872   Not Collective
4873 
4874   Input Parameters:
4875 + dm    - The DMPlex object
4876 - point - The point
4877 
4878   Output Parameter:
4879 . depth - The depth of the point
4880 
4881   Level: intermediate
4882 
4883 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4884 @*/
4885 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4886 {
4887   PetscFunctionBegin;
4888   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4889   PetscValidIntPointer(depth, 3);
4890   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4891   PetscFunctionReturn(0);
4892 }
4893 
4894 /*@
4895   DMPlexGetPointHeight - Get the height of a given point
4896 
4897   Not Collective
4898 
4899   Input Parameters:
4900 + dm    - The DMPlex object
4901 - point - The point
4902 
4903   Output Parameter:
4904 . height - The height of the point
4905 
4906   Level: intermediate
4907 
4908 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4909 @*/
4910 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4911 {
4912   PetscInt       n, pDepth;
4913 
4914   PetscFunctionBegin;
4915   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4916   PetscValidIntPointer(height, 3);
4917   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4918   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4919   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
4920   PetscFunctionReturn(0);
4921 }
4922 
4923 /*@
4924   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4925 
4926   Not Collective
4927 
4928   Input Parameter:
4929 . dm - The DMPlex object
4930 
4931   Output Parameter:
4932 . celltypeLabel - The DMLabel recording cell polytope type
4933 
4934   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4935   DMCreateLabel(dm, "celltype") beforehand.
4936 
4937   Level: developer
4938 
4939 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4940 @*/
4941 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
4942 {
4943   PetscFunctionBegin;
4944   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4945   PetscValidPointer(celltypeLabel, 2);
4946   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4947   *celltypeLabel = dm->celltypeLabel;
4948   PetscFunctionReturn(0);
4949 }
4950 
4951 /*@
4952   DMPlexGetCellType - Get the polytope type of a given cell
4953 
4954   Not Collective
4955 
4956   Input Parameters:
4957 + dm   - The DMPlex object
4958 - cell - The cell
4959 
4960   Output Parameter:
4961 . celltype - The polytope type of the cell
4962 
4963   Level: intermediate
4964 
4965 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4966 @*/
4967 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
4968 {
4969   DMLabel        label;
4970   PetscInt       ct;
4971 
4972   PetscFunctionBegin;
4973   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4974   PetscValidPointer(celltype, 3);
4975   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4976   PetscCall(DMLabelGetValue(label, cell, &ct));
4977   PetscCheck(ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4978   *celltype = (DMPolytopeType) ct;
4979   PetscFunctionReturn(0);
4980 }
4981 
4982 /*@
4983   DMPlexSetCellType - Set the polytope type of a given cell
4984 
4985   Not Collective
4986 
4987   Input Parameters:
4988 + dm   - The DMPlex object
4989 . cell - The cell
4990 - celltype - The polytope type of the cell
4991 
4992   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
4993   is executed. This function will override the computed type. However, if automatic classification will not succeed
4994   and a user wants to manually specify all types, the classification must be disabled by calling
4995   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
4996 
4997   Level: advanced
4998 
4999 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5000 @*/
5001 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5002 {
5003   DMLabel        label;
5004 
5005   PetscFunctionBegin;
5006   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5007   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5008   PetscCall(DMLabelSetValue(label, cell, celltype));
5009   PetscFunctionReturn(0);
5010 }
5011 
5012 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5013 {
5014   PetscSection   section, s;
5015   Mat            m;
5016   PetscInt       maxHeight;
5017 
5018   PetscFunctionBegin;
5019   PetscCall(DMClone(dm, cdm));
5020   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5021   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5022   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5023   PetscCall(DMSetLocalSection(*cdm, section));
5024   PetscCall(PetscSectionDestroy(&section));
5025   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5026   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5027   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5028   PetscCall(PetscSectionDestroy(&s));
5029   PetscCall(MatDestroy(&m));
5030 
5031   PetscCall(DMSetNumFields(*cdm, 1));
5032   PetscCall(DMCreateDS(*cdm));
5033   PetscFunctionReturn(0);
5034 }
5035 
5036 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5037 {
5038   Vec coordsLocal, cellCoordsLocal;
5039   DM  coordsDM,    cellCoordsDM;
5040 
5041   PetscFunctionBegin;
5042   *field = NULL;
5043   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5044   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5045   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5046   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5047   if (coordsLocal && coordsDM) {
5048     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5049     else                                 PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5050   }
5051   PetscFunctionReturn(0);
5052 }
5053 
5054 /*@C
5055   DMPlexGetConeSection - Return a section which describes the layout of cone data
5056 
5057   Not Collective
5058 
5059   Input Parameters:
5060 . dm        - The DMPlex object
5061 
5062   Output Parameter:
5063 . section - The PetscSection object
5064 
5065   Level: developer
5066 
5067 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5068 @*/
5069 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5070 {
5071   DM_Plex *mesh = (DM_Plex*) dm->data;
5072 
5073   PetscFunctionBegin;
5074   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5075   if (section) *section = mesh->coneSection;
5076   PetscFunctionReturn(0);
5077 }
5078 
5079 /*@C
5080   DMPlexGetSupportSection - Return a section which describes the layout of support data
5081 
5082   Not Collective
5083 
5084   Input Parameters:
5085 . dm        - The DMPlex object
5086 
5087   Output Parameter:
5088 . section - The PetscSection object
5089 
5090   Level: developer
5091 
5092 .seealso: `DMPlexGetConeSection()`
5093 @*/
5094 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5095 {
5096   DM_Plex *mesh = (DM_Plex*) dm->data;
5097 
5098   PetscFunctionBegin;
5099   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5100   if (section) *section = mesh->supportSection;
5101   PetscFunctionReturn(0);
5102 }
5103 
5104 /*@C
5105   DMPlexGetCones - Return cone data
5106 
5107   Not Collective
5108 
5109   Input Parameters:
5110 . dm        - The DMPlex object
5111 
5112   Output Parameter:
5113 . cones - The cone for each point
5114 
5115   Level: developer
5116 
5117 .seealso: `DMPlexGetConeSection()`
5118 @*/
5119 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5120 {
5121   DM_Plex *mesh = (DM_Plex*) dm->data;
5122 
5123   PetscFunctionBegin;
5124   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5125   if (cones) *cones = mesh->cones;
5126   PetscFunctionReturn(0);
5127 }
5128 
5129 /*@C
5130   DMPlexGetConeOrientations - Return cone orientation data
5131 
5132   Not Collective
5133 
5134   Input Parameters:
5135 . dm        - The DMPlex object
5136 
5137   Output Parameter:
5138 . coneOrientations - The array of cone orientations for all points
5139 
5140   Level: developer
5141 
5142   Notes:
5143   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5144 
5145   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5146 
5147 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5148 @*/
5149 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5150 {
5151   DM_Plex *mesh = (DM_Plex*) dm->data;
5152 
5153   PetscFunctionBegin;
5154   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5155   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5156   PetscFunctionReturn(0);
5157 }
5158 
5159 /******************************** FEM Support **********************************/
5160 
5161 /*
5162  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5163  representing a line in the section.
5164 */
5165 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
5166 {
5167   PetscFunctionBeginHot;
5168   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5169   if (line < 0) {
5170     *k = 0;
5171     *Nc = 0;
5172   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
5173     *k = 1;
5174   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
5175     /* An order k SEM disc has k-1 dofs on an edge */
5176     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5177     *k = *k / *Nc + 1;
5178   }
5179   PetscFunctionReturn(0);
5180 }
5181 
5182 /*@
5183 
5184   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5185   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5186   section provided (or the section of the DM).
5187 
5188   Input Parameters:
5189 + dm      - The DM
5190 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5191 - section - The PetscSection to reorder, or NULL for the default section
5192 
5193   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5194   degree of the basis.
5195 
5196   Example:
5197   A typical interpolated single-quad mesh might order points as
5198 .vb
5199   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5200 
5201   v4 -- e6 -- v3
5202   |           |
5203   e7    c0    e8
5204   |           |
5205   v1 -- e5 -- v2
5206 .ve
5207 
5208   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5209   dofs in the order of points, e.g.,
5210 .vb
5211     c0 -> [0,1,2,3]
5212     v1 -> [4]
5213     ...
5214     e5 -> [8, 9]
5215 .ve
5216 
5217   which corresponds to the dofs
5218 .vb
5219     6   10  11  7
5220     13  2   3   15
5221     12  0   1   14
5222     4   8   9   5
5223 .ve
5224 
5225   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5226 .vb
5227   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5228 .ve
5229 
5230   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5231 .vb
5232    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5233 .ve
5234 
5235   Level: developer
5236 
5237 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5238 @*/
5239 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5240 {
5241   DMLabel        label;
5242   PetscInt       dim, depth = -1, eStart = -1, Nf;
5243   PetscBool      vertexchart;
5244 
5245   PetscFunctionBegin;
5246   PetscCall(DMGetDimension(dm, &dim));
5247   if (dim < 1) PetscFunctionReturn(0);
5248   if (point < 0) {
5249     PetscInt sStart,sEnd;
5250 
5251     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5252     point = sEnd-sStart ? sStart : point;
5253   }
5254   PetscCall(DMPlexGetDepthLabel(dm, &label));
5255   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5256   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5257   if (depth == 1) {eStart = point;}
5258   else if  (depth == dim) {
5259     const PetscInt *cone;
5260 
5261     PetscCall(DMPlexGetCone(dm, point, &cone));
5262     if (dim == 2) eStart = cone[0];
5263     else if (dim == 3) {
5264       const PetscInt *cone2;
5265       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5266       eStart = cone2[0];
5267     } 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);
5268   } 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);
5269   {                             /* Determine whether the chart covers all points or just vertices. */
5270     PetscInt pStart,pEnd,cStart,cEnd;
5271     PetscCall(DMPlexGetDepthStratum(dm,0,&pStart,&pEnd));
5272     PetscCall(PetscSectionGetChart(section,&cStart,&cEnd));
5273     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5274     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5275     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5276   }
5277   PetscCall(PetscSectionGetNumFields(section, &Nf));
5278   for (PetscInt d=1; d<=dim; d++) {
5279     PetscInt k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5280     PetscInt *perm;
5281 
5282     for (f = 0; f < Nf; ++f) {
5283       PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5284       size += PetscPowInt(k+1, d)*Nc;
5285     }
5286     PetscCall(PetscMalloc1(size, &perm));
5287     for (f = 0; f < Nf; ++f) {
5288       switch (d) {
5289       case 1:
5290         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5291         /*
5292          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5293          We want              [ vtx0; edge of length k-1; vtx1 ]
5294          */
5295         for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
5296         for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
5297         for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
5298         foffset = offset;
5299         break;
5300       case 2:
5301         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5302         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5303         /* The SEM order is
5304 
5305          v_lb, {e_b}, v_rb,
5306          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5307          v_lt, reverse {e_t}, v_rt
5308          */
5309         {
5310           const PetscInt of   = 0;
5311           const PetscInt oeb  = of   + PetscSqr(k-1);
5312           const PetscInt oer  = oeb  + (k-1);
5313           const PetscInt oet  = oer  + (k-1);
5314           const PetscInt oel  = oet  + (k-1);
5315           const PetscInt ovlb = oel  + (k-1);
5316           const PetscInt ovrb = ovlb + 1;
5317           const PetscInt ovrt = ovrb + 1;
5318           const PetscInt ovlt = ovrt + 1;
5319           PetscInt       o;
5320 
5321           /* bottom */
5322           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
5323           for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5324           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
5325           /* middle */
5326           for (i = 0; i < k-1; ++i) {
5327             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
5328             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;
5329             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
5330           }
5331           /* top */
5332           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
5333           for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5334           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
5335           foffset = offset;
5336         }
5337         break;
5338       case 3:
5339         /* The original hex closure is
5340 
5341          {c,
5342          f_b, f_t, f_f, f_b, f_r, f_l,
5343          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5344          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5345          */
5346         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5347         /* The SEM order is
5348          Bottom Slice
5349          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5350          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5351          v_blb, {e_bb}, v_brb,
5352 
5353          Middle Slice (j)
5354          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5355          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5356          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5357 
5358          Top Slice
5359          v_tlf, {e_tf}, v_trf,
5360          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5361          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5362          */
5363         {
5364           const PetscInt oc    = 0;
5365           const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
5366           const PetscInt oft   = ofb   + PetscSqr(k-1);
5367           const PetscInt off   = oft   + PetscSqr(k-1);
5368           const PetscInt ofk   = off   + PetscSqr(k-1);
5369           const PetscInt ofr   = ofk   + PetscSqr(k-1);
5370           const PetscInt ofl   = ofr   + PetscSqr(k-1);
5371           const PetscInt oebl  = ofl   + PetscSqr(k-1);
5372           const PetscInt oebb  = oebl  + (k-1);
5373           const PetscInt oebr  = oebb  + (k-1);
5374           const PetscInt oebf  = oebr  + (k-1);
5375           const PetscInt oetf  = oebf  + (k-1);
5376           const PetscInt oetr  = oetf  + (k-1);
5377           const PetscInt oetb  = oetr  + (k-1);
5378           const PetscInt oetl  = oetb  + (k-1);
5379           const PetscInt oerf  = oetl  + (k-1);
5380           const PetscInt oelf  = oerf  + (k-1);
5381           const PetscInt oelb  = oelf  + (k-1);
5382           const PetscInt oerb  = oelb  + (k-1);
5383           const PetscInt ovblf = oerb  + (k-1);
5384           const PetscInt ovblb = ovblf + 1;
5385           const PetscInt ovbrb = ovblb + 1;
5386           const PetscInt ovbrf = ovbrb + 1;
5387           const PetscInt ovtlf = ovbrf + 1;
5388           const PetscInt ovtrf = ovtlf + 1;
5389           const PetscInt ovtrb = ovtrf + 1;
5390           const PetscInt ovtlb = ovtrb + 1;
5391           PetscInt       o, n;
5392 
5393           /* Bottom Slice */
5394           /*   bottom */
5395           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
5396           for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5397           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
5398           /*   middle */
5399           for (i = 0; i < k-1; ++i) {
5400             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
5401             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;}
5402             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
5403           }
5404           /*   top */
5405           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
5406           for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5407           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
5408 
5409           /* Middle Slice */
5410           for (j = 0; j < k-1; ++j) {
5411             /*   bottom */
5412             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
5413             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;
5414             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
5415             /*   middle */
5416             for (i = 0; i < k-1; ++i) {
5417               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
5418               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;
5419               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
5420             }
5421             /*   top */
5422             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
5423             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;
5424             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
5425           }
5426 
5427           /* Top Slice */
5428           /*   bottom */
5429           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
5430           for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5431           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
5432           /*   middle */
5433           for (i = 0; i < k-1; ++i) {
5434             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
5435             for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
5436             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
5437           }
5438           /*   top */
5439           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
5440           for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5441           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
5442 
5443           foffset = offset;
5444         }
5445         break;
5446       default: SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5447       }
5448     }
5449     PetscCheck(offset == size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5450     /* Check permutation */
5451     {
5452       PetscInt *check;
5453 
5454       PetscCall(PetscMalloc1(size, &check));
5455       for (i = 0; i < size; ++i) {
5456         check[i] = -1;
5457         PetscCheck(perm[i] >= 0 && perm[i] < size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5458       }
5459       for (i = 0; i < size; ++i) check[perm[i]] = i;
5460       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5461       PetscCall(PetscFree(check));
5462     }
5463     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size, PETSC_OWN_POINTER, perm));
5464     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5465       PetscInt *loc_perm;
5466       PetscCall(PetscMalloc1(size*2, &loc_perm));
5467       for (PetscInt i=0; i<size; i++) {
5468         loc_perm[i] = perm[i];
5469         loc_perm[size+i] = size + perm[i];
5470       }
5471       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size*2, PETSC_OWN_POINTER, loc_perm));
5472     }
5473   }
5474   PetscFunctionReturn(0);
5475 }
5476 
5477 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5478 {
5479   PetscDS        prob;
5480   PetscInt       depth, Nf, h;
5481   DMLabel        label;
5482 
5483   PetscFunctionBeginHot;
5484   PetscCall(DMGetDS(dm, &prob));
5485   Nf      = prob->Nf;
5486   label   = dm->depthLabel;
5487   *dspace = NULL;
5488   if (field < Nf) {
5489     PetscObject disc = prob->disc[field];
5490 
5491     if (disc->classid == PETSCFE_CLASSID) {
5492       PetscDualSpace dsp;
5493 
5494       PetscCall(PetscFEGetDualSpace((PetscFE)disc,&dsp));
5495       PetscCall(DMLabelGetNumValues(label,&depth));
5496       PetscCall(DMLabelGetValue(label,point,&h));
5497       h    = depth - 1 - h;
5498       if (h) {
5499         PetscCall(PetscDualSpaceGetHeightSubspace(dsp,h,dspace));
5500       } else {
5501         *dspace = dsp;
5502       }
5503     }
5504   }
5505   PetscFunctionReturn(0);
5506 }
5507 
5508 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5509 {
5510   PetscScalar    *array;
5511   const PetscScalar *vArray;
5512   const PetscInt *cone, *coneO;
5513   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
5514 
5515   PetscFunctionBeginHot;
5516   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5517   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5518   PetscCall(DMPlexGetCone(dm, point, &cone));
5519   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5520   if (!values || !*values) {
5521     if ((point >= pStart) && (point < pEnd)) {
5522       PetscInt dof;
5523 
5524       PetscCall(PetscSectionGetDof(section, point, &dof));
5525       size += dof;
5526     }
5527     for (p = 0; p < numPoints; ++p) {
5528       const PetscInt cp = cone[p];
5529       PetscInt       dof;
5530 
5531       if ((cp < pStart) || (cp >= pEnd)) continue;
5532       PetscCall(PetscSectionGetDof(section, cp, &dof));
5533       size += dof;
5534     }
5535     if (!values) {
5536       if (csize) *csize = size;
5537       PetscFunctionReturn(0);
5538     }
5539     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5540   } else {
5541     array = *values;
5542   }
5543   size = 0;
5544   PetscCall(VecGetArrayRead(v, &vArray));
5545   if ((point >= pStart) && (point < pEnd)) {
5546     PetscInt     dof, off, d;
5547     const PetscScalar *varr;
5548 
5549     PetscCall(PetscSectionGetDof(section, point, &dof));
5550     PetscCall(PetscSectionGetOffset(section, point, &off));
5551     varr = &vArray[off];
5552     for (d = 0; d < dof; ++d, ++offset) {
5553       array[offset] = varr[d];
5554     }
5555     size += dof;
5556   }
5557   for (p = 0; p < numPoints; ++p) {
5558     const PetscInt cp = cone[p];
5559     PetscInt       o  = coneO[p];
5560     PetscInt       dof, off, d;
5561     const PetscScalar *varr;
5562 
5563     if ((cp < pStart) || (cp >= pEnd)) continue;
5564     PetscCall(PetscSectionGetDof(section, cp, &dof));
5565     PetscCall(PetscSectionGetOffset(section, cp, &off));
5566     varr = &vArray[off];
5567     if (o >= 0) {
5568       for (d = 0; d < dof; ++d, ++offset) {
5569         array[offset] = varr[d];
5570       }
5571     } else {
5572       for (d = dof-1; d >= 0; --d, ++offset) {
5573         array[offset] = varr[d];
5574       }
5575     }
5576     size += dof;
5577   }
5578   PetscCall(VecRestoreArrayRead(v, &vArray));
5579   if (!*values) {
5580     if (csize) *csize = size;
5581     *values = array;
5582   } else {
5583     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5584     *csize = size;
5585   }
5586   PetscFunctionReturn(0);
5587 }
5588 
5589 /* Compress out points not in the section */
5590 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5591 {
5592   const PetscInt np = *numPoints;
5593   PetscInt       pStart, pEnd, p, q;
5594 
5595   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5596   for (p = 0, q = 0; p < np; ++p) {
5597     const PetscInt r = points[p*2];
5598     if ((r >= pStart) && (r < pEnd)) {
5599       points[q*2]   = r;
5600       points[q*2+1] = points[p*2+1];
5601       ++q;
5602     }
5603   }
5604   *numPoints = q;
5605   return 0;
5606 }
5607 
5608 /* Compressed closure does not apply closure permutation */
5609 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5610 {
5611   const PetscInt *cla = NULL;
5612   PetscInt       np, *pts = NULL;
5613 
5614   PetscFunctionBeginHot;
5615   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints));
5616   if (*clPoints) {
5617     PetscInt dof, off;
5618 
5619     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5620     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5621     PetscCall(ISGetIndices(*clPoints, &cla));
5622     np   = dof/2;
5623     pts  = (PetscInt *) &cla[off];
5624   } else {
5625     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5626     PetscCall(CompressPoints_Private(section, &np, pts));
5627   }
5628   *numPoints = np;
5629   *points    = pts;
5630   *clp       = cla;
5631   PetscFunctionReturn(0);
5632 }
5633 
5634 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5635 {
5636   PetscFunctionBeginHot;
5637   if (!*clPoints) {
5638     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5639   } else {
5640     PetscCall(ISRestoreIndices(*clPoints, clp));
5641   }
5642   *numPoints = 0;
5643   *points    = NULL;
5644   *clSec     = NULL;
5645   *clPoints  = NULL;
5646   *clp       = NULL;
5647   PetscFunctionReturn(0);
5648 }
5649 
5650 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5651 {
5652   PetscInt          offset = 0, p;
5653   const PetscInt    **perms = NULL;
5654   const PetscScalar **flips = NULL;
5655 
5656   PetscFunctionBeginHot;
5657   *size = 0;
5658   PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
5659   for (p = 0; p < numPoints; p++) {
5660     const PetscInt    point = points[2*p];
5661     const PetscInt    *perm = perms ? perms[p] : NULL;
5662     const PetscScalar *flip = flips ? flips[p] : NULL;
5663     PetscInt          dof, off, d;
5664     const PetscScalar *varr;
5665 
5666     PetscCall(PetscSectionGetDof(section, point, &dof));
5667     PetscCall(PetscSectionGetOffset(section, point, &off));
5668     varr = &vArray[off];
5669     if (clperm) {
5670       if (perm) {
5671         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5672       } else {
5673         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5674       }
5675       if (flip) {
5676         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5677       }
5678     } else {
5679       if (perm) {
5680         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5681       } else {
5682         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5683       }
5684       if (flip) {
5685         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5686       }
5687     }
5688     offset += dof;
5689   }
5690   PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
5691   *size = offset;
5692   PetscFunctionReturn(0);
5693 }
5694 
5695 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[])
5696 {
5697   PetscInt          offset = 0, f;
5698 
5699   PetscFunctionBeginHot;
5700   *size = 0;
5701   for (f = 0; f < numFields; ++f) {
5702     PetscInt          p;
5703     const PetscInt    **perms = NULL;
5704     const PetscScalar **flips = NULL;
5705 
5706     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5707     for (p = 0; p < numPoints; p++) {
5708       const PetscInt    point = points[2*p];
5709       PetscInt          fdof, foff, b;
5710       const PetscScalar *varr;
5711       const PetscInt    *perm = perms ? perms[p] : NULL;
5712       const PetscScalar *flip = flips ? flips[p] : NULL;
5713 
5714       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5715       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5716       varr = &vArray[foff];
5717       if (clperm) {
5718         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5719         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5720         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5721       } else {
5722         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5723         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5724         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5725       }
5726       offset += fdof;
5727     }
5728     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5729   }
5730   *size = offset;
5731   PetscFunctionReturn(0);
5732 }
5733 
5734 /*@C
5735   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5736 
5737   Not collective
5738 
5739   Input Parameters:
5740 + dm - The DM
5741 . section - The section describing the layout in v, or NULL to use the default section
5742 . v - The local vector
5743 - point - The point in the DM
5744 
5745   Input/Output Parameters:
5746 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5747 - values - An array to use for the values, or NULL to have it allocated automatically;
5748            if the user provided NULL, it is a borrowed array and should not be freed
5749 
5750 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5751 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5752 $ assembly function, and a user may already have allocated storage for this operation.
5753 $
5754 $ A typical use could be
5755 $
5756 $  values = NULL;
5757 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5758 $  for (cl = 0; cl < clSize; ++cl) {
5759 $    <Compute on closure>
5760 $  }
5761 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5762 $
5763 $ or
5764 $
5765 $  PetscMalloc1(clMaxSize, &values);
5766 $  for (p = pStart; p < pEnd; ++p) {
5767 $    clSize = clMaxSize;
5768 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5769 $    for (cl = 0; cl < clSize; ++cl) {
5770 $      <Compute on closure>
5771 $    }
5772 $  }
5773 $  PetscFree(values);
5774 
5775   Fortran Notes:
5776   Since it returns an array, this routine is only available in Fortran 90, and you must
5777   include petsc.h90 in your code.
5778 
5779   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5780 
5781   Level: intermediate
5782 
5783 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5784 @*/
5785 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5786 {
5787   PetscSection       clSection;
5788   IS                 clPoints;
5789   PetscInt          *points = NULL;
5790   const PetscInt    *clp, *perm;
5791   PetscInt           depth, numFields, numPoints, asize;
5792 
5793   PetscFunctionBeginHot;
5794   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5795   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5796   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5797   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5798   PetscCall(DMPlexGetDepth(dm, &depth));
5799   PetscCall(PetscSectionGetNumFields(section, &numFields));
5800   if (depth == 1 && numFields < 2) {
5801     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5802     PetscFunctionReturn(0);
5803   }
5804   /* Get points */
5805   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5806   /* Get sizes */
5807   asize = 0;
5808   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5809     PetscInt dof;
5810     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5811     asize += dof;
5812   }
5813   if (values) {
5814     const PetscScalar *vArray;
5815     PetscInt          size;
5816 
5817     if (*values) {
5818       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);
5819     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5820     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm));
5821     PetscCall(VecGetArrayRead(v, &vArray));
5822     /* Get values */
5823     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5824     else               PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5825     PetscCheck(asize == size,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5826     /* Cleanup array */
5827     PetscCall(VecRestoreArrayRead(v, &vArray));
5828   }
5829   if (csize) *csize = asize;
5830   /* Cleanup points */
5831   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5832   PetscFunctionReturn(0);
5833 }
5834 
5835 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5836 {
5837   DMLabel            depthLabel;
5838   PetscSection       clSection;
5839   IS                 clPoints;
5840   PetscScalar       *array;
5841   const PetscScalar *vArray;
5842   PetscInt          *points = NULL;
5843   const PetscInt    *clp, *perm = NULL;
5844   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5845 
5846   PetscFunctionBeginHot;
5847   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5848   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5849   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5850   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5851   PetscCall(DMPlexGetDepth(dm, &mdepth));
5852   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5853   PetscCall(PetscSectionGetNumFields(section, &numFields));
5854   if (mdepth == 1 && numFields < 2) {
5855     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5856     PetscFunctionReturn(0);
5857   }
5858   /* Get points */
5859   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5860   for (clsize=0,p=0; p<Np; p++) {
5861     PetscInt dof;
5862     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
5863     clsize += dof;
5864   }
5865   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm));
5866   /* Filter points */
5867   for (p = 0; p < numPoints*2; p += 2) {
5868     PetscInt dep;
5869 
5870     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5871     if (dep != depth) continue;
5872     points[Np*2+0] = points[p];
5873     points[Np*2+1] = points[p+1];
5874     ++Np;
5875   }
5876   /* Get array */
5877   if (!values || !*values) {
5878     PetscInt asize = 0, dof;
5879 
5880     for (p = 0; p < Np*2; p += 2) {
5881       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5882       asize += dof;
5883     }
5884     if (!values) {
5885       PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5886       if (csize) *csize = asize;
5887       PetscFunctionReturn(0);
5888     }
5889     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5890   } else {
5891     array = *values;
5892   }
5893   PetscCall(VecGetArrayRead(v, &vArray));
5894   /* Get values */
5895   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5896   else               PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5897   /* Cleanup points */
5898   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5899   /* Cleanup array */
5900   PetscCall(VecRestoreArrayRead(v, &vArray));
5901   if (!*values) {
5902     if (csize) *csize = size;
5903     *values = array;
5904   } else {
5905     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5906     *csize = size;
5907   }
5908   PetscFunctionReturn(0);
5909 }
5910 
5911 /*@C
5912   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5913 
5914   Not collective
5915 
5916   Input Parameters:
5917 + dm - The DM
5918 . section - The section describing the layout in v, or NULL to use the default section
5919 . v - The local vector
5920 . point - The point in the DM
5921 . csize - The number of values in the closure, or NULL
5922 - values - The array of values, which is a borrowed array and should not be freed
5923 
5924   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5925 
5926   Fortran Notes:
5927   Since it returns an array, this routine is only available in Fortran 90, and you must
5928   include petsc.h90 in your code.
5929 
5930   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5931 
5932   Level: intermediate
5933 
5934 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5935 @*/
5936 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5937 {
5938   PetscInt       size = 0;
5939 
5940   PetscFunctionBegin;
5941   /* Should work without recalculating size */
5942   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values));
5943   *values = NULL;
5944   PetscFunctionReturn(0);
5945 }
5946 
5947 static inline void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5948 static inline void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5949 
5950 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[])
5951 {
5952   PetscInt        cdof;   /* The number of constraints on this point */
5953   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5954   PetscScalar    *a;
5955   PetscInt        off, cind = 0, k;
5956 
5957   PetscFunctionBegin;
5958   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5959   PetscCall(PetscSectionGetOffset(section, point, &off));
5960   a    = &array[off];
5961   if (!cdof || setBC) {
5962     if (clperm) {
5963       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5964       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5965     } else {
5966       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5967       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5968     }
5969   } else {
5970     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5971     if (clperm) {
5972       if (perm) {for (k = 0; k < dof; ++k) {
5973           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5974           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5975         }
5976       } else {
5977         for (k = 0; k < dof; ++k) {
5978           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5979           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5980         }
5981       }
5982     } else {
5983       if (perm) {
5984         for (k = 0; k < dof; ++k) {
5985           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5986           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
5987         }
5988       } else {
5989         for (k = 0; k < dof; ++k) {
5990           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5991           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
5992         }
5993       }
5994     }
5995   }
5996   PetscFunctionReturn(0);
5997 }
5998 
5999 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[])
6000 {
6001   PetscInt        cdof;   /* The number of constraints on this point */
6002   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6003   PetscScalar    *a;
6004   PetscInt        off, cind = 0, k;
6005 
6006   PetscFunctionBegin;
6007   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6008   PetscCall(PetscSectionGetOffset(section, point, &off));
6009   a    = &array[off];
6010   if (cdof) {
6011     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6012     if (clperm) {
6013       if (perm) {
6014         for (k = 0; k < dof; ++k) {
6015           if ((cind < cdof) && (k == cdofs[cind])) {
6016             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
6017             cind++;
6018           }
6019         }
6020       } else {
6021         for (k = 0; k < dof; ++k) {
6022           if ((cind < cdof) && (k == cdofs[cind])) {
6023             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
6024             cind++;
6025           }
6026         }
6027       }
6028     } else {
6029       if (perm) {
6030         for (k = 0; k < dof; ++k) {
6031           if ((cind < cdof) && (k == cdofs[cind])) {
6032             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6033             cind++;
6034           }
6035         }
6036       } else {
6037         for (k = 0; k < dof; ++k) {
6038           if ((cind < cdof) && (k == cdofs[cind])) {
6039             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6040             cind++;
6041           }
6042         }
6043       }
6044     }
6045   }
6046   PetscFunctionReturn(0);
6047 }
6048 
6049 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[])
6050 {
6051   PetscScalar    *a;
6052   PetscInt        fdof, foff, fcdof, foffset = *offset;
6053   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6054   PetscInt        cind = 0, b;
6055 
6056   PetscFunctionBegin;
6057   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6058   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6059   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6060   a    = &array[foff];
6061   if (!fcdof || setBC) {
6062     if (clperm) {
6063       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
6064       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
6065     } else {
6066       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
6067       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
6068     }
6069   } else {
6070     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6071     if (clperm) {
6072       if (perm) {
6073         for (b = 0; b < fdof; b++) {
6074           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6075           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6076         }
6077       } else {
6078         for (b = 0; b < fdof; b++) {
6079           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6080           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6081         }
6082       }
6083     } else {
6084       if (perm) {
6085         for (b = 0; b < fdof; b++) {
6086           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6087           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6088         }
6089       } else {
6090         for (b = 0; b < fdof; b++) {
6091           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6092           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6093         }
6094       }
6095     }
6096   }
6097   *offset += fdof;
6098   PetscFunctionReturn(0);
6099 }
6100 
6101 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[])
6102 {
6103   PetscScalar    *a;
6104   PetscInt        fdof, foff, fcdof, foffset = *offset;
6105   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6106   PetscInt        Nc, cind = 0, ncind = 0, b;
6107   PetscBool       ncSet, fcSet;
6108 
6109   PetscFunctionBegin;
6110   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6111   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6112   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6113   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6114   a    = &array[foff];
6115   if (fcdof) {
6116     /* We just override fcdof and fcdofs with Ncc and comps */
6117     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6118     if (clperm) {
6119       if (perm) {
6120         if (comps) {
6121           for (b = 0; b < fdof; b++) {
6122             ncSet = fcSet = PETSC_FALSE;
6123             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6124             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6125             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
6126           }
6127         } else {
6128           for (b = 0; b < fdof; b++) {
6129             if ((cind < fcdof) && (b == fcdofs[cind])) {
6130               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6131               ++cind;
6132             }
6133           }
6134         }
6135       } else {
6136         if (comps) {
6137           for (b = 0; b < fdof; b++) {
6138             ncSet = fcSet = PETSC_FALSE;
6139             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6140             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6141             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
6142           }
6143         } else {
6144           for (b = 0; b < fdof; b++) {
6145             if ((cind < fcdof) && (b == fcdofs[cind])) {
6146               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6147               ++cind;
6148             }
6149           }
6150         }
6151       }
6152     } else {
6153       if (perm) {
6154         if (comps) {
6155           for (b = 0; b < fdof; b++) {
6156             ncSet = fcSet = PETSC_FALSE;
6157             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6158             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6159             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
6160           }
6161         } else {
6162           for (b = 0; b < fdof; b++) {
6163             if ((cind < fcdof) && (b == fcdofs[cind])) {
6164               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6165               ++cind;
6166             }
6167           }
6168         }
6169       } else {
6170         if (comps) {
6171           for (b = 0; b < fdof; b++) {
6172             ncSet = fcSet = PETSC_FALSE;
6173             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6174             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6175             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
6176           }
6177         } else {
6178           for (b = 0; b < fdof; b++) {
6179             if ((cind < fcdof) && (b == fcdofs[cind])) {
6180               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6181               ++cind;
6182             }
6183           }
6184         }
6185       }
6186     }
6187   }
6188   *offset += fdof;
6189   PetscFunctionReturn(0);
6190 }
6191 
6192 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6193 {
6194   PetscScalar    *array;
6195   const PetscInt *cone, *coneO;
6196   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6197 
6198   PetscFunctionBeginHot;
6199   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6200   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6201   PetscCall(DMPlexGetCone(dm, point, &cone));
6202   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6203   PetscCall(VecGetArray(v, &array));
6204   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6205     const PetscInt cp = !p ? point : cone[p-1];
6206     const PetscInt o  = !p ? 0     : coneO[p-1];
6207 
6208     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
6209     PetscCall(PetscSectionGetDof(section, cp, &dof));
6210     /* ADD_VALUES */
6211     {
6212       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6213       PetscScalar    *a;
6214       PetscInt        cdof, coff, cind = 0, k;
6215 
6216       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6217       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6218       a    = &array[coff];
6219       if (!cdof) {
6220         if (o >= 0) {
6221           for (k = 0; k < dof; ++k) {
6222             a[k] += values[off+k];
6223           }
6224         } else {
6225           for (k = 0; k < dof; ++k) {
6226             a[k] += values[off+dof-k-1];
6227           }
6228         }
6229       } else {
6230         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6231         if (o >= 0) {
6232           for (k = 0; k < dof; ++k) {
6233             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6234             a[k] += values[off+k];
6235           }
6236         } else {
6237           for (k = 0; k < dof; ++k) {
6238             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6239             a[k] += values[off+dof-k-1];
6240           }
6241         }
6242       }
6243     }
6244   }
6245   PetscCall(VecRestoreArray(v, &array));
6246   PetscFunctionReturn(0);
6247 }
6248 
6249 /*@C
6250   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6251 
6252   Not collective
6253 
6254   Input Parameters:
6255 + dm - The DM
6256 . section - The section describing the layout in v, or NULL to use the default section
6257 . v - The local vector
6258 . point - The point in the DM
6259 . values - The array of values
6260 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6261          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6262 
6263   Fortran Notes:
6264   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6265 
6266   Level: intermediate
6267 
6268 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6269 @*/
6270 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6271 {
6272   PetscSection    clSection;
6273   IS              clPoints;
6274   PetscScalar    *array;
6275   PetscInt       *points = NULL;
6276   const PetscInt *clp, *clperm = NULL;
6277   PetscInt        depth, numFields, numPoints, p, clsize;
6278 
6279   PetscFunctionBeginHot;
6280   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6281   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6282   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6283   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6284   PetscCall(DMPlexGetDepth(dm, &depth));
6285   PetscCall(PetscSectionGetNumFields(section, &numFields));
6286   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6287     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6288     PetscFunctionReturn(0);
6289   }
6290   /* Get points */
6291   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6292   for (clsize=0,p=0; p<numPoints; p++) {
6293     PetscInt dof;
6294     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
6295     clsize += dof;
6296   }
6297   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
6298   /* Get array */
6299   PetscCall(VecGetArray(v, &array));
6300   /* Get values */
6301   if (numFields > 0) {
6302     PetscInt offset = 0, f;
6303     for (f = 0; f < numFields; ++f) {
6304       const PetscInt    **perms = NULL;
6305       const PetscScalar **flips = NULL;
6306 
6307       PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6308       switch (mode) {
6309       case INSERT_VALUES:
6310         for (p = 0; p < numPoints; p++) {
6311           const PetscInt    point = points[2*p];
6312           const PetscInt    *perm = perms ? perms[p] : NULL;
6313           const PetscScalar *flip = flips ? flips[p] : NULL;
6314           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6315         } break;
6316       case INSERT_ALL_VALUES:
6317         for (p = 0; p < numPoints; p++) {
6318           const PetscInt    point = points[2*p];
6319           const PetscInt    *perm = perms ? perms[p] : NULL;
6320           const PetscScalar *flip = flips ? flips[p] : NULL;
6321           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6322         } break;
6323       case INSERT_BC_VALUES:
6324         for (p = 0; p < numPoints; p++) {
6325           const PetscInt    point = points[2*p];
6326           const PetscInt    *perm = perms ? perms[p] : NULL;
6327           const PetscScalar *flip = flips ? flips[p] : NULL;
6328           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6329         } break;
6330       case ADD_VALUES:
6331         for (p = 0; p < numPoints; p++) {
6332           const PetscInt    point = points[2*p];
6333           const PetscInt    *perm = perms ? perms[p] : NULL;
6334           const PetscScalar *flip = flips ? flips[p] : NULL;
6335           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6336         } break;
6337       case ADD_ALL_VALUES:
6338         for (p = 0; p < numPoints; p++) {
6339           const PetscInt    point = points[2*p];
6340           const PetscInt    *perm = perms ? perms[p] : NULL;
6341           const PetscScalar *flip = flips ? flips[p] : NULL;
6342           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6343         } break;
6344       case ADD_BC_VALUES:
6345         for (p = 0; p < numPoints; p++) {
6346           const PetscInt    point = points[2*p];
6347           const PetscInt    *perm = perms ? perms[p] : NULL;
6348           const PetscScalar *flip = flips ? flips[p] : NULL;
6349           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6350         } break;
6351       default:
6352         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6353       }
6354       PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6355     }
6356   } else {
6357     PetscInt dof, off;
6358     const PetscInt    **perms = NULL;
6359     const PetscScalar **flips = NULL;
6360 
6361     PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
6362     switch (mode) {
6363     case INSERT_VALUES:
6364       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6365         const PetscInt    point = points[2*p];
6366         const PetscInt    *perm = perms ? perms[p] : NULL;
6367         const PetscScalar *flip = flips ? flips[p] : NULL;
6368         PetscCall(PetscSectionGetDof(section, point, &dof));
6369         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6370       } break;
6371     case INSERT_ALL_VALUES:
6372       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6373         const PetscInt    point = points[2*p];
6374         const PetscInt    *perm = perms ? perms[p] : NULL;
6375         const PetscScalar *flip = flips ? flips[p] : NULL;
6376         PetscCall(PetscSectionGetDof(section, point, &dof));
6377         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
6378       } break;
6379     case INSERT_BC_VALUES:
6380       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6381         const PetscInt    point = points[2*p];
6382         const PetscInt    *perm = perms ? perms[p] : NULL;
6383         const PetscScalar *flip = flips ? flips[p] : NULL;
6384         PetscCall(PetscSectionGetDof(section, point, &dof));
6385         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
6386       } break;
6387     case ADD_VALUES:
6388       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6389         const PetscInt    point = points[2*p];
6390         const PetscInt    *perm = perms ? perms[p] : NULL;
6391         const PetscScalar *flip = flips ? flips[p] : NULL;
6392         PetscCall(PetscSectionGetDof(section, point, &dof));
6393         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
6394       } break;
6395     case ADD_ALL_VALUES:
6396       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6397         const PetscInt    point = points[2*p];
6398         const PetscInt    *perm = perms ? perms[p] : NULL;
6399         const PetscScalar *flip = flips ? flips[p] : NULL;
6400         PetscCall(PetscSectionGetDof(section, point, &dof));
6401         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
6402       } break;
6403     case ADD_BC_VALUES:
6404       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6405         const PetscInt    point = points[2*p];
6406         const PetscInt    *perm = perms ? perms[p] : NULL;
6407         const PetscScalar *flip = flips ? flips[p] : NULL;
6408         PetscCall(PetscSectionGetDof(section, point, &dof));
6409         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
6410       } break;
6411     default:
6412       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6413     }
6414     PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
6415   }
6416   /* Cleanup points */
6417   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6418   /* Cleanup array */
6419   PetscCall(VecRestoreArray(v, &array));
6420   PetscFunctionReturn(0);
6421 }
6422 
6423 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6424 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
6425 {
6426   PetscFunctionBegin;
6427   if (label) {
6428     PetscBool contains;
6429     PetscInt  fdof;
6430 
6431     PetscCall(DMLabelStratumHasPoint(label, labelId, point, &contains));
6432     if (!contains) {
6433       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6434       *offset += fdof;
6435       PetscFunctionReturn(1);
6436     }
6437   }
6438   PetscFunctionReturn(0);
6439 }
6440 
6441 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6442 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)
6443 {
6444   PetscSection    clSection;
6445   IS              clPoints;
6446   PetscScalar    *array;
6447   PetscInt       *points = NULL;
6448   const PetscInt *clp;
6449   PetscInt        numFields, numPoints, p;
6450   PetscInt        offset = 0, f;
6451 
6452   PetscFunctionBeginHot;
6453   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6454   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6455   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6456   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6457   PetscCall(PetscSectionGetNumFields(section, &numFields));
6458   /* Get points */
6459   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6460   /* Get array */
6461   PetscCall(VecGetArray(v, &array));
6462   /* Get values */
6463   for (f = 0; f < numFields; ++f) {
6464     const PetscInt    **perms = NULL;
6465     const PetscScalar **flips = NULL;
6466 
6467     if (!fieldActive[f]) {
6468       for (p = 0; p < numPoints*2; p += 2) {
6469         PetscInt fdof;
6470         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6471         offset += fdof;
6472       }
6473       continue;
6474     }
6475     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6476     switch (mode) {
6477     case INSERT_VALUES:
6478       for (p = 0; p < numPoints; p++) {
6479         const PetscInt    point = points[2*p];
6480         const PetscInt    *perm = perms ? perms[p] : NULL;
6481         const PetscScalar *flip = flips ? flips[p] : NULL;
6482         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6483         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6484       } break;
6485     case INSERT_ALL_VALUES:
6486       for (p = 0; p < numPoints; p++) {
6487         const PetscInt    point = points[2*p];
6488         const PetscInt    *perm = perms ? perms[p] : NULL;
6489         const PetscScalar *flip = flips ? flips[p] : NULL;
6490         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6491         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6492       } break;
6493     case INSERT_BC_VALUES:
6494       for (p = 0; p < numPoints; p++) {
6495         const PetscInt    point = points[2*p];
6496         const PetscInt    *perm = perms ? perms[p] : NULL;
6497         const PetscScalar *flip = flips ? flips[p] : NULL;
6498         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6499         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6500       } break;
6501     case ADD_VALUES:
6502       for (p = 0; p < numPoints; p++) {
6503         const PetscInt    point = points[2*p];
6504         const PetscInt    *perm = perms ? perms[p] : NULL;
6505         const PetscScalar *flip = flips ? flips[p] : NULL;
6506         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6507         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6508       } break;
6509     case ADD_ALL_VALUES:
6510       for (p = 0; p < numPoints; p++) {
6511         const PetscInt    point = points[2*p];
6512         const PetscInt    *perm = perms ? perms[p] : NULL;
6513         const PetscScalar *flip = flips ? flips[p] : NULL;
6514         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6515         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6516       } break;
6517     default:
6518       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6519     }
6520     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6521   }
6522   /* Cleanup points */
6523   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6524   /* Cleanup array */
6525   PetscCall(VecRestoreArray(v, &array));
6526   PetscFunctionReturn(0);
6527 }
6528 
6529 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6530 {
6531   PetscMPIInt    rank;
6532   PetscInt       i, j;
6533 
6534   PetscFunctionBegin;
6535   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6536   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6537   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6538   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6539   numCIndices = numCIndices ? numCIndices : numRIndices;
6540   if (!values) PetscFunctionReturn(0);
6541   for (i = 0; i < numRIndices; i++) {
6542     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6543     for (j = 0; j < numCIndices; j++) {
6544 #if defined(PETSC_USE_COMPLEX)
6545       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j])));
6546 #else
6547       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]));
6548 #endif
6549     }
6550     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6551   }
6552   PetscFunctionReturn(0);
6553 }
6554 
6555 /*
6556   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6557 
6558   Input Parameters:
6559 + section - The section for this data layout
6560 . islocal - Is the section (and thus indices being requested) local or global?
6561 . point   - The point contributing dofs with these indices
6562 . off     - The global offset of this point
6563 . loff    - The local offset of each field
6564 . setBC   - The flag determining whether to include indices of boundary values
6565 . perm    - A permutation of the dofs on this point, or NULL
6566 - indperm - A permutation of the entire indices array, or NULL
6567 
6568   Output Parameter:
6569 . indices - Indices for dofs on this point
6570 
6571   Level: developer
6572 
6573   Note: The indices could be local or global, depending on the value of 'off'.
6574 */
6575 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6576 {
6577   PetscInt        dof;   /* The number of unknowns on this point */
6578   PetscInt        cdof;  /* The number of constraints on this point */
6579   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6580   PetscInt        cind = 0, k;
6581 
6582   PetscFunctionBegin;
6583   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6584   PetscCall(PetscSectionGetDof(section, point, &dof));
6585   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6586   if (!cdof || setBC) {
6587     for (k = 0; k < dof; ++k) {
6588       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6589       const PetscInt ind    = indperm ? indperm[preind] : preind;
6590 
6591       indices[ind] = off + k;
6592     }
6593   } else {
6594     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6595     for (k = 0; k < dof; ++k) {
6596       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6597       const PetscInt ind    = indperm ? indperm[preind] : preind;
6598 
6599       if ((cind < cdof) && (k == cdofs[cind])) {
6600         /* Insert check for returning constrained indices */
6601         indices[ind] = -(off+k+1);
6602         ++cind;
6603       } else {
6604         indices[ind] = off + k - (islocal ? 0 : cind);
6605       }
6606     }
6607   }
6608   *loff += dof;
6609   PetscFunctionReturn(0);
6610 }
6611 
6612 /*
6613  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6614 
6615  Input Parameters:
6616 + section - a section (global or local)
6617 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6618 . point - point within section
6619 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6620 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6621 . setBC - identify constrained (boundary condition) points via involution.
6622 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6623 . permsoff - offset
6624 - indperm - index permutation
6625 
6626  Output Parameter:
6627 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6628 . indices - array to hold indices (as defined by section) of each dof associated with point
6629 
6630  Notes:
6631  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6632  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6633  in the local vector.
6634 
6635  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6636  significant).  It is invalid to call with a global section and setBC=true.
6637 
6638  Developer Note:
6639  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6640  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6641  offset could be obtained from the section instead of passing it explicitly as we do now.
6642 
6643  Example:
6644  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6645  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6646  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6647  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.
6648 
6649  Level: developer
6650 */
6651 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[])
6652 {
6653   PetscInt       numFields, foff, f;
6654 
6655   PetscFunctionBegin;
6656   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6657   PetscCall(PetscSectionGetNumFields(section, &numFields));
6658   for (f = 0, foff = 0; f < numFields; ++f) {
6659     PetscInt        fdof, cfdof;
6660     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6661     PetscInt        cind = 0, b;
6662     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6663 
6664     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6665     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6666     if (!cfdof || setBC) {
6667       for (b = 0; b < fdof; ++b) {
6668         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6669         const PetscInt ind    = indperm ? indperm[preind] : preind;
6670 
6671         indices[ind] = off+foff+b;
6672       }
6673     } else {
6674       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6675       for (b = 0; b < fdof; ++b) {
6676         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6677         const PetscInt ind    = indperm ? indperm[preind] : preind;
6678 
6679         if ((cind < cfdof) && (b == fcdofs[cind])) {
6680           indices[ind] = -(off+foff+b+1);
6681           ++cind;
6682         } else {
6683           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6684         }
6685       }
6686     }
6687     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6688     foffs[f] += fdof;
6689   }
6690   PetscFunctionReturn(0);
6691 }
6692 
6693 /*
6694   This version believes the globalSection offsets for each field, rather than just the point offset
6695 
6696  . foffs - The offset into 'indices' for each field, since it is segregated by field
6697 
6698  Notes:
6699  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6700  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6701 */
6702 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6703 {
6704   PetscInt       numFields, foff, f;
6705 
6706   PetscFunctionBegin;
6707   PetscCall(PetscSectionGetNumFields(section, &numFields));
6708   for (f = 0; f < numFields; ++f) {
6709     PetscInt        fdof, cfdof;
6710     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6711     PetscInt        cind = 0, b;
6712     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6713 
6714     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6715     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6716     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6717     if (!cfdof) {
6718       for (b = 0; b < fdof; ++b) {
6719         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6720         const PetscInt ind    = indperm ? indperm[preind] : preind;
6721 
6722         indices[ind] = foff+b;
6723       }
6724     } else {
6725       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6726       for (b = 0; b < fdof; ++b) {
6727         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6728         const PetscInt ind    = indperm ? indperm[preind] : preind;
6729 
6730         if ((cind < cfdof) && (b == fcdofs[cind])) {
6731           indices[ind] = -(foff+b+1);
6732           ++cind;
6733         } else {
6734           indices[ind] = foff+b-cind;
6735         }
6736       }
6737     }
6738     foffs[f] += fdof;
6739   }
6740   PetscFunctionReturn(0);
6741 }
6742 
6743 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)
6744 {
6745   Mat             cMat;
6746   PetscSection    aSec, cSec;
6747   IS              aIS;
6748   PetscInt        aStart = -1, aEnd = -1;
6749   const PetscInt  *anchors;
6750   PetscInt        numFields, f, p, q, newP = 0;
6751   PetscInt        newNumPoints = 0, newNumIndices = 0;
6752   PetscInt        *newPoints, *indices, *newIndices;
6753   PetscInt        maxAnchor, maxDof;
6754   PetscInt        newOffsets[32];
6755   PetscInt        *pointMatOffsets[32];
6756   PetscInt        *newPointOffsets[32];
6757   PetscScalar     *pointMat[32];
6758   PetscScalar     *newValues=NULL,*tmpValues;
6759   PetscBool       anyConstrained = PETSC_FALSE;
6760 
6761   PetscFunctionBegin;
6762   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6763   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6764   PetscCall(PetscSectionGetNumFields(section, &numFields));
6765 
6766   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
6767   /* if there are point-to-point constraints */
6768   if (aSec) {
6769     PetscCall(PetscArrayzero(newOffsets, 32));
6770     PetscCall(ISGetIndices(aIS,&anchors));
6771     PetscCall(PetscSectionGetChart(aSec,&aStart,&aEnd));
6772     /* figure out how many points are going to be in the new element matrix
6773      * (we allow double counting, because it's all just going to be summed
6774      * into the global matrix anyway) */
6775     for (p = 0; p < 2*numPoints; p+=2) {
6776       PetscInt b    = points[p];
6777       PetscInt bDof = 0, bSecDof;
6778 
6779       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6780       if (!bSecDof) {
6781         continue;
6782       }
6783       if (b >= aStart && b < aEnd) {
6784         PetscCall(PetscSectionGetDof(aSec,b,&bDof));
6785       }
6786       if (bDof) {
6787         /* this point is constrained */
6788         /* it is going to be replaced by its anchors */
6789         PetscInt bOff, q;
6790 
6791         anyConstrained = PETSC_TRUE;
6792         newNumPoints  += bDof;
6793         PetscCall(PetscSectionGetOffset(aSec,b,&bOff));
6794         for (q = 0; q < bDof; q++) {
6795           PetscInt a = anchors[bOff + q];
6796           PetscInt aDof;
6797 
6798           PetscCall(PetscSectionGetDof(section,a,&aDof));
6799           newNumIndices += aDof;
6800           for (f = 0; f < numFields; ++f) {
6801             PetscInt fDof;
6802 
6803             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6804             newOffsets[f+1] += fDof;
6805           }
6806         }
6807       }
6808       else {
6809         /* this point is not constrained */
6810         newNumPoints++;
6811         newNumIndices += bSecDof;
6812         for (f = 0; f < numFields; ++f) {
6813           PetscInt fDof;
6814 
6815           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6816           newOffsets[f+1] += fDof;
6817         }
6818       }
6819     }
6820   }
6821   if (!anyConstrained) {
6822     if (outNumPoints)  *outNumPoints  = 0;
6823     if (outNumIndices) *outNumIndices = 0;
6824     if (outPoints)     *outPoints     = NULL;
6825     if (outValues)     *outValues     = NULL;
6826     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6827     PetscFunctionReturn(0);
6828   }
6829 
6830   if (outNumPoints)  *outNumPoints  = newNumPoints;
6831   if (outNumIndices) *outNumIndices = newNumIndices;
6832 
6833   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6834 
6835   if (!outPoints && !outValues) {
6836     if (offsets) {
6837       for (f = 0; f <= numFields; f++) {
6838         offsets[f] = newOffsets[f];
6839       }
6840     }
6841     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6842     PetscFunctionReturn(0);
6843   }
6844 
6845   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6846 
6847   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6848 
6849   /* workspaces */
6850   if (numFields) {
6851     for (f = 0; f < numFields; f++) {
6852       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
6853       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
6854     }
6855   }
6856   else {
6857     PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
6858     PetscCall(DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]));
6859   }
6860 
6861   /* get workspaces for the point-to-point matrices */
6862   if (numFields) {
6863     PetscInt totalOffset, totalMatOffset;
6864 
6865     for (p = 0; p < numPoints; p++) {
6866       PetscInt b    = points[2*p];
6867       PetscInt bDof = 0, bSecDof;
6868 
6869       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6870       if (!bSecDof) {
6871         for (f = 0; f < numFields; f++) {
6872           newPointOffsets[f][p + 1] = 0;
6873           pointMatOffsets[f][p + 1] = 0;
6874         }
6875         continue;
6876       }
6877       if (b >= aStart && b < aEnd) {
6878         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6879       }
6880       if (bDof) {
6881         for (f = 0; f < numFields; f++) {
6882           PetscInt fDof, q, bOff, allFDof = 0;
6883 
6884           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6885           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6886           for (q = 0; q < bDof; q++) {
6887             PetscInt a = anchors[bOff + q];
6888             PetscInt aFDof;
6889 
6890             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6891             allFDof += aFDof;
6892           }
6893           newPointOffsets[f][p+1] = allFDof;
6894           pointMatOffsets[f][p+1] = fDof * allFDof;
6895         }
6896       }
6897       else {
6898         for (f = 0; f < numFields; f++) {
6899           PetscInt fDof;
6900 
6901           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6902           newPointOffsets[f][p+1] = fDof;
6903           pointMatOffsets[f][p+1] = 0;
6904         }
6905       }
6906     }
6907     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6908       newPointOffsets[f][0] = totalOffset;
6909       pointMatOffsets[f][0] = totalMatOffset;
6910       for (p = 0; p < numPoints; p++) {
6911         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6912         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6913       }
6914       totalOffset    = newPointOffsets[f][numPoints];
6915       totalMatOffset = pointMatOffsets[f][numPoints];
6916       PetscCall(DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
6917     }
6918   }
6919   else {
6920     for (p = 0; p < numPoints; p++) {
6921       PetscInt b    = points[2*p];
6922       PetscInt bDof = 0, bSecDof;
6923 
6924       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6925       if (!bSecDof) {
6926         newPointOffsets[0][p + 1] = 0;
6927         pointMatOffsets[0][p + 1] = 0;
6928         continue;
6929       }
6930       if (b >= aStart && b < aEnd) {
6931         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6932       }
6933       if (bDof) {
6934         PetscInt bOff, q, allDof = 0;
6935 
6936         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6937         for (q = 0; q < bDof; q++) {
6938           PetscInt a = anchors[bOff + q], aDof;
6939 
6940           PetscCall(PetscSectionGetDof(section, a, &aDof));
6941           allDof += aDof;
6942         }
6943         newPointOffsets[0][p+1] = allDof;
6944         pointMatOffsets[0][p+1] = bSecDof * allDof;
6945       }
6946       else {
6947         newPointOffsets[0][p+1] = bSecDof;
6948         pointMatOffsets[0][p+1] = 0;
6949       }
6950     }
6951     newPointOffsets[0][0] = 0;
6952     pointMatOffsets[0][0] = 0;
6953     for (p = 0; p < numPoints; p++) {
6954       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6955       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6956     }
6957     PetscCall(DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
6958   }
6959 
6960   /* output arrays */
6961   PetscCall(DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
6962 
6963   /* get the point-to-point matrices; construct newPoints */
6964   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6965   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6966   PetscCall(DMGetWorkArray(dm,maxDof,MPIU_INT,&indices));
6967   PetscCall(DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
6968   if (numFields) {
6969     for (p = 0, newP = 0; p < numPoints; p++) {
6970       PetscInt b    = points[2*p];
6971       PetscInt o    = points[2*p+1];
6972       PetscInt bDof = 0, bSecDof;
6973 
6974       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6975       if (!bSecDof) {
6976         continue;
6977       }
6978       if (b >= aStart && b < aEnd) {
6979         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6980       }
6981       if (bDof) {
6982         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6983 
6984         fStart[0] = 0;
6985         fEnd[0]   = 0;
6986         for (f = 0; f < numFields; f++) {
6987           PetscInt fDof;
6988 
6989           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
6990           fStart[f+1] = fStart[f] + fDof;
6991           fEnd[f+1]   = fStart[f+1];
6992         }
6993         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
6994         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
6995 
6996         fAnchorStart[0] = 0;
6997         fAnchorEnd[0]   = 0;
6998         for (f = 0; f < numFields; f++) {
6999           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7000 
7001           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
7002           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
7003         }
7004         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7005         for (q = 0; q < bDof; q++) {
7006           PetscInt a = anchors[bOff + q], aOff;
7007 
7008           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7009           newPoints[2*(newP + q)]     = a;
7010           newPoints[2*(newP + q) + 1] = 0;
7011           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7012           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7013         }
7014         newP += bDof;
7015 
7016         if (outValues) {
7017           /* get the point-to-point submatrix */
7018           for (f = 0; f < numFields; f++) {
7019             PetscCall(MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]));
7020           }
7021         }
7022       }
7023       else {
7024         newPoints[2 * newP]     = b;
7025         newPoints[2 * newP + 1] = o;
7026         newP++;
7027       }
7028     }
7029   } else {
7030     for (p = 0; p < numPoints; p++) {
7031       PetscInt b    = points[2*p];
7032       PetscInt o    = points[2*p+1];
7033       PetscInt bDof = 0, bSecDof;
7034 
7035       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7036       if (!bSecDof) {
7037         continue;
7038       }
7039       if (b >= aStart && b < aEnd) {
7040         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7041       }
7042       if (bDof) {
7043         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7044 
7045         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7046         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7047 
7048         PetscCall(PetscSectionGetOffset (aSec, b, &bOff));
7049         for (q = 0; q < bDof; q++) {
7050           PetscInt a = anchors[bOff + q], aOff;
7051 
7052           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7053 
7054           newPoints[2*(newP + q)]     = a;
7055           newPoints[2*(newP + q) + 1] = 0;
7056           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7057           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7058         }
7059         newP += bDof;
7060 
7061         /* get the point-to-point submatrix */
7062         if (outValues) {
7063           PetscCall(MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]));
7064         }
7065       }
7066       else {
7067         newPoints[2 * newP]     = b;
7068         newPoints[2 * newP + 1] = o;
7069         newP++;
7070       }
7071     }
7072   }
7073 
7074   if (outValues) {
7075     PetscCall(DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7076     PetscCall(PetscArrayzero(tmpValues,newNumIndices*numIndices));
7077     /* multiply constraints on the right */
7078     if (numFields) {
7079       for (f = 0; f < numFields; f++) {
7080         PetscInt oldOff = offsets[f];
7081 
7082         for (p = 0; p < numPoints; p++) {
7083           PetscInt cStart = newPointOffsets[f][p];
7084           PetscInt b      = points[2 * p];
7085           PetscInt c, r, k;
7086           PetscInt dof;
7087 
7088           PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7089           if (!dof) {
7090             continue;
7091           }
7092           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7093             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
7094             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
7095 
7096             for (r = 0; r < numIndices; r++) {
7097               for (c = 0; c < nCols; c++) {
7098                 for (k = 0; k < dof; k++) {
7099                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7100                 }
7101               }
7102             }
7103           }
7104           else {
7105             /* copy this column as is */
7106             for (r = 0; r < numIndices; r++) {
7107               for (c = 0; c < dof; c++) {
7108                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7109               }
7110             }
7111           }
7112           oldOff += dof;
7113         }
7114       }
7115     }
7116     else {
7117       PetscInt oldOff = 0;
7118       for (p = 0; p < numPoints; p++) {
7119         PetscInt cStart = newPointOffsets[0][p];
7120         PetscInt b      = points[2 * p];
7121         PetscInt c, r, k;
7122         PetscInt dof;
7123 
7124         PetscCall(PetscSectionGetDof(section,b,&dof));
7125         if (!dof) {
7126           continue;
7127         }
7128         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7129           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
7130           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
7131 
7132           for (r = 0; r < numIndices; r++) {
7133             for (c = 0; c < nCols; c++) {
7134               for (k = 0; k < dof; k++) {
7135                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7136               }
7137             }
7138           }
7139         }
7140         else {
7141           /* copy this column as is */
7142           for (r = 0; r < numIndices; r++) {
7143             for (c = 0; c < dof; c++) {
7144               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7145             }
7146           }
7147         }
7148         oldOff += dof;
7149       }
7150     }
7151 
7152     if (multiplyLeft) {
7153       PetscCall(DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues));
7154       PetscCall(PetscArrayzero(newValues,newNumIndices*newNumIndices));
7155       /* multiply constraints transpose on the left */
7156       if (numFields) {
7157         for (f = 0; f < numFields; f++) {
7158           PetscInt oldOff = offsets[f];
7159 
7160           for (p = 0; p < numPoints; p++) {
7161             PetscInt rStart = newPointOffsets[f][p];
7162             PetscInt b      = points[2 * p];
7163             PetscInt c, r, k;
7164             PetscInt dof;
7165 
7166             PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7167             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7168               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
7169               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
7170 
7171               for (r = 0; r < nRows; r++) {
7172                 for (c = 0; c < newNumIndices; c++) {
7173                   for (k = 0; k < dof; k++) {
7174                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7175                   }
7176                 }
7177               }
7178             }
7179             else {
7180               /* copy this row as is */
7181               for (r = 0; r < dof; r++) {
7182                 for (c = 0; c < newNumIndices; c++) {
7183                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7184                 }
7185               }
7186             }
7187             oldOff += dof;
7188           }
7189         }
7190       }
7191       else {
7192         PetscInt oldOff = 0;
7193 
7194         for (p = 0; p < numPoints; p++) {
7195           PetscInt rStart = newPointOffsets[0][p];
7196           PetscInt b      = points[2 * p];
7197           PetscInt c, r, k;
7198           PetscInt dof;
7199 
7200           PetscCall(PetscSectionGetDof(section,b,&dof));
7201           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7202             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
7203             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
7204 
7205             for (r = 0; r < nRows; r++) {
7206               for (c = 0; c < newNumIndices; c++) {
7207                 for (k = 0; k < dof; k++) {
7208                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7209                 }
7210               }
7211             }
7212           }
7213           else {
7214             /* copy this row as is */
7215             for (r = 0; r < dof; r++) {
7216               for (c = 0; c < newNumIndices; c++) {
7217                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7218               }
7219             }
7220           }
7221           oldOff += dof;
7222         }
7223       }
7224 
7225       PetscCall(DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7226     }
7227     else {
7228       newValues = tmpValues;
7229     }
7230   }
7231 
7232   /* clean up */
7233   PetscCall(DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices));
7234   PetscCall(DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
7235 
7236   if (numFields) {
7237     for (f = 0; f < numFields; f++) {
7238       PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
7239       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
7240       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
7241     }
7242   }
7243   else {
7244     PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
7245     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
7246     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]));
7247   }
7248   PetscCall(ISRestoreIndices(aIS,&anchors));
7249 
7250   /* output */
7251   if (outPoints) {
7252     *outPoints = newPoints;
7253   }
7254   else {
7255     PetscCall(DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
7256   }
7257   if (outValues) {
7258     *outValues = newValues;
7259   }
7260   for (f = 0; f <= numFields; f++) {
7261     offsets[f] = newOffsets[f];
7262   }
7263   PetscFunctionReturn(0);
7264 }
7265 
7266 /*@C
7267   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7268 
7269   Not collective
7270 
7271   Input Parameters:
7272 + dm         - The DM
7273 . section    - The PetscSection describing the points (a local section)
7274 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7275 . point      - The point defining the closure
7276 - useClPerm  - Use the closure point permutation if available
7277 
7278   Output Parameters:
7279 + numIndices - The number of dof indices in the closure of point with the input sections
7280 . indices    - The dof indices
7281 . outOffsets - Array to write the field offsets into, or NULL
7282 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7283 
7284   Notes:
7285   Must call DMPlexRestoreClosureIndices() to free allocated memory
7286 
7287   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7288   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7289   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7290   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7291   indices (with the above semantics) are implied.
7292 
7293   Level: advanced
7294 
7295 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7296 @*/
7297 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7298                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7299 {
7300   /* Closure ordering */
7301   PetscSection        clSection;
7302   IS                  clPoints;
7303   const PetscInt     *clp;
7304   PetscInt           *points;
7305   const PetscInt     *clperm = NULL;
7306   /* Dof permutation and sign flips */
7307   const PetscInt    **perms[32] = {NULL};
7308   const PetscScalar **flips[32] = {NULL};
7309   PetscScalar        *valCopy   = NULL;
7310   /* Hanging node constraints */
7311   PetscInt           *pointsC = NULL;
7312   PetscScalar        *valuesC = NULL;
7313   PetscInt            NclC, NiC;
7314 
7315   PetscInt           *idx;
7316   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
7317   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7318 
7319   PetscFunctionBeginHot;
7320   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7321   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7322   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7323   if (numIndices) PetscValidIntPointer(numIndices, 6);
7324   if (indices)    PetscValidPointer(indices, 7);
7325   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7326   if (values)     PetscValidPointer(values, 9);
7327   PetscCall(PetscSectionGetNumFields(section, &Nf));
7328   PetscCheck(Nf <= 31,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7329   PetscCall(PetscArrayzero(offsets, 32));
7330   /* 1) Get points in closure */
7331   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7332   if (useClPerm) {
7333     PetscInt depth, clsize;
7334     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7335     for (clsize=0,p=0; p<Ncl; p++) {
7336       PetscInt dof;
7337       PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
7338       clsize += dof;
7339     }
7340     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
7341   }
7342   /* 2) Get number of indices on these points and field offsets from section */
7343   for (p = 0; p < Ncl*2; p += 2) {
7344     PetscInt dof, fdof;
7345 
7346     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7347     for (f = 0; f < Nf; ++f) {
7348       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7349       offsets[f+1] += fdof;
7350     }
7351     Ni += dof;
7352   }
7353   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
7354   PetscCheck(!Nf || offsets[Nf] == Ni,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7355   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7356   for (f = 0; f < PetscMax(1, Nf); ++f) {
7357     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7358     else    PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7359     /* may need to apply sign changes to the element matrix */
7360     if (values && flips[f]) {
7361       PetscInt foffset = offsets[f];
7362 
7363       for (p = 0; p < Ncl; ++p) {
7364         PetscInt           pnt  = points[2*p], fdof;
7365         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7366 
7367         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7368         else     PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7369         if (flip) {
7370           PetscInt i, j, k;
7371 
7372           if (!valCopy) {
7373             PetscCall(DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7374             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7375             *values = valCopy;
7376           }
7377           for (i = 0; i < fdof; ++i) {
7378             PetscScalar fval = flip[i];
7379 
7380             for (k = 0; k < Ni; ++k) {
7381               valCopy[Ni * (foffset + i) + k] *= fval;
7382               valCopy[Ni * k + (foffset + i)] *= fval;
7383             }
7384           }
7385         }
7386         foffset += fdof;
7387       }
7388     }
7389   }
7390   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7391   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7392   if (NclC) {
7393     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7394     for (f = 0; f < PetscMax(1, Nf); ++f) {
7395       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7396       else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7397     }
7398     for (f = 0; f < PetscMax(1, Nf); ++f) {
7399       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7400       else    PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7401     }
7402     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7403     Ncl     = NclC;
7404     Ni      = NiC;
7405     points  = pointsC;
7406     if (values) *values = valuesC;
7407   }
7408   /* 5) Calculate indices */
7409   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7410   if (Nf) {
7411     PetscInt  idxOff;
7412     PetscBool useFieldOffsets;
7413 
7414     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
7415     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7416     if (useFieldOffsets) {
7417       for (p = 0; p < Ncl; ++p) {
7418         const PetscInt pnt = points[p*2];
7419 
7420         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7421       }
7422     } else {
7423       for (p = 0; p < Ncl; ++p) {
7424         const PetscInt pnt = points[p*2];
7425 
7426         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7427         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7428          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7429          * global section. */
7430         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7431       }
7432     }
7433   } else {
7434     PetscInt off = 0, idxOff;
7435 
7436     for (p = 0; p < Ncl; ++p) {
7437       const PetscInt  pnt  = points[p*2];
7438       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7439 
7440       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7441       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7442        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7443       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7444     }
7445   }
7446   /* 6) Cleanup */
7447   for (f = 0; f < PetscMax(1, Nf); ++f) {
7448     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7449     else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7450   }
7451   if (NclC) {
7452     PetscCall(DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC));
7453   } else {
7454     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7455   }
7456 
7457   if (numIndices) *numIndices = Ni;
7458   if (indices)    *indices    = idx;
7459   PetscFunctionReturn(0);
7460 }
7461 
7462 /*@C
7463   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7464 
7465   Not collective
7466 
7467   Input Parameters:
7468 + dm         - The DM
7469 . section    - The PetscSection describing the points (a local section)
7470 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7471 . point      - The point defining the closure
7472 - useClPerm  - Use the closure point permutation if available
7473 
7474   Output Parameters:
7475 + numIndices - The number of dof indices in the closure of point with the input sections
7476 . indices    - The dof indices
7477 . outOffsets - Array to write the field offsets into, or NULL
7478 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7479 
7480   Notes:
7481   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7482 
7483   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7484   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7485   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7486   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7487   indices (with the above semantics) are implied.
7488 
7489   Level: advanced
7490 
7491 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7492 @*/
7493 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7494                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7495 {
7496   PetscFunctionBegin;
7497   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7498   PetscValidPointer(indices, 7);
7499   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7500   PetscFunctionReturn(0);
7501 }
7502 
7503 /*@C
7504   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7505 
7506   Not collective
7507 
7508   Input Parameters:
7509 + dm - The DM
7510 . section - The section describing the layout in v, or NULL to use the default section
7511 . globalSection - The section describing the layout in v, or NULL to use the default global section
7512 . A - The matrix
7513 . point - The point in the DM
7514 . values - The array of values
7515 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7516 
7517   Fortran Notes:
7518   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7519 
7520   Level: intermediate
7521 
7522 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7523 @*/
7524 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7525 {
7526   DM_Plex           *mesh = (DM_Plex*) dm->data;
7527   PetscInt          *indices;
7528   PetscInt           numIndices;
7529   const PetscScalar *valuesOrig = values;
7530   PetscErrorCode     ierr;
7531 
7532   PetscFunctionBegin;
7533   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7534   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7535   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7536   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7537   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7538   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7539 
7540   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7541 
7542   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7543   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7544   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7545   if (ierr) {
7546     PetscMPIInt    rank;
7547 
7548     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7549     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7550     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7551     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7552     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7553     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7554   }
7555   if (mesh->printFEM > 1) {
7556     PetscInt i;
7557     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7558     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7559     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7560   }
7561 
7562   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7563   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7564   PetscFunctionReturn(0);
7565 }
7566 
7567 /*@C
7568   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7569 
7570   Not collective
7571 
7572   Input Parameters:
7573 + dmRow - The DM for the row fields
7574 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7575 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7576 . dmCol - The DM for the column fields
7577 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7578 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7579 . A - The matrix
7580 . point - The point in the DMs
7581 . values - The array of values
7582 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7583 
7584   Level: intermediate
7585 
7586 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7587 @*/
7588 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7589 {
7590   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7591   PetscInt          *indicesRow, *indicesCol;
7592   PetscInt           numIndicesRow, numIndicesCol;
7593   const PetscScalar *valuesOrig = values;
7594   PetscErrorCode     ierr;
7595 
7596   PetscFunctionBegin;
7597   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7598   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7599   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7600   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7601   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7602   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7603   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7604   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7605   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7606   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7607   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7608 
7609   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7610   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7611 
7612   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7613   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7614   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7615   if (ierr) {
7616     PetscMPIInt    rank;
7617 
7618     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7619     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7620     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7621     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7622     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7623     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7624   }
7625 
7626   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7627   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7628   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7629   PetscFunctionReturn(0);
7630 }
7631 
7632 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7633 {
7634   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7635   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7636   PetscInt       *cpoints = NULL;
7637   PetscInt       *findices, *cindices;
7638   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7639   PetscInt        foffsets[32], coffsets[32];
7640   DMPolytopeType  ct;
7641   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7642   PetscErrorCode  ierr;
7643 
7644   PetscFunctionBegin;
7645   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7646   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7647   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7648   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7649   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7650   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7651   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7652   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7653   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7654   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7655   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7656   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7657   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7658   PetscCall(PetscArrayzero(foffsets, 32));
7659   PetscCall(PetscArrayzero(coffsets, 32));
7660   /* Column indices */
7661   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7662   maxFPoints = numCPoints;
7663   /* Compress out points not in the section */
7664   /*   TODO: Squeeze out points with 0 dof as well */
7665   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7666   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7667     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7668       cpoints[q*2]   = cpoints[p];
7669       cpoints[q*2+1] = cpoints[p+1];
7670       ++q;
7671     }
7672   }
7673   numCPoints = q;
7674   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7675     PetscInt fdof;
7676 
7677     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7678     if (!dof) continue;
7679     for (f = 0; f < numFields; ++f) {
7680       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7681       coffsets[f+1] += fdof;
7682     }
7683     numCIndices += dof;
7684   }
7685   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7686   /* Row indices */
7687   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7688   {
7689     DMPlexTransform tr;
7690     DMPolytopeType *rct;
7691     PetscInt       *rsize, *rcone, *rornt, Nt;
7692 
7693     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7694     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7695     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7696     numSubcells = rsize[Nt-1];
7697     PetscCall(DMPlexTransformDestroy(&tr));
7698   }
7699   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7700   for (r = 0, q = 0; r < numSubcells; ++r) {
7701     /* TODO Map from coarse to fine cells */
7702     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7703     /* Compress out points not in the section */
7704     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7705     for (p = 0; p < numFPoints*2; p += 2) {
7706       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7707         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7708         if (!dof) continue;
7709         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7710         if (s < q) continue;
7711         ftotpoints[q*2]   = fpoints[p];
7712         ftotpoints[q*2+1] = fpoints[p+1];
7713         ++q;
7714       }
7715     }
7716     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7717   }
7718   numFPoints = q;
7719   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7720     PetscInt fdof;
7721 
7722     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7723     if (!dof) continue;
7724     for (f = 0; f < numFields; ++f) {
7725       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7726       foffsets[f+1] += fdof;
7727     }
7728     numFIndices += dof;
7729   }
7730   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7731 
7732   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7733   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7734   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7735   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7736   if (numFields) {
7737     const PetscInt **permsF[32] = {NULL};
7738     const PetscInt **permsC[32] = {NULL};
7739 
7740     for (f = 0; f < numFields; f++) {
7741       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7742       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7743     }
7744     for (p = 0; p < numFPoints; p++) {
7745       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7746       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7747     }
7748     for (p = 0; p < numCPoints; p++) {
7749       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7750       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7751     }
7752     for (f = 0; f < numFields; f++) {
7753       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7754       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7755     }
7756   } else {
7757     const PetscInt **permsF = NULL;
7758     const PetscInt **permsC = NULL;
7759 
7760     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7761     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7762     for (p = 0, off = 0; p < numFPoints; p++) {
7763       const PetscInt *perm = permsF ? permsF[p] : NULL;
7764 
7765       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7766       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7767     }
7768     for (p = 0, off = 0; p < numCPoints; p++) {
7769       const PetscInt *perm = permsC ? permsC[p] : NULL;
7770 
7771       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7772       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7773     }
7774     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7775     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7776   }
7777   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7778   /* TODO: flips */
7779   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7780   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7781   if (ierr) {
7782     PetscMPIInt    rank;
7783 
7784     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7785     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7786     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7787     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7788     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7789   }
7790   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7791   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7792   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7793   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7794   PetscFunctionReturn(0);
7795 }
7796 
7797 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7798 {
7799   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7800   PetscInt      *cpoints = NULL;
7801   PetscInt       foffsets[32], coffsets[32];
7802   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7803   DMPolytopeType ct;
7804   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7805 
7806   PetscFunctionBegin;
7807   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7808   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7809   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7810   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7811   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7812   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7813   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7814   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7815   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7816   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7817   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7818   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7819   PetscCall(PetscArrayzero(foffsets, 32));
7820   PetscCall(PetscArrayzero(coffsets, 32));
7821   /* Column indices */
7822   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7823   maxFPoints = numCPoints;
7824   /* Compress out points not in the section */
7825   /*   TODO: Squeeze out points with 0 dof as well */
7826   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7827   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7828     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7829       cpoints[q*2]   = cpoints[p];
7830       cpoints[q*2+1] = cpoints[p+1];
7831       ++q;
7832     }
7833   }
7834   numCPoints = q;
7835   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7836     PetscInt fdof;
7837 
7838     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7839     if (!dof) continue;
7840     for (f = 0; f < numFields; ++f) {
7841       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7842       coffsets[f+1] += fdof;
7843     }
7844     numCIndices += dof;
7845   }
7846   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7847   /* Row indices */
7848   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7849   {
7850     DMPlexTransform tr;
7851     DMPolytopeType *rct;
7852     PetscInt       *rsize, *rcone, *rornt, Nt;
7853 
7854     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7855     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7856     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7857     numSubcells = rsize[Nt-1];
7858     PetscCall(DMPlexTransformDestroy(&tr));
7859   }
7860   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7861   for (r = 0, q = 0; r < numSubcells; ++r) {
7862     /* TODO Map from coarse to fine cells */
7863     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7864     /* Compress out points not in the section */
7865     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7866     for (p = 0; p < numFPoints*2; p += 2) {
7867       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7868         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7869         if (!dof) continue;
7870         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7871         if (s < q) continue;
7872         ftotpoints[q*2]   = fpoints[p];
7873         ftotpoints[q*2+1] = fpoints[p+1];
7874         ++q;
7875       }
7876     }
7877     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7878   }
7879   numFPoints = q;
7880   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7881     PetscInt fdof;
7882 
7883     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7884     if (!dof) continue;
7885     for (f = 0; f < numFields; ++f) {
7886       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7887       foffsets[f+1] += fdof;
7888     }
7889     numFIndices += dof;
7890   }
7891   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7892 
7893   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7894   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7895   if (numFields) {
7896     const PetscInt **permsF[32] = {NULL};
7897     const PetscInt **permsC[32] = {NULL};
7898 
7899     for (f = 0; f < numFields; f++) {
7900       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7901       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7902     }
7903     for (p = 0; p < numFPoints; p++) {
7904       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7905       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7906     }
7907     for (p = 0; p < numCPoints; p++) {
7908       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7909       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7910     }
7911     for (f = 0; f < numFields; f++) {
7912       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7913       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7914     }
7915   } else {
7916     const PetscInt **permsF = NULL;
7917     const PetscInt **permsC = NULL;
7918 
7919     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7920     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7921     for (p = 0, off = 0; p < numFPoints; p++) {
7922       const PetscInt *perm = permsF ? permsF[p] : NULL;
7923 
7924       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7925       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7926     }
7927     for (p = 0, off = 0; p < numCPoints; p++) {
7928       const PetscInt *perm = permsC ? permsC[p] : NULL;
7929 
7930       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7931       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7932     }
7933     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7934     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7935   }
7936   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7937   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7938   PetscFunctionReturn(0);
7939 }
7940 
7941 /*@C
7942   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7943 
7944   Input Parameter:
7945 . dm   - The DMPlex object
7946 
7947   Output Parameter:
7948 . cellHeight - The height of a cell
7949 
7950   Level: developer
7951 
7952 .seealso `DMPlexSetVTKCellHeight()`
7953 @*/
7954 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7955 {
7956   DM_Plex *mesh = (DM_Plex*) dm->data;
7957 
7958   PetscFunctionBegin;
7959   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7960   PetscValidIntPointer(cellHeight, 2);
7961   *cellHeight = mesh->vtkCellHeight;
7962   PetscFunctionReturn(0);
7963 }
7964 
7965 /*@C
7966   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7967 
7968   Input Parameters:
7969 + dm   - The DMPlex object
7970 - cellHeight - The height of a cell
7971 
7972   Level: developer
7973 
7974 .seealso `DMPlexGetVTKCellHeight()`
7975 @*/
7976 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7977 {
7978   DM_Plex *mesh = (DM_Plex*) dm->data;
7979 
7980   PetscFunctionBegin;
7981   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7982   mesh->vtkCellHeight = cellHeight;
7983   PetscFunctionReturn(0);
7984 }
7985 
7986 /*@
7987   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7988 
7989   Input Parameter:
7990 . dm - The DMPlex object
7991 
7992   Output Parameters:
7993 + gcStart - The first ghost cell, or NULL
7994 - gcEnd   - The upper bound on ghost cells, or NULL
7995 
7996   Level: advanced
7997 
7998 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
7999 @*/
8000 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8001 {
8002   DMLabel        ctLabel;
8003 
8004   PetscFunctionBegin;
8005   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8006   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8007   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8008   // Reset label for fast lookup
8009   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8010   PetscFunctionReturn(0);
8011 }
8012 
8013 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8014 {
8015   PetscSection   section, globalSection;
8016   PetscInt      *numbers, p;
8017 
8018   PetscFunctionBegin;
8019   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
8020   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8021   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8022   for (p = pStart; p < pEnd; ++p) {
8023     PetscCall(PetscSectionSetDof(section, p, 1));
8024   }
8025   PetscCall(PetscSectionSetUp(section));
8026   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8027   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8028   for (p = pStart; p < pEnd; ++p) {
8029     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8030     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8031     else                       numbers[p-pStart] += shift;
8032   }
8033   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8034   if (globalSize) {
8035     PetscLayout layout;
8036     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8037     PetscCall(PetscLayoutGetSize(layout, globalSize));
8038     PetscCall(PetscLayoutDestroy(&layout));
8039   }
8040   PetscCall(PetscSectionDestroy(&section));
8041   PetscCall(PetscSectionDestroy(&globalSection));
8042   PetscFunctionReturn(0);
8043 }
8044 
8045 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8046 {
8047   PetscInt       cellHeight, cStart, cEnd;
8048 
8049   PetscFunctionBegin;
8050   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8051   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8052   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8053   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8054   PetscFunctionReturn(0);
8055 }
8056 
8057 /*@
8058   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8059 
8060   Input Parameter:
8061 . dm   - The DMPlex object
8062 
8063   Output Parameter:
8064 . globalCellNumbers - Global cell numbers for all cells on this process
8065 
8066   Level: developer
8067 
8068 .seealso `DMPlexGetVertexNumbering()`
8069 @*/
8070 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8071 {
8072   DM_Plex       *mesh = (DM_Plex*) dm->data;
8073 
8074   PetscFunctionBegin;
8075   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8076   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8077   *globalCellNumbers = mesh->globalCellNumbers;
8078   PetscFunctionReturn(0);
8079 }
8080 
8081 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8082 {
8083   PetscInt       vStart, vEnd;
8084 
8085   PetscFunctionBegin;
8086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8087   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8088   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8089   PetscFunctionReturn(0);
8090 }
8091 
8092 /*@
8093   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8094 
8095   Input Parameter:
8096 . dm   - The DMPlex object
8097 
8098   Output Parameter:
8099 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8100 
8101   Level: developer
8102 
8103 .seealso `DMPlexGetCellNumbering()`
8104 @*/
8105 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8106 {
8107   DM_Plex       *mesh = (DM_Plex*) dm->data;
8108 
8109   PetscFunctionBegin;
8110   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8111   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8112   *globalVertexNumbers = mesh->globalVertexNumbers;
8113   PetscFunctionReturn(0);
8114 }
8115 
8116 /*@
8117   DMPlexCreatePointNumbering - Create a global numbering for all points.
8118 
8119   Collective on dm
8120 
8121   Input Parameter:
8122 . dm   - The DMPlex object
8123 
8124   Output Parameter:
8125 . globalPointNumbers - Global numbers for all points on this process
8126 
8127   Notes:
8128 
8129   The point numbering IS is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8130   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8131   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8132   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8133 
8134   The partitioned mesh is
8135 ```
8136  (2)--0--(3)--1--(4)    (1)--0--(2)
8137 ```
8138   and its global numbering is
8139 ```
8140   (3)--0--(4)--1--(5)--2--(6)
8141 ```
8142   Then the global numbering is provided as
8143 ```
8144 [0] Number of indices in set 5
8145 [0] 0 0
8146 [0] 1 1
8147 [0] 2 3
8148 [0] 3 4
8149 [0] 4 -6
8150 [1] Number of indices in set 3
8151 [1] 0 2
8152 [1] 1 5
8153 [1] 2 6
8154 ```
8155 
8156   Level: developer
8157 
8158 .seealso `DMPlexGetCellNumbering()`
8159 @*/
8160 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8161 {
8162   IS             nums[4];
8163   PetscInt       depths[4], gdepths[4], starts[4];
8164   PetscInt       depth, d, shift = 0;
8165 
8166   PetscFunctionBegin;
8167   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8168   PetscCall(DMPlexGetDepth(dm, &depth));
8169   /* For unstratified meshes use dim instead of depth */
8170   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8171   for (d = 0; d <= depth; ++d) {
8172     PetscInt end;
8173 
8174     depths[d] = depth-d;
8175     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8176     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8177   }
8178   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8179   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8180   for (d = 0; d <= depth; ++d) {
8181     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]);
8182   }
8183   for (d = 0; d <= depth; ++d) {
8184     PetscInt pStart, pEnd, gsize;
8185 
8186     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8187     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8188     shift += gsize;
8189   }
8190   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8191   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8192   PetscFunctionReturn(0);
8193 }
8194 
8195 /*@
8196   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8197 
8198   Input Parameter:
8199 . dm - The DMPlex object
8200 
8201   Output Parameter:
8202 . ranks - The rank field
8203 
8204   Options Database Keys:
8205 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8206 
8207   Level: intermediate
8208 
8209 .seealso: `DMView()`
8210 @*/
8211 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8212 {
8213   DM             rdm;
8214   PetscFE        fe;
8215   PetscScalar   *r;
8216   PetscMPIInt    rank;
8217   DMPolytopeType ct;
8218   PetscInt       dim, cStart, cEnd, c;
8219   PetscBool      simplex;
8220 
8221   PetscFunctionBeginUser;
8222   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8223   PetscValidPointer(ranks, 2);
8224   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8225   PetscCall(DMClone(dm, &rdm));
8226   PetscCall(DMGetDimension(rdm, &dim));
8227   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8228   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8229   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8230   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8231   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8232   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8233   PetscCall(PetscFEDestroy(&fe));
8234   PetscCall(DMCreateDS(rdm));
8235   PetscCall(DMCreateGlobalVector(rdm, ranks));
8236   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8237   PetscCall(VecGetArray(*ranks, &r));
8238   for (c = cStart; c < cEnd; ++c) {
8239     PetscScalar *lr;
8240 
8241     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8242     if (lr) *lr = rank;
8243   }
8244   PetscCall(VecRestoreArray(*ranks, &r));
8245   PetscCall(DMDestroy(&rdm));
8246   PetscFunctionReturn(0);
8247 }
8248 
8249 /*@
8250   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8251 
8252   Input Parameters:
8253 + dm    - The DMPlex
8254 - label - The DMLabel
8255 
8256   Output Parameter:
8257 . val - The label value field
8258 
8259   Options Database Keys:
8260 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8261 
8262   Level: intermediate
8263 
8264 .seealso: `DMView()`
8265 @*/
8266 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8267 {
8268   DM             rdm;
8269   PetscFE        fe;
8270   PetscScalar   *v;
8271   PetscInt       dim, cStart, cEnd, c;
8272 
8273   PetscFunctionBeginUser;
8274   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8275   PetscValidPointer(label, 2);
8276   PetscValidPointer(val, 3);
8277   PetscCall(DMClone(dm, &rdm));
8278   PetscCall(DMGetDimension(rdm, &dim));
8279   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8280   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8281   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8282   PetscCall(PetscFEDestroy(&fe));
8283   PetscCall(DMCreateDS(rdm));
8284   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8285   PetscCall(DMCreateGlobalVector(rdm, val));
8286   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8287   PetscCall(VecGetArray(*val, &v));
8288   for (c = cStart; c < cEnd; ++c) {
8289     PetscScalar *lv;
8290     PetscInt     cval;
8291 
8292     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8293     PetscCall(DMLabelGetValue(label, c, &cval));
8294     *lv = cval;
8295   }
8296   PetscCall(VecRestoreArray(*val, &v));
8297   PetscCall(DMDestroy(&rdm));
8298   PetscFunctionReturn(0);
8299 }
8300 
8301 /*@
8302   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8303 
8304   Input Parameter:
8305 . dm - The DMPlex object
8306 
8307   Notes:
8308   This is a useful diagnostic when creating meshes programmatically.
8309 
8310   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8311 
8312   Level: developer
8313 
8314 .seealso: `DMCreate()`, `DMSetFromOptions()`
8315 @*/
8316 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8317 {
8318   PetscSection    coneSection, supportSection;
8319   const PetscInt *cone, *support;
8320   PetscInt        coneSize, c, supportSize, s;
8321   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8322   PetscBool       storagecheck = PETSC_TRUE;
8323 
8324   PetscFunctionBegin;
8325   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8326   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8327   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8328   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8329   /* Check that point p is found in the support of its cone points, and vice versa */
8330   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8331   for (p = pStart; p < pEnd; ++p) {
8332     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8333     PetscCall(DMPlexGetCone(dm, p, &cone));
8334     for (c = 0; c < coneSize; ++c) {
8335       PetscBool dup = PETSC_FALSE;
8336       PetscInt  d;
8337       for (d = c-1; d >= 0; --d) {
8338         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8339       }
8340       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8341       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8342       for (s = 0; s < supportSize; ++s) {
8343         if (support[s] == p) break;
8344       }
8345       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8346         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8347         for (s = 0; s < coneSize; ++s) {
8348           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8349         }
8350         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8351         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8352         for (s = 0; s < supportSize; ++s) {
8353           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8354         }
8355         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8356         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]);
8357         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8358       }
8359     }
8360     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8361     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8362     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8363     PetscCall(DMPlexGetSupport(dm, p, &support));
8364     for (s = 0; s < supportSize; ++s) {
8365       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8366       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8367       for (c = 0; c < coneSize; ++c) {
8368         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8369         if (cone[c] != pp) { c = 0; break; }
8370         if (cone[c] == p) break;
8371       }
8372       if (c >= coneSize) {
8373         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8374         for (c = 0; c < supportSize; ++c) {
8375           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8376         }
8377         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8378         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8379         for (c = 0; c < coneSize; ++c) {
8380           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8381         }
8382         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8383         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8384       }
8385     }
8386   }
8387   if (storagecheck) {
8388     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8389     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8390     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8391   }
8392   PetscFunctionReturn(0);
8393 }
8394 
8395 /*
8396   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.
8397 */
8398 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8399 {
8400   DMPolytopeType  cct;
8401   PetscInt        ptpoints[4];
8402   const PetscInt *cone, *ccone, *ptcone;
8403   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8404 
8405   PetscFunctionBegin;
8406   *unsplit = 0;
8407   switch (ct) {
8408     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8409       ptpoints[npt++] = c;
8410       break;
8411     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8412       PetscCall(DMPlexGetCone(dm, c, &cone));
8413       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8414       for (cp = 0; cp < coneSize; ++cp) {
8415         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8416         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8417       }
8418       break;
8419     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8420     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8421       PetscCall(DMPlexGetCone(dm, c, &cone));
8422       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8423       for (cp = 0; cp < coneSize; ++cp) {
8424         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8425         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8426         for (ccp = 0; ccp < cconeSize; ++ccp) {
8427           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8428           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8429             PetscInt p;
8430             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8431             if (p == npt) ptpoints[npt++] = ccone[ccp];
8432           }
8433         }
8434       }
8435       break;
8436     default: break;
8437   }
8438   for (pt = 0; pt < npt; ++pt) {
8439     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8440     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8441   }
8442   PetscFunctionReturn(0);
8443 }
8444 
8445 /*@
8446   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8447 
8448   Input Parameters:
8449 + dm - The DMPlex object
8450 - cellHeight - Normally 0
8451 
8452   Notes:
8453   This is a useful diagnostic when creating meshes programmatically.
8454   Currently applicable only to homogeneous simplex or tensor meshes.
8455 
8456   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8457 
8458   Level: developer
8459 
8460 .seealso: `DMCreate()`, `DMSetFromOptions()`
8461 @*/
8462 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8463 {
8464   DMPlexInterpolatedFlag interp;
8465   DMPolytopeType         ct;
8466   PetscInt               vStart, vEnd, cStart, cEnd, c;
8467 
8468   PetscFunctionBegin;
8469   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8470   PetscCall(DMPlexIsInterpolated(dm, &interp));
8471   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8472   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8473   for (c = cStart; c < cEnd; ++c) {
8474     PetscInt *closure = NULL;
8475     PetscInt  coneSize, closureSize, cl, Nv = 0;
8476 
8477     PetscCall(DMPlexGetCellType(dm, c, &ct));
8478     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8479     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8480     if (interp == DMPLEX_INTERPOLATED_FULL) {
8481       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8482       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));
8483     }
8484     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8485     for (cl = 0; cl < closureSize*2; cl += 2) {
8486       const PetscInt p = closure[cl];
8487       if ((p >= vStart) && (p < vEnd)) ++Nv;
8488     }
8489     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8490     /* Special Case: Tensor faces with identified vertices */
8491     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8492       PetscInt unsplit;
8493 
8494       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8495       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8496     }
8497     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));
8498   }
8499   PetscFunctionReturn(0);
8500 }
8501 
8502 /*@
8503   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8504 
8505   Collective
8506 
8507   Input Parameters:
8508 + dm - The DMPlex object
8509 - cellHeight - Normally 0
8510 
8511   Notes:
8512   This is a useful diagnostic when creating meshes programmatically.
8513   This routine is only relevant for meshes that are fully interpolated across all ranks.
8514   It will error out if a partially interpolated mesh is given on some rank.
8515   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8516 
8517   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8518 
8519   Level: developer
8520 
8521 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8522 @*/
8523 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8524 {
8525   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8526   DMPlexInterpolatedFlag interpEnum;
8527 
8528   PetscFunctionBegin;
8529   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8530   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8531   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8532   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8533     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8534     PetscFunctionReturn(0);
8535   }
8536 
8537   PetscCall(DMGetDimension(dm, &dim));
8538   PetscCall(DMPlexGetDepth(dm, &depth));
8539   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8540   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8541     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8542     for (c = cStart; c < cEnd; ++c) {
8543       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8544       const DMPolytopeType *faceTypes;
8545       DMPolytopeType        ct;
8546       PetscInt              numFaces, coneSize, f;
8547       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8548 
8549       PetscCall(DMPlexGetCellType(dm, c, &ct));
8550       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8551       if (unsplit) continue;
8552       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8553       PetscCall(DMPlexGetCone(dm, c, &cone));
8554       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8555       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8556       for (cl = 0; cl < closureSize*2; cl += 2) {
8557         const PetscInt p = closure[cl];
8558         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8559       }
8560       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8561       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);
8562       for (f = 0; f < numFaces; ++f) {
8563         DMPolytopeType fct;
8564         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8565 
8566         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8567         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8568         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8569           const PetscInt p = fclosure[cl];
8570           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8571         }
8572         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]);
8573         for (v = 0; v < fnumCorners; ++v) {
8574           if (fclosure[v] != faces[fOff+v]) {
8575             PetscInt v1;
8576 
8577             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8578             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8579             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8580             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff+v1]));
8581             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8582             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]);
8583           }
8584         }
8585         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8586         fOff += faceSizes[f];
8587       }
8588       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8589       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8590     }
8591   }
8592   PetscFunctionReturn(0);
8593 }
8594 
8595 /*@
8596   DMPlexCheckGeometry - Check the geometry of mesh cells
8597 
8598   Input Parameter:
8599 . dm - The DMPlex object
8600 
8601   Notes:
8602   This is a useful diagnostic when creating meshes programmatically.
8603 
8604   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8605 
8606   Level: developer
8607 
8608 .seealso: `DMCreate()`, `DMSetFromOptions()`
8609 @*/
8610 PetscErrorCode DMPlexCheckGeometry(DM dm)
8611 {
8612   Vec       coordinates;
8613   PetscReal detJ, J[9], refVol = 1.0;
8614   PetscReal vol;
8615   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8616 
8617   PetscFunctionBegin;
8618   PetscCall(DMGetDimension(dm, &dim));
8619   PetscCall(DMGetCoordinateDim(dm, &dE));
8620   if (dim != dE) PetscFunctionReturn(0);
8621   PetscCall(DMPlexGetDepth(dm, &depth));
8622   for (d = 0; d < dim; ++d) refVol *= 2.0;
8623   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8624   /* Make sure local coordinates are created, because that step is collective */
8625   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8626   for (c = cStart; c < cEnd; ++c) {
8627     DMPolytopeType ct;
8628     PetscInt       unsplit;
8629     PetscBool      ignoreZeroVol = PETSC_FALSE;
8630 
8631     PetscCall(DMPlexGetCellType(dm, c, &ct));
8632     switch (ct) {
8633       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8634       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8635       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8636         ignoreZeroVol = PETSC_TRUE; break;
8637       default: break;
8638     }
8639     switch (ct) {
8640       case DM_POLYTOPE_TRI_PRISM:
8641       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8642       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8643       case DM_POLYTOPE_PYRAMID:
8644         continue;
8645       default: break;
8646     }
8647     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8648     if (unsplit) continue;
8649     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8650     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);
8651     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ*refVol)));
8652     /* This should work with periodicity since DG coordinates should be used */
8653     if (depth > 1) {
8654       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8655       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);
8656       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double) vol));
8657     }
8658   }
8659   PetscFunctionReturn(0);
8660 }
8661 
8662 /*@
8663   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8664 
8665   Collective
8666 
8667   Input Parameters:
8668 + dm - The DMPlex object
8669 - pointSF - The Point SF, or NULL for Point SF attached to DM
8670 
8671   Notes:
8672   This is mainly intended for debugging/testing purposes.
8673 
8674   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8675 
8676   Level: developer
8677 
8678 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8679 @*/
8680 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF)
8681 {
8682   PetscInt        l, nleaves, nroots, overlap;
8683   const PetscInt *locals;
8684   const PetscSFNode *remotes;
8685   PetscBool       distributed;
8686   MPI_Comm        comm;
8687   PetscMPIInt     rank;
8688 
8689   PetscFunctionBegin;
8690   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8691   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8692   else         pointSF = dm->sf;
8693   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8694   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8695   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8696   {
8697     PetscMPIInt    mpiFlag;
8698 
8699     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF),&mpiFlag));
8700     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)",mpiFlag);
8701   }
8702   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8703   PetscCall(DMPlexIsDistributed(dm, &distributed));
8704   if (!distributed) {
8705     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);
8706     PetscFunctionReturn(0);
8707   }
8708   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);
8709   PetscCall(DMPlexGetOverlap(dm, &overlap));
8710 
8711   /* Check SF graph is compatible with DMPlex chart */
8712   {
8713     PetscInt pStart, pEnd, maxLeaf;
8714 
8715     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8716     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8717     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd-pStart, nroots);
8718     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8719   }
8720 
8721   /* Check Point SF has no local points referenced */
8722   for (l = 0; l < nleaves; l++) {
8723     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);
8724   }
8725 
8726   /* Check there are no cells in interface */
8727   if (!overlap) {
8728     PetscInt cellHeight, cStart, cEnd;
8729 
8730     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8731     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8732     for (l = 0; l < nleaves; ++l) {
8733       const PetscInt point = locals ? locals[l] : l;
8734 
8735       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8736     }
8737   }
8738 
8739   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8740   {
8741     const PetscInt *rootdegree;
8742 
8743     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8744     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8745     for (l = 0; l < nleaves; ++l) {
8746       const PetscInt  point = locals ? locals[l] : l;
8747       const PetscInt *cone;
8748       PetscInt        coneSize, c, idx;
8749 
8750       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8751       PetscCall(DMPlexGetCone(dm, point, &cone));
8752       for (c = 0; c < coneSize; ++c) {
8753         if (!rootdegree[cone[c]]) {
8754           if (locals) {
8755             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8756           } else {
8757             idx = (cone[c] < nleaves) ? cone[c] : -1;
8758           }
8759           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8760         }
8761       }
8762     }
8763   }
8764   PetscFunctionReturn(0);
8765 }
8766 
8767 /*@
8768   DMPlexCheck - Perform various checks of Plex sanity
8769 
8770   Input Parameter:
8771 . dm - The DMPlex object
8772 
8773   Notes:
8774   This is a useful diagnostic when creating meshes programmatically.
8775 
8776   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8777 
8778   Currently does not include DMPlexCheckCellShape().
8779 
8780   Level: developer
8781 
8782 .seealso: DMCreate(), DMSetFromOptions()
8783 @*/
8784 PetscErrorCode DMPlexCheck(DM dm)
8785 {
8786   PetscInt cellHeight;
8787 
8788   PetscFunctionBegin;
8789   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8790   PetscCall(DMPlexCheckSymmetry(dm));
8791   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8792   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8793   PetscCall(DMPlexCheckGeometry(dm));
8794   PetscCall(DMPlexCheckPointSF(dm, NULL));
8795   PetscCall(DMPlexCheckInterfaceCones(dm));
8796   PetscFunctionReturn(0);
8797 }
8798 
8799 typedef struct cell_stats
8800 {
8801   PetscReal min, max, sum, squaresum;
8802   PetscInt  count;
8803 } cell_stats_t;
8804 
8805 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8806 {
8807   PetscInt i, N = *len;
8808 
8809   for (i = 0; i < N; i++) {
8810     cell_stats_t *A = (cell_stats_t *) a;
8811     cell_stats_t *B = (cell_stats_t *) b;
8812 
8813     B->min = PetscMin(A->min,B->min);
8814     B->max = PetscMax(A->max,B->max);
8815     B->sum += A->sum;
8816     B->squaresum += A->squaresum;
8817     B->count += A->count;
8818   }
8819 }
8820 
8821 /*@
8822   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8823 
8824   Collective on dm
8825 
8826   Input Parameters:
8827 + dm        - The DMPlex object
8828 . output    - If true, statistics will be displayed on stdout
8829 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8830 
8831   Notes:
8832   This is mainly intended for debugging/testing purposes.
8833 
8834   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8835 
8836   Level: developer
8837 
8838 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8839 @*/
8840 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8841 {
8842   DM             dmCoarse;
8843   cell_stats_t   stats, globalStats;
8844   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8845   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8846   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8847   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8848   PetscMPIInt    rank,size;
8849 
8850   PetscFunctionBegin;
8851   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8852   stats.min   = PETSC_MAX_REAL;
8853   stats.max   = PETSC_MIN_REAL;
8854   stats.sum   = stats.squaresum = 0.;
8855   stats.count = 0;
8856 
8857   PetscCallMPI(MPI_Comm_size(comm, &size));
8858   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8859   PetscCall(DMGetCoordinateDim(dm,&cdim));
8860   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8861   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8862   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8863   for (c = cStart; c < cEnd; c++) {
8864     PetscInt  i;
8865     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8866 
8867     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8868     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8869     for (i = 0; i < PetscSqr(cdim); ++i) {
8870       frobJ    += J[i] * J[i];
8871       frobInvJ += invJ[i] * invJ[i];
8872     }
8873     cond2 = frobJ * frobInvJ;
8874     cond  = PetscSqrtReal(cond2);
8875 
8876     stats.min        = PetscMin(stats.min,cond);
8877     stats.max        = PetscMax(stats.max,cond);
8878     stats.sum       += cond;
8879     stats.squaresum += cond2;
8880     stats.count++;
8881     if (output && cond > limit) {
8882       PetscSection coordSection;
8883       Vec          coordsLocal;
8884       PetscScalar *coords = NULL;
8885       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8886 
8887       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8888       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8889       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8890       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double) cond));
8891       for (i = 0; i < Nv/cdim; ++i) {
8892         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8893         for (d = 0; d < cdim; ++d) {
8894           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8895           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8896         }
8897         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8898       }
8899       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8900       for (cl = 0; cl < clSize*2; cl += 2) {
8901         const PetscInt edge = closure[cl];
8902 
8903         if ((edge >= eStart) && (edge < eEnd)) {
8904           PetscReal len;
8905 
8906           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8907           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double) len));
8908         }
8909       }
8910       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8911       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8912     }
8913   }
8914   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8915 
8916   if (size > 1) {
8917     PetscMPIInt   blockLengths[2] = {4,1};
8918     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8919     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8920     MPI_Op        statReduce;
8921 
8922     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8923     PetscCallMPI(MPI_Type_commit(&statType));
8924     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8925     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8926     PetscCallMPI(MPI_Op_free(&statReduce));
8927     PetscCallMPI(MPI_Type_free(&statType));
8928   } else {
8929     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8930   }
8931   if (rank == 0) {
8932     count = globalStats.count;
8933     min   = globalStats.min;
8934     max   = globalStats.max;
8935     mean  = globalStats.sum / globalStats.count;
8936     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8937   }
8938 
8939   if (output) {
8940     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));
8941   }
8942   PetscCall(PetscFree2(J,invJ));
8943 
8944   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8945   if (dmCoarse) {
8946     PetscBool isplex;
8947 
8948     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8949     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8950   }
8951   PetscFunctionReturn(0);
8952 }
8953 
8954 /*@
8955   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8956   orthogonal quality below given tolerance.
8957 
8958   Collective on dm
8959 
8960   Input Parameters:
8961 + dm   - The DMPlex object
8962 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8963 - atol - [0, 1] Absolute tolerance for tagging cells.
8964 
8965   Output Parameters:
8966 + OrthQual      - Vec containing orthogonal quality per cell
8967 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8968 
8969   Options Database Keys:
8970 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8971 supported.
8972 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8973 
8974   Notes:
8975   Orthogonal quality is given by the following formula:
8976 
8977   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8978 
8979   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
8980   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8981   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8982   calculating the cosine of the angle between these vectors.
8983 
8984   Orthogonal quality ranges from 1 (best) to 0 (worst).
8985 
8986   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8987   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8988 
8989   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8990 
8991   Level: intermediate
8992 
8993 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8994 @*/
8995 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8996 {
8997   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8998   PetscInt                *idx;
8999   PetscScalar             *oqVals;
9000   const PetscScalar       *cellGeomArr, *faceGeomArr;
9001   PetscReal               *ci, *fi, *Ai;
9002   MPI_Comm                comm;
9003   Vec                     cellgeom, facegeom;
9004   DM                      dmFace, dmCell;
9005   IS                      glob;
9006   ISLocalToGlobalMapping  ltog;
9007   PetscViewer             vwr;
9008 
9009   PetscFunctionBegin;
9010   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9011   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
9012   PetscValidPointer(OrthQual, 4);
9013   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
9014   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
9015   PetscCall(DMGetDimension(dm, &nc));
9016   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9017   {
9018     DMPlexInterpolatedFlag interpFlag;
9019 
9020     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9021     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9022       PetscMPIInt rank;
9023 
9024       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9025       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9026     }
9027   }
9028   if (OrthQualLabel) {
9029     PetscValidPointer(OrthQualLabel, 5);
9030     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9031     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9032   } else {*OrthQualLabel = NULL;}
9033   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9034   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9035   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9036   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9037   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9038   PetscCall(VecCreate(comm, OrthQual));
9039   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9040   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
9041   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9042   PetscCall(VecSetUp(*OrthQual));
9043   PetscCall(ISDestroy(&glob));
9044   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9045   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9046   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9047   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9048   PetscCall(VecGetDM(cellgeom, &dmCell));
9049   PetscCall(VecGetDM(facegeom, &dmFace));
9050   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9051   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
9052     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9053     PetscInt           cellarr[2], *adj = NULL;
9054     PetscScalar        *cArr, *fArr;
9055     PetscReal          minvalc = 1.0, minvalf = 1.0;
9056     PetscFVCellGeom    *cg;
9057 
9058     idx[cellIter] = cell-cStart;
9059     cellarr[0] = cell;
9060     /* Make indexing into cellGeom easier */
9061     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9062     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9063     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9064     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9065     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
9066       PetscInt         i;
9067       const PetscInt   neigh = adj[cellneigh];
9068       PetscReal        normci = 0, normfi = 0, normai = 0;
9069       PetscFVCellGeom  *cgneigh;
9070       PetscFVFaceGeom  *fg;
9071 
9072       /* Don't count ourselves in the neighbor list */
9073       if (neigh == cell) continue;
9074       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9075       cellarr[1] = neigh;
9076       {
9077         PetscInt       numcovpts;
9078         const PetscInt *covpts;
9079 
9080         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9081         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9082         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9083       }
9084 
9085       /* Compute c_i, f_i and their norms */
9086       for (i = 0; i < nc; i++) {
9087         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9088         fi[i] = fg->centroid[i] - cg->centroid[i];
9089         Ai[i] = fg->normal[i];
9090         normci += PetscPowReal(ci[i], 2);
9091         normfi += PetscPowReal(fi[i], 2);
9092         normai += PetscPowReal(Ai[i], 2);
9093       }
9094       normci = PetscSqrtReal(normci);
9095       normfi = PetscSqrtReal(normfi);
9096       normai = PetscSqrtReal(normai);
9097 
9098       /* Normalize and compute for each face-cell-normal pair */
9099       for (i = 0; i < nc; i++) {
9100         ci[i] = ci[i]/normci;
9101         fi[i] = fi[i]/normfi;
9102         Ai[i] = Ai[i]/normai;
9103         /* PetscAbs because I don't know if normals are guaranteed to point out */
9104         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9105         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9106       }
9107       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9108         minvalc = PetscRealPart(cArr[cellneighiter]);
9109       }
9110       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9111         minvalf = PetscRealPart(fArr[cellneighiter]);
9112       }
9113     }
9114     PetscCall(PetscFree(adj));
9115     PetscCall(PetscFree2(cArr, fArr));
9116     /* Defer to cell if they're equal */
9117     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9118     if (OrthQualLabel) {
9119       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9120     }
9121   }
9122   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9123   PetscCall(VecAssemblyBegin(*OrthQual));
9124   PetscCall(VecAssemblyEnd(*OrthQual));
9125   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9126   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9127   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9128   if (OrthQualLabel) {
9129     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9130   }
9131   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9132   PetscCall(PetscViewerDestroy(&vwr));
9133   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9134   PetscFunctionReturn(0);
9135 }
9136 
9137 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9138  * interpolator construction */
9139 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9140 {
9141   PetscSection   section, newSection, gsection;
9142   PetscSF        sf;
9143   PetscBool      hasConstraints, ghasConstraints;
9144 
9145   PetscFunctionBegin;
9146   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9147   PetscValidPointer(odm,2);
9148   PetscCall(DMGetLocalSection(dm, &section));
9149   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9150   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9151   if (!ghasConstraints) {
9152     PetscCall(PetscObjectReference((PetscObject)dm));
9153     *odm = dm;
9154     PetscFunctionReturn(0);
9155   }
9156   PetscCall(DMClone(dm, odm));
9157   PetscCall(DMCopyFields(dm, *odm));
9158   PetscCall(DMGetLocalSection(*odm, &newSection));
9159   PetscCall(DMGetPointSF(*odm, &sf));
9160   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9161   PetscCall(DMSetGlobalSection(*odm, gsection));
9162   PetscCall(PetscSectionDestroy(&gsection));
9163   PetscFunctionReturn(0);
9164 }
9165 
9166 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9167 {
9168   DM             dmco, dmfo;
9169   Mat            interpo;
9170   Vec            rscale;
9171   Vec            cglobalo, clocal;
9172   Vec            fglobal, fglobalo, flocal;
9173   PetscBool      regular;
9174 
9175   PetscFunctionBegin;
9176   PetscCall(DMGetFullDM(dmc, &dmco));
9177   PetscCall(DMGetFullDM(dmf, &dmfo));
9178   PetscCall(DMSetCoarseDM(dmfo, dmco));
9179   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9180   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9181   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9182   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9183   PetscCall(DMCreateLocalVector(dmc, &clocal));
9184   PetscCall(VecSet(cglobalo, 0.));
9185   PetscCall(VecSet(clocal, 0.));
9186   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9187   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9188   PetscCall(DMCreateLocalVector(dmf, &flocal));
9189   PetscCall(VecSet(fglobal, 0.));
9190   PetscCall(VecSet(fglobalo, 0.));
9191   PetscCall(VecSet(flocal, 0.));
9192   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9193   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9194   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9195   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9196   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9197   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9198   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9199   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9200   *shift = fglobal;
9201   PetscCall(VecDestroy(&flocal));
9202   PetscCall(VecDestroy(&fglobalo));
9203   PetscCall(VecDestroy(&clocal));
9204   PetscCall(VecDestroy(&cglobalo));
9205   PetscCall(VecDestroy(&rscale));
9206   PetscCall(MatDestroy(&interpo));
9207   PetscCall(DMDestroy(&dmfo));
9208   PetscCall(DMDestroy(&dmco));
9209   PetscFunctionReturn(0);
9210 }
9211 
9212 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9213 {
9214   PetscObject    shifto;
9215   Vec            shift;
9216 
9217   PetscFunctionBegin;
9218   if (!interp) {
9219     Vec rscale;
9220 
9221     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9222     PetscCall(VecDestroy(&rscale));
9223   } else {
9224     PetscCall(PetscObjectReference((PetscObject)interp));
9225   }
9226   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9227   if (!shifto) {
9228     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9229     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9230     shifto = (PetscObject) shift;
9231     PetscCall(VecDestroy(&shift));
9232   }
9233   shift = (Vec) shifto;
9234   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9235   PetscCall(VecAXPY(fineSol, 1.0, shift));
9236   PetscCall(MatDestroy(&interp));
9237   PetscFunctionReturn(0);
9238 }
9239 
9240 /* Pointwise interpolation
9241      Just code FEM for now
9242      u^f = I u^c
9243      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9244      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9245      I_{ij} = psi^f_i phi^c_j
9246 */
9247 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9248 {
9249   PetscSection   gsc, gsf;
9250   PetscInt       m, n;
9251   void          *ctx;
9252   DM             cdm;
9253   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9254 
9255   PetscFunctionBegin;
9256   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9257   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9258   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9259   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9260 
9261   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9262   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9263   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9264   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9265   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9266 
9267   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9268   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9269   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9270   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9271   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9272   if (scaling) {
9273     /* Use naive scaling */
9274     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9275   }
9276   PetscFunctionReturn(0);
9277 }
9278 
9279 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9280 {
9281   VecScatter     ctx;
9282 
9283   PetscFunctionBegin;
9284   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9285   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9286   PetscCall(VecScatterDestroy(&ctx));
9287   PetscFunctionReturn(0);
9288 }
9289 
9290 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9291                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9292                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9293                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9294 {
9295   const PetscInt Nc = uOff[1] - uOff[0];
9296   PetscInt       c;
9297   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9298 }
9299 
9300 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9301 {
9302   DM             dmc;
9303   PetscDS        ds;
9304   Vec            ones, locmass;
9305   IS             cellIS;
9306   PetscFormKey   key;
9307   PetscInt       depth;
9308 
9309   PetscFunctionBegin;
9310   PetscCall(DMClone(dm, &dmc));
9311   PetscCall(DMCopyDisc(dm, dmc));
9312   PetscCall(DMGetDS(dmc, &ds));
9313   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9314   PetscCall(DMCreateGlobalVector(dmc, mass));
9315   PetscCall(DMGetLocalVector(dmc, &ones));
9316   PetscCall(DMGetLocalVector(dmc, &locmass));
9317   PetscCall(DMPlexGetDepth(dmc, &depth));
9318   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9319   PetscCall(VecSet(locmass, 0.0));
9320   PetscCall(VecSet(ones, 1.0));
9321   key.label = NULL;
9322   key.value = 0;
9323   key.field = 0;
9324   key.part  = 0;
9325   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9326   PetscCall(ISDestroy(&cellIS));
9327   PetscCall(VecSet(*mass, 0.0));
9328   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9329   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9330   PetscCall(DMRestoreLocalVector(dmc, &ones));
9331   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9332   PetscCall(DMDestroy(&dmc));
9333   PetscFunctionReturn(0);
9334 }
9335 
9336 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9337 {
9338   PetscSection   gsc, gsf;
9339   PetscInt       m, n;
9340   void          *ctx;
9341   DM             cdm;
9342   PetscBool      regular;
9343 
9344   PetscFunctionBegin;
9345   if (dmFine == dmCoarse) {
9346     DM            dmc;
9347     PetscDS       ds;
9348     PetscWeakForm wf;
9349     Vec           u;
9350     IS            cellIS;
9351     PetscFormKey  key;
9352     PetscInt      depth;
9353 
9354     PetscCall(DMClone(dmFine, &dmc));
9355     PetscCall(DMCopyDisc(dmFine, dmc));
9356     PetscCall(DMGetDS(dmc, &ds));
9357     PetscCall(PetscDSGetWeakForm(ds, &wf));
9358     PetscCall(PetscWeakFormClear(wf));
9359     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9360     PetscCall(DMCreateMatrix(dmc, mass));
9361     PetscCall(DMGetLocalVector(dmc, &u));
9362     PetscCall(DMPlexGetDepth(dmc, &depth));
9363     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9364     PetscCall(MatZeroEntries(*mass));
9365     key.label = NULL;
9366     key.value = 0;
9367     key.field = 0;
9368     key.part  = 0;
9369     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9370     PetscCall(ISDestroy(&cellIS));
9371     PetscCall(DMRestoreLocalVector(dmc, &u));
9372     PetscCall(DMDestroy(&dmc));
9373   } else {
9374     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9375     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9376     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9377     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9378 
9379     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9380     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9381     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9382     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9383 
9384     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9385     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9386     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9387     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9388   }
9389   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9390   PetscFunctionReturn(0);
9391 }
9392 
9393 /*@
9394   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9395 
9396   Input Parameter:
9397 . dm - The DMPlex object
9398 
9399   Output Parameter:
9400 . regular - The flag
9401 
9402   Level: intermediate
9403 
9404 .seealso: `DMPlexSetRegularRefinement()`
9405 @*/
9406 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9407 {
9408   PetscFunctionBegin;
9409   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9410   PetscValidBoolPointer(regular, 2);
9411   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9412   PetscFunctionReturn(0);
9413 }
9414 
9415 /*@
9416   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9417 
9418   Input Parameters:
9419 + dm - The DMPlex object
9420 - regular - The flag
9421 
9422   Level: intermediate
9423 
9424 .seealso: `DMPlexGetRegularRefinement()`
9425 @*/
9426 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9427 {
9428   PetscFunctionBegin;
9429   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9430   ((DM_Plex *) dm->data)->regularRefinement = regular;
9431   PetscFunctionReturn(0);
9432 }
9433 
9434 /* anchors */
9435 /*@
9436   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9437   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9438 
9439   not collective
9440 
9441   Input Parameter:
9442 . dm - The DMPlex object
9443 
9444   Output Parameters:
9445 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9446 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9447 
9448   Level: intermediate
9449 
9450 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9451 @*/
9452 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9453 {
9454   DM_Plex *plex = (DM_Plex *)dm->data;
9455 
9456   PetscFunctionBegin;
9457   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9458   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9459   if (anchorSection) *anchorSection = plex->anchorSection;
9460   if (anchorIS) *anchorIS = plex->anchorIS;
9461   PetscFunctionReturn(0);
9462 }
9463 
9464 /*@
9465   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9466   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9467   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9468 
9469   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9470   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9471 
9472   collective on dm
9473 
9474   Input Parameters:
9475 + dm - The DMPlex object
9476 . 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).
9477 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9478 
9479   The reference counts of anchorSection and anchorIS are incremented.
9480 
9481   Level: intermediate
9482 
9483 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9484 @*/
9485 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9486 {
9487   DM_Plex        *plex = (DM_Plex *)dm->data;
9488   PetscMPIInt    result;
9489 
9490   PetscFunctionBegin;
9491   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9492   if (anchorSection) {
9493     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9494     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9495     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9496   }
9497   if (anchorIS) {
9498     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9499     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9500     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9501   }
9502 
9503   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9504   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9505   plex->anchorSection = anchorSection;
9506 
9507   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9508   PetscCall(ISDestroy(&plex->anchorIS));
9509   plex->anchorIS = anchorIS;
9510 
9511   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9512     PetscInt size, a, pStart, pEnd;
9513     const PetscInt *anchors;
9514 
9515     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9516     PetscCall(ISGetLocalSize(anchorIS,&size));
9517     PetscCall(ISGetIndices(anchorIS,&anchors));
9518     for (a = 0; a < size; a++) {
9519       PetscInt p;
9520 
9521       p = anchors[a];
9522       if (p >= pStart && p < pEnd) {
9523         PetscInt dof;
9524 
9525         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9526         if (dof) {
9527 
9528           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9529           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %" PetscInt_FMT " cannot be constrained and an anchor",p);
9530         }
9531       }
9532     }
9533     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9534   }
9535   /* reset the generic constraints */
9536   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9537   PetscFunctionReturn(0);
9538 }
9539 
9540 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9541 {
9542   PetscSection anchorSection;
9543   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9544 
9545   PetscFunctionBegin;
9546   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9547   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9548   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9549   PetscCall(PetscSectionGetNumFields(section,&numFields));
9550   if (numFields) {
9551     PetscInt f;
9552     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9553 
9554     for (f = 0; f < numFields; f++) {
9555       PetscInt numComp;
9556 
9557       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9558       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9559     }
9560   }
9561   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9562   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9563   pStart = PetscMax(pStart,sStart);
9564   pEnd   = PetscMin(pEnd,sEnd);
9565   pEnd   = PetscMax(pStart,pEnd);
9566   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9567   for (p = pStart; p < pEnd; p++) {
9568     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9569     if (dof) {
9570       PetscCall(PetscSectionGetDof(section,p,&dof));
9571       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9572       for (f = 0; f < numFields; f++) {
9573         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9574         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9575       }
9576     }
9577   }
9578   PetscCall(PetscSectionSetUp(*cSec));
9579   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9580   PetscFunctionReturn(0);
9581 }
9582 
9583 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9584 {
9585   PetscSection   aSec;
9586   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9587   const PetscInt *anchors;
9588   PetscInt       numFields, f;
9589   IS             aIS;
9590   MatType        mtype;
9591   PetscBool      iscuda,iskokkos;
9592 
9593   PetscFunctionBegin;
9594   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9595   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9596   PetscCall(PetscSectionGetStorageSize(section, &n));
9597   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9598   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9599   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9600   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9601   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9602   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9603   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9604   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9605   else mtype = MATSEQAIJ;
9606   PetscCall(MatSetType(*cMat,mtype));
9607   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9608   PetscCall(ISGetIndices(aIS,&anchors));
9609   /* cSec will be a subset of aSec and section */
9610   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9611   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9612   PetscCall(PetscMalloc1(m+1,&i));
9613   i[0] = 0;
9614   PetscCall(PetscSectionGetNumFields(section,&numFields));
9615   for (p = pStart; p < pEnd; p++) {
9616     PetscInt rDof, rOff, r;
9617 
9618     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9619     if (!rDof) continue;
9620     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9621     if (numFields) {
9622       for (f = 0; f < numFields; f++) {
9623         annz = 0;
9624         for (r = 0; r < rDof; r++) {
9625           a = anchors[rOff + r];
9626           if (a < sStart || a >= sEnd) continue;
9627           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9628           annz += aDof;
9629         }
9630         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9631         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9632         for (q = 0; q < dof; q++) {
9633           i[off + q + 1] = i[off + q] + annz;
9634         }
9635       }
9636     } else {
9637       annz = 0;
9638       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9639       for (q = 0; q < dof; q++) {
9640         a = anchors[rOff + q];
9641         if (a < sStart || a >= sEnd) continue;
9642         PetscCall(PetscSectionGetDof(section,a,&aDof));
9643         annz += aDof;
9644       }
9645       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9646       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9647       for (q = 0; q < dof; q++) {
9648         i[off + q + 1] = i[off + q] + annz;
9649       }
9650     }
9651   }
9652   nnz = i[m];
9653   PetscCall(PetscMalloc1(nnz,&j));
9654   offset = 0;
9655   for (p = pStart; p < pEnd; p++) {
9656     if (numFields) {
9657       for (f = 0; f < numFields; f++) {
9658         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9659         for (q = 0; q < dof; q++) {
9660           PetscInt rDof, rOff, r;
9661           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9662           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9663           for (r = 0; r < rDof; r++) {
9664             PetscInt s;
9665 
9666             a = anchors[rOff + r];
9667             if (a < sStart || a >= sEnd) continue;
9668             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9669             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9670             for (s = 0; s < aDof; s++) {
9671               j[offset++] = aOff + s;
9672             }
9673           }
9674         }
9675       }
9676     } else {
9677       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9678       for (q = 0; q < dof; q++) {
9679         PetscInt rDof, rOff, r;
9680         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9681         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9682         for (r = 0; r < rDof; r++) {
9683           PetscInt s;
9684 
9685           a = anchors[rOff + r];
9686           if (a < sStart || a >= sEnd) continue;
9687           PetscCall(PetscSectionGetDof(section,a,&aDof));
9688           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9689           for (s = 0; s < aDof; s++) {
9690             j[offset++] = aOff + s;
9691           }
9692         }
9693       }
9694     }
9695   }
9696   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9697   PetscCall(PetscFree(i));
9698   PetscCall(PetscFree(j));
9699   PetscCall(ISRestoreIndices(aIS,&anchors));
9700   PetscFunctionReturn(0);
9701 }
9702 
9703 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9704 {
9705   DM_Plex        *plex = (DM_Plex *)dm->data;
9706   PetscSection   anchorSection, section, cSec;
9707   Mat            cMat;
9708 
9709   PetscFunctionBegin;
9710   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9711   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9712   if (anchorSection) {
9713     PetscInt Nf;
9714 
9715     PetscCall(DMGetLocalSection(dm,&section));
9716     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9717     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9718     PetscCall(DMGetNumFields(dm,&Nf));
9719     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9720     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9721     PetscCall(PetscSectionDestroy(&cSec));
9722     PetscCall(MatDestroy(&cMat));
9723   }
9724   PetscFunctionReturn(0);
9725 }
9726 
9727 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9728 {
9729   IS             subis;
9730   PetscSection   section, subsection;
9731 
9732   PetscFunctionBegin;
9733   PetscCall(DMGetLocalSection(dm, &section));
9734   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9735   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9736   /* Create subdomain */
9737   PetscCall(DMPlexFilter(dm, label, value, subdm));
9738   /* Create submodel */
9739   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9740   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9741   PetscCall(DMSetLocalSection(*subdm, subsection));
9742   PetscCall(PetscSectionDestroy(&subsection));
9743   PetscCall(DMCopyDisc(dm, *subdm));
9744   /* Create map from submodel to global model */
9745   if (is) {
9746     PetscSection    sectionGlobal, subsectionGlobal;
9747     IS              spIS;
9748     const PetscInt *spmap;
9749     PetscInt       *subIndices;
9750     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9751     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9752 
9753     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9754     PetscCall(ISGetIndices(spIS, &spmap));
9755     PetscCall(PetscSectionGetNumFields(section, &Nf));
9756     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9757     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9758     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9759     for (p = pStart; p < pEnd; ++p) {
9760       PetscInt gdof, pSubSize  = 0;
9761 
9762       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9763       if (gdof > 0) {
9764         for (f = 0; f < Nf; ++f) {
9765           PetscInt fdof, fcdof;
9766 
9767           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9768           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9769           pSubSize += fdof-fcdof;
9770         }
9771         subSize += pSubSize;
9772         if (pSubSize) {
9773           if (bs < 0) {
9774             bs = pSubSize;
9775           } else if (bs != pSubSize) {
9776             /* Layout does not admit a pointwise block size */
9777             bs = 1;
9778           }
9779         }
9780       }
9781     }
9782     /* Must have same blocksize on all procs (some might have no points) */
9783     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9784     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9785     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9786     else                            {bs = bsMinMax[0];}
9787     PetscCall(PetscMalloc1(subSize, &subIndices));
9788     for (p = pStart; p < pEnd; ++p) {
9789       PetscInt gdof, goff;
9790 
9791       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9792       if (gdof > 0) {
9793         const PetscInt point = spmap[p];
9794 
9795         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9796         for (f = 0; f < Nf; ++f) {
9797           PetscInt fdof, fcdof, fc, f2, poff = 0;
9798 
9799           /* Can get rid of this loop by storing field information in the global section */
9800           for (f2 = 0; f2 < f; ++f2) {
9801             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9802             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9803             poff += fdof-fcdof;
9804           }
9805           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9806           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9807           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9808             subIndices[subOff] = goff+poff+fc;
9809           }
9810         }
9811       }
9812     }
9813     PetscCall(ISRestoreIndices(spIS, &spmap));
9814     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9815     if (bs > 1) {
9816       /* We need to check that the block size does not come from non-contiguous fields */
9817       PetscInt i, j, set = 1;
9818       for (i = 0; i < subSize; i += bs) {
9819         for (j = 0; j < bs; ++j) {
9820           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9821         }
9822       }
9823       if (set) PetscCall(ISSetBlockSize(*is, bs));
9824     }
9825     /* Attach nullspace */
9826     for (f = 0; f < Nf; ++f) {
9827       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9828       if ((*subdm)->nullspaceConstructors[f]) break;
9829     }
9830     if (f < Nf) {
9831       MatNullSpace nullSpace;
9832       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9833 
9834       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9835       PetscCall(MatNullSpaceDestroy(&nullSpace));
9836     }
9837   }
9838   PetscFunctionReturn(0);
9839 }
9840 
9841 /*@
9842   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9843 
9844   Input Parameter:
9845 - dm - The DM
9846 
9847   Level: developer
9848 
9849   Options Database Keys:
9850 . -dm_plex_monitor_throughput - Activate the monitor
9851 
9852 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9853 @*/
9854 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9855 {
9856 #if defined(PETSC_USE_LOG)
9857   PetscStageLog      stageLog;
9858   PetscLogEvent      event;
9859   PetscLogStage      stage;
9860   PetscEventPerfInfo eventInfo;
9861   PetscReal          cellRate, flopRate;
9862   PetscInt           cStart, cEnd, Nf, N;
9863   const char        *name;
9864 #endif
9865 
9866   PetscFunctionBegin;
9867   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9868 #if defined(PETSC_USE_LOG)
9869   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9870   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9871   PetscCall(DMGetNumFields(dm, &Nf));
9872   PetscCall(PetscLogGetStageLog(&stageLog));
9873   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9874   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9875   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9876   N        = (cEnd - cStart)*Nf*eventInfo.count;
9877   flopRate = eventInfo.flops/eventInfo.time;
9878   cellRate = N/eventInfo.time;
9879   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)));
9880 #else
9881   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9882 #endif
9883   PetscFunctionReturn(0);
9884 }
9885