xref: /petsc/src/dm/impls/plex/plex.c (revision 2e956fe4fc852fabc23b437482e1fb7b77fddb0d)
1 #include <petsc/private/dmpleximpl.h>   /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/isimpl.h>
3 #include <petsc/private/vecimpl.h>
4 #include <petsc/private/glvisvecimpl.h>
5 #include <petscsf.h>
6 #include <petscds.h>
7 #include <petscdraw.h>
8 #include <petscdmfield.h>
9 #include <petscdmplextransform.h>
10 
11 /* Logging support */
12 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF,DMPLEX_LocatePoints,DMPLEX_TopologyView,DMPLEX_LabelsView,DMPLEX_CoordinatesView,DMPLEX_SectionView,DMPLEX_GlobalVectorView,DMPLEX_LocalVectorView,DMPLEX_TopologyLoad,DMPLEX_LabelsLoad,DMPLEX_CoordinatesLoad,DMPLEX_SectionLoad,DMPLEX_GlobalVectorLoad,DMPLEX_LocalVectorLoad;
13 
14 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
15 
16 /*@
17   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
18 
19   Input Parameter:
20 . dm      - The DMPlex object
21 
22   Output Parameter:
23 . simplex - Flag checking for a simplex
24 
25   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
26   If the mesh has no cells, this returns PETSC_FALSE.
27 
28   Level: intermediate
29 
30 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
31 @*/
32 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
33 {
34   DMPolytopeType ct;
35   PetscInt       cStart, cEnd;
36 
37   PetscFunctionBegin;
38   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
39   if (cEnd <= cStart) {*simplex = PETSC_FALSE; PetscFunctionReturn(0);}
40   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
41   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
42   PetscFunctionReturn(0);
43 }
44 
45 /*@
46   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
47 
48   Input Parameters:
49 + dm     - The DMPlex object
50 - height - The cell height in the Plex, 0 is the default
51 
52   Output Parameters:
53 + cStart - The first "normal" cell
54 - cEnd   - The upper bound on "normal"" cells
55 
56   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
57 
58   Level: developer
59 
60 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
61 @*/
62 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
63 {
64   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
65   PetscInt       cS, cE, c;
66 
67   PetscFunctionBegin;
68   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
69   for (c = cS; c < cE; ++c) {
70     DMPolytopeType cct;
71 
72     PetscCall(DMPlexGetCellType(dm, c, &cct));
73     if ((PetscInt) cct < 0) break;
74     switch (cct) {
75       case DM_POLYTOPE_POINT:
76       case DM_POLYTOPE_SEGMENT:
77       case DM_POLYTOPE_TRIANGLE:
78       case DM_POLYTOPE_QUADRILATERAL:
79       case DM_POLYTOPE_TETRAHEDRON:
80       case DM_POLYTOPE_HEXAHEDRON:
81         ct = cct;
82         break;
83       default: break;
84     }
85     if (ct != DM_POLYTOPE_UNKNOWN) break;
86   }
87   if (ct != DM_POLYTOPE_UNKNOWN) {
88     DMLabel ctLabel;
89 
90     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
91     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
92   }
93   if (cStart) *cStart = cS;
94   if (cEnd)   *cEnd   = cE;
95   PetscFunctionReturn(0);
96 }
97 
98 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
99 {
100   PetscInt       cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
101   PetscInt       vcdof[2] = {0,0}, globalvcdof[2];
102 
103   PetscFunctionBegin;
104   *ft  = PETSC_VTK_INVALID;
105   PetscCall(DMGetCoordinateDim(dm, &cdim));
106   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
107   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
108   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
109   if (field >= 0) {
110     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
111     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
112   } else {
113     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
114     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
115   }
116   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
117   if (globalvcdof[0]) {
118     *sStart = vStart;
119     *sEnd   = vEnd;
120     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
121     else                        *ft = PETSC_VTK_POINT_FIELD;
122   } else if (globalvcdof[1]) {
123     *sStart = cStart;
124     *sEnd   = cEnd;
125     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
126     else                        *ft = PETSC_VTK_CELL_FIELD;
127   } else {
128     if (field >= 0) {
129       const char *fieldname;
130 
131       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
132       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
133     } else {
134       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section\n"));
135     }
136   }
137   PetscFunctionReturn(0);
138 }
139 
140 /*@
141   DMPlexVecView1D - Plot many 1D solutions on the same line graph
142 
143   Collective on dm
144 
145   Input Parameters:
146 + dm - The DMPlex
147 . n  - The number of vectors
148 . u  - The array of local vectors
149 - viewer - The Draw viewer
150 
151   Level: advanced
152 
153 .seealso: `VecViewFromOptions()`, `VecView()`
154 @*/
155 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
156 {
157   PetscDS            ds;
158   PetscDraw          draw = NULL;
159   PetscDrawLG        lg;
160   Vec                coordinates;
161   const PetscScalar *coords, **sol;
162   PetscReal         *vals;
163   PetscInt          *Nc;
164   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
165   char             **names;
166 
167   PetscFunctionBegin;
168   PetscCall(DMGetDS(dm, &ds));
169   PetscCall(PetscDSGetNumFields(ds, &Nf));
170   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
171   PetscCall(PetscDSGetComponents(ds, &Nc));
172 
173   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
174   if (!draw) PetscFunctionReturn(0);
175   PetscCall(PetscDrawLGCreate(draw, n*Nl, &lg));
176 
177   PetscCall(PetscMalloc3(n, &sol, n*Nl, &names, n*Nl, &vals));
178   for (i = 0, l = 0; i < n; ++i) {
179     const char *vname;
180 
181     PetscCall(PetscObjectGetName((PetscObject) u[i], &vname));
182     for (f = 0; f < Nf; ++f) {
183       PetscObject disc;
184       const char *fname;
185       char        tmpname[PETSC_MAX_PATH_LEN];
186 
187       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
188       /* TODO Create names for components */
189       for (c = 0; c < Nc[f]; ++c, ++l) {
190         PetscCall(PetscObjectGetName(disc, &fname));
191         PetscCall(PetscStrcpy(tmpname, vname));
192         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
193         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
194         PetscCall(PetscStrallocpy(tmpname, &names[l]));
195       }
196     }
197   }
198   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *) names));
199   /* Just add P_1 support for now */
200   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
201   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
202   PetscCall(VecGetArrayRead(coordinates, &coords));
203   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
204   for (v = vStart; v < vEnd; ++v) {
205     PetscScalar *x, *svals;
206 
207     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
208     for (i = 0; i < n; ++i) {
209       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
210       for (l = 0; l < Nl; ++l) vals[i*Nl + l] = PetscRealPart(svals[l]);
211     }
212     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
213   }
214   PetscCall(VecRestoreArrayRead(coordinates, &coords));
215   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
216   for (l = 0; l < n*Nl; ++l) PetscCall(PetscFree(names[l]));
217   PetscCall(PetscFree3(sol, names, vals));
218 
219   PetscCall(PetscDrawLGDraw(lg));
220   PetscCall(PetscDrawLGDestroy(&lg));
221   PetscFunctionReturn(0);
222 }
223 
224 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
225 {
226   DM             dm;
227 
228   PetscFunctionBegin;
229   PetscCall(VecGetDM(u, &dm));
230   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
231   PetscFunctionReturn(0);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
235 {
236   DM                 dm;
237   PetscSection       s;
238   PetscDraw          draw, popup;
239   DM                 cdm;
240   PetscSection       coordSection;
241   Vec                coordinates;
242   const PetscScalar *coords, *array;
243   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
244   PetscReal          vbound[2], time;
245   PetscBool          flg;
246   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
247   const char        *name;
248   char               title[PETSC_MAX_PATH_LEN];
249 
250   PetscFunctionBegin;
251   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
252   PetscCall(VecGetDM(v, &dm));
253   PetscCall(DMGetCoordinateDim(dm, &dim));
254   PetscCall(DMGetLocalSection(dm, &s));
255   PetscCall(PetscSectionGetNumFields(s, &Nf));
256   PetscCall(DMGetCoarsenLevel(dm, &level));
257   PetscCall(DMGetCoordinateDM(dm, &cdm));
258   PetscCall(DMGetLocalSection(cdm, &coordSection));
259   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
260   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
261   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
262 
263   PetscCall(PetscObjectGetName((PetscObject) v, &name));
264   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
265 
266   PetscCall(VecGetLocalSize(coordinates, &N));
267   PetscCall(VecGetArrayRead(coordinates, &coords));
268   for (c = 0; c < N; c += dim) {
269     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
270     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
271   }
272   PetscCall(VecRestoreArrayRead(coordinates, &coords));
273   PetscCall(PetscDrawClear(draw));
274 
275   /* Could implement something like DMDASelectFields() */
276   for (f = 0; f < Nf; ++f) {
277     DM   fdm = dm;
278     Vec  fv  = v;
279     IS   fis;
280     char prefix[PETSC_MAX_PATH_LEN];
281     const char *fname;
282 
283     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
284     PetscCall(PetscSectionGetFieldName(s, f, &fname));
285 
286     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix,sizeof(prefix)));
287     else               {prefix[0] = '\0';}
288     if (Nf > 1) {
289       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
290       PetscCall(VecGetSubVector(v, fis, &fv));
291       PetscCall(PetscStrlcat(prefix, fname,sizeof(prefix)));
292       PetscCall(PetscStrlcat(prefix, "_",sizeof(prefix)));
293     }
294     for (comp = 0; comp < Nc; ++comp, ++w) {
295       PetscInt nmax = 2;
296 
297       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
298       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
299       else        PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
300       PetscCall(PetscDrawSetTitle(draw, title));
301 
302       /* TODO Get max and min only for this component */
303       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
304       if (!flg) {
305         PetscCall(VecMin(fv, NULL, &vbound[0]));
306         PetscCall(VecMax(fv, NULL, &vbound[1]));
307         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
308       }
309       PetscCall(PetscDrawGetPopup(draw, &popup));
310       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
311       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
312 
313       PetscCall(VecGetArrayRead(fv, &array));
314       for (c = cStart; c < cEnd; ++c) {
315         PetscScalar *coords = NULL, *a = NULL;
316         PetscInt     numCoords, color[4] = {-1,-1,-1,-1};
317 
318         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
319         if (a) {
320           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
321           color[1] = color[2] = color[3] = color[0];
322         } else {
323           PetscScalar *vals = NULL;
324           PetscInt     numVals, va;
325 
326           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
327           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);
328           switch (numVals/Nc) {
329           case 3: /* P1 Triangle */
330           case 4: /* P1 Quadrangle */
331             for (va = 0; va < numVals/Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp]), vbound[0], vbound[1]);
332             break;
333           case 6: /* P2 Triangle */
334           case 8: /* P2 Quadrangle */
335             for (va = 0; va < numVals/(Nc*2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp + numVals/(Nc*2)]), vbound[0], vbound[1]);
336             break;
337           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals/Nc);
338           }
339           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
340         }
341         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
342         switch (numCoords) {
343         case 6:
344         case 12: /* Localized triangle */
345           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]));
346           break;
347         case 8:
348         case 16: /* Localized quadrilateral */
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           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]));
351           break;
352         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
353         }
354         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
355       }
356       PetscCall(VecRestoreArrayRead(fv, &array));
357       PetscCall(PetscDrawFlush(draw));
358       PetscCall(PetscDrawPause(draw));
359       PetscCall(PetscDrawSave(draw));
360     }
361     if (Nf > 1) {
362       PetscCall(VecRestoreSubVector(v, fis, &fv));
363       PetscCall(ISDestroy(&fis));
364       PetscCall(DMDestroy(&fdm));
365     }
366   }
367   PetscFunctionReturn(0);
368 }
369 
370 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
371 {
372   DM        dm;
373   PetscDraw draw;
374   PetscInt  dim;
375   PetscBool isnull;
376 
377   PetscFunctionBegin;
378   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
379   PetscCall(PetscDrawIsNull(draw, &isnull));
380   if (isnull) PetscFunctionReturn(0);
381 
382   PetscCall(VecGetDM(v, &dm));
383   PetscCall(DMGetCoordinateDim(dm, &dim));
384   switch (dim) {
385   case 1: PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));break;
386   case 2: PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));break;
387   default: SETERRQ(PetscObjectComm((PetscObject) v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
388   }
389   PetscFunctionReturn(0);
390 }
391 
392 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
393 {
394   DM                      dm;
395   Vec                     locv;
396   const char              *name;
397   PetscSection            section;
398   PetscInt                pStart, pEnd;
399   PetscInt                numFields;
400   PetscViewerVTKFieldType ft;
401 
402   PetscFunctionBegin;
403   PetscCall(VecGetDM(v, &dm));
404   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
405   PetscCall(PetscObjectGetName((PetscObject) v, &name));
406   PetscCall(PetscObjectSetName((PetscObject) locv, name));
407   PetscCall(VecCopy(v, locv));
408   PetscCall(DMGetLocalSection(dm, &section));
409   PetscCall(PetscSectionGetNumFields(section, &numFields));
410   if (!numFields) {
411     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
412     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE,(PetscObject) locv));
413   } else {
414     PetscInt f;
415 
416     for (f = 0; f < numFields; f++) {
417       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
418       if (ft == PETSC_VTK_INVALID) continue;
419       PetscCall(PetscObjectReference((PetscObject)locv));
420       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE,(PetscObject) locv));
421     }
422     PetscCall(VecDestroy(&locv));
423   }
424   PetscFunctionReturn(0);
425 }
426 
427 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
428 {
429   DM             dm;
430   PetscBool      isvtk, ishdf5, isdraw, isglvis;
431 
432   PetscFunctionBegin;
433   PetscCall(VecGetDM(v, &dm));
434   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
435   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk));
436   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5));
437   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw));
438   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis));
439   if (isvtk || ishdf5 || isdraw || isglvis) {
440     PetscInt    i,numFields;
441     PetscObject fe;
442     PetscBool   fem = PETSC_FALSE;
443     Vec         locv = v;
444     const char  *name;
445     PetscInt    step;
446     PetscReal   time;
447 
448     PetscCall(DMGetNumFields(dm, &numFields));
449     for (i=0; i<numFields; i++) {
450       PetscCall(DMGetField(dm, i, NULL, &fe));
451       if (fe->classid == PETSCFE_CLASSID) { fem = PETSC_TRUE; break; }
452     }
453     if (fem) {
454       PetscObject isZero;
455 
456       PetscCall(DMGetLocalVector(dm, &locv));
457       PetscCall(PetscObjectGetName((PetscObject) v, &name));
458       PetscCall(PetscObjectSetName((PetscObject) locv, name));
459       PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
460       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
461       PetscCall(VecCopy(v, locv));
462       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
463       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
464     }
465     if (isvtk) {
466       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
467     } else if (ishdf5) {
468 #if defined(PETSC_HAVE_HDF5)
469       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
470 #else
471       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
472 #endif
473     } else if (isdraw) {
474       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
475     } else if (isglvis) {
476       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
477       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
478       PetscCall(VecView_GLVis(locv, viewer));
479     }
480     if (fem) {
481       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
482       PetscCall(DMRestoreLocalVector(dm, &locv));
483     }
484   } else {
485     PetscBool isseq;
486 
487     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
488     if (isseq) PetscCall(VecView_Seq(v, viewer));
489     else       PetscCall(VecView_MPI(v, viewer));
490   }
491   PetscFunctionReturn(0);
492 }
493 
494 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
495 {
496   DM        dm;
497   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii;
498 
499   PetscFunctionBegin;
500   PetscCall(VecGetDM(v, &dm));
501   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
502   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
503   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
504   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
505   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
506   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
507   if (isvtk || isdraw || isglvis) {
508     Vec         locv;
509     PetscObject isZero;
510     const char *name;
511 
512     PetscCall(DMGetLocalVector(dm, &locv));
513     PetscCall(PetscObjectGetName((PetscObject) v, &name));
514     PetscCall(PetscObjectSetName((PetscObject) locv, name));
515     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
516     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
517     PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
518     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
519     PetscCall(VecView_Plex_Local(locv, viewer));
520     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
521     PetscCall(DMRestoreLocalVector(dm, &locv));
522   } else if (ishdf5) {
523 #if defined(PETSC_HAVE_HDF5)
524     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
525 #else
526     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
527 #endif
528   } else if (isexodusii) {
529 #if defined(PETSC_HAVE_EXODUSII)
530     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
531 #else
532     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
533 #endif
534   } else {
535     PetscBool isseq;
536 
537     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
538     if (isseq) PetscCall(VecView_Seq(v, viewer));
539     else       PetscCall(VecView_MPI(v, viewer));
540   }
541   PetscFunctionReturn(0);
542 }
543 
544 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
545 {
546   DM                dm;
547   MPI_Comm          comm;
548   PetscViewerFormat format;
549   Vec               v;
550   PetscBool         isvtk, ishdf5;
551 
552   PetscFunctionBegin;
553   PetscCall(VecGetDM(originalv, &dm));
554   PetscCall(PetscObjectGetComm((PetscObject) originalv, &comm));
555   PetscCheck(dm,comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
556   PetscCall(PetscViewerGetFormat(viewer, &format));
557   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
558   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,  &isvtk));
559   if (format == PETSC_VIEWER_NATIVE) {
560     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
561     /* this need a better fix */
562     if (dm->useNatural) {
563       if (dm->sfNatural) {
564         const char *vecname;
565         PetscInt    n, nroots;
566 
567         PetscCall(VecGetLocalSize(originalv, &n));
568         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
569         if (n == nroots) {
570           PetscCall(DMGetGlobalVector(dm, &v));
571           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
572           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
573           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
574           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
575         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
576       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
577     } else v = originalv;
578   } else v = originalv;
579 
580   if (ishdf5) {
581 #if defined(PETSC_HAVE_HDF5)
582     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
583 #else
584     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
585 #endif
586   } else if (isvtk) {
587     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
588   } else {
589     PetscBool isseq;
590 
591     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
592     if (isseq) PetscCall(VecView_Seq(v, viewer));
593     else       PetscCall(VecView_MPI(v, viewer));
594   }
595   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
596   PetscFunctionReturn(0);
597 }
598 
599 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
600 {
601   DM             dm;
602   PetscBool      ishdf5;
603 
604   PetscFunctionBegin;
605   PetscCall(VecGetDM(v, &dm));
606   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
607   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
608   if (ishdf5) {
609     DM          dmBC;
610     Vec         gv;
611     const char *name;
612 
613     PetscCall(DMGetOutputDM(dm, &dmBC));
614     PetscCall(DMGetGlobalVector(dmBC, &gv));
615     PetscCall(PetscObjectGetName((PetscObject) v, &name));
616     PetscCall(PetscObjectSetName((PetscObject) gv, name));
617     PetscCall(VecLoad_Default(gv, viewer));
618     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
619     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
620     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
621   } else {
622     PetscCall(VecLoad_Default(v, viewer));
623   }
624   PetscFunctionReturn(0);
625 }
626 
627 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
628 {
629   DM             dm;
630   PetscBool      ishdf5,isexodusii;
631 
632   PetscFunctionBegin;
633   PetscCall(VecGetDM(v, &dm));
634   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
635   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
636   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
637   if (ishdf5) {
638 #if defined(PETSC_HAVE_HDF5)
639     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
640 #else
641     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
642 #endif
643   } else if (isexodusii) {
644 #if defined(PETSC_HAVE_EXODUSII)
645     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
646 #else
647     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
648 #endif
649   } else {
650     PetscCall(VecLoad_Default(v, viewer));
651   }
652   PetscFunctionReturn(0);
653 }
654 
655 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
656 {
657   DM                dm;
658   PetscViewerFormat format;
659   PetscBool         ishdf5;
660 
661   PetscFunctionBegin;
662   PetscCall(VecGetDM(originalv, &dm));
663   PetscCheck(dm,PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
664   PetscCall(PetscViewerGetFormat(viewer, &format));
665   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
666   if (format == PETSC_VIEWER_NATIVE) {
667     if (dm->useNatural) {
668       if (dm->sfNatural) {
669         if (ishdf5) {
670 #if defined(PETSC_HAVE_HDF5)
671           Vec         v;
672           const char *vecname;
673 
674           PetscCall(DMGetGlobalVector(dm, &v));
675           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
676           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
677           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
678           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
679           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
680           PetscCall(DMRestoreGlobalVector(dm, &v));
681 #else
682           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
683 #endif
684         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
685       }
686     } else {
687       PetscCall(VecLoad_Default(originalv, viewer));
688     }
689   }
690   PetscFunctionReturn(0);
691 }
692 
693 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
694 {
695   PetscSection       coordSection;
696   Vec                coordinates;
697   DMLabel            depthLabel, celltypeLabel;
698   const char        *name[4];
699   const PetscScalar *a;
700   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
701 
702   PetscFunctionBegin;
703   PetscCall(DMGetDimension(dm, &dim));
704   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
705   PetscCall(DMGetCoordinateSection(dm, &coordSection));
706   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
707   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
708   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
709   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
710   PetscCall(VecGetArrayRead(coordinates, &a));
711   name[0]     = "vertex";
712   name[1]     = "edge";
713   name[dim-1] = "face";
714   name[dim]   = "cell";
715   for (c = cStart; c < cEnd; ++c) {
716     PetscInt *closure = NULL;
717     PetscInt  closureSize, cl, ct;
718 
719     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
720     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
721     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
722     PetscCall(PetscViewerASCIIPushTab(viewer));
723     for (cl = 0; cl < closureSize*2; cl += 2) {
724       PetscInt point = closure[cl], depth, dof, off, d, p;
725 
726       if ((point < pStart) || (point >= pEnd)) continue;
727       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
728       if (!dof) continue;
729       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
730       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
731       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
732       for (p = 0; p < dof/dim; ++p) {
733         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
734         for (d = 0; d < dim; ++d) {
735           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
736           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d])));
737         }
738         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
739       }
740       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
741     }
742     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
743     PetscCall(PetscViewerASCIIPopTab(viewer));
744   }
745   PetscCall(VecRestoreArrayRead(coordinates, &a));
746   PetscFunctionReturn(0);
747 }
748 
749 typedef enum {CS_CARTESIAN, CS_POLAR, CS_CYLINDRICAL, CS_SPHERICAL} CoordSystem;
750 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
751 
752 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
753 {
754   PetscInt       i;
755 
756   PetscFunctionBegin;
757   if (dim > 3) {
758     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) PetscRealPart(x[i])));
759   } else {
760     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
761 
762     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
763     switch (cs) {
764       case CS_CARTESIAN: for (i = 0; i < dim; ++i) trcoords[i] = coords[i];break;
765       case CS_POLAR:
766         PetscCheck(dim == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
767         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
768         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
769         break;
770       case CS_CYLINDRICAL:
771         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
772         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
773         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
774         trcoords[2] = coords[2];
775         break;
776       case CS_SPHERICAL:
777         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
778         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
779         trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
780         trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
781         break;
782     }
783     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) trcoords[i]));
784   }
785   PetscFunctionReturn(0);
786 }
787 
788 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
789 {
790   DM_Plex          *mesh = (DM_Plex*) dm->data;
791   DM                cdm;
792   PetscSection      coordSection;
793   Vec               coordinates;
794   PetscViewerFormat format;
795 
796   PetscFunctionBegin;
797   PetscCall(DMGetCoordinateDM(dm, &cdm));
798   PetscCall(DMGetLocalSection(cdm, &coordSection));
799   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
800   PetscCall(PetscViewerGetFormat(viewer, &format));
801   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
802     const char *name;
803     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
804     PetscInt    pStart, pEnd, p, numLabels, l;
805     PetscMPIInt rank, size;
806 
807     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
808     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
809     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
810     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
811     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
812     PetscCall(DMGetDimension(dm, &dim));
813     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
814     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
815     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
816     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
817     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
818     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
819     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
820     for (p = pStart; p < pEnd; ++p) {
821       PetscInt dof, off, s;
822 
823       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
824       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
825       for (s = off; s < off+dof; ++s) {
826         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
827       }
828     }
829     PetscCall(PetscViewerFlush(viewer));
830     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
831     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
832     for (p = pStart; p < pEnd; ++p) {
833       PetscInt dof, off, c;
834 
835       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
836       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
837       for (c = off; c < off+dof; ++c) {
838         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
839       }
840     }
841     PetscCall(PetscViewerFlush(viewer));
842     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
843     if (coordSection && coordinates) {
844       CoordSystem        cs = CS_CARTESIAN;
845       const PetscScalar *array;
846       PetscInt           Nf, Nc, pStart, pEnd, p;
847       PetscMPIInt        rank;
848       const char        *name;
849 
850       PetscCall(PetscOptionsGetEnum(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *) &cs, NULL));
851       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
852       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
853       PetscCheck(Nf == 1,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
854       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
855       PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
856       PetscCall(PetscObjectGetName((PetscObject) coordinates, &name));
857       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
858       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
859       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
860 
861       PetscCall(VecGetArrayRead(coordinates, &array));
862       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
863       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
864       for (p = pStart; p < pEnd; ++p) {
865         PetscInt dof, off;
866 
867         PetscCall(PetscSectionGetDof(coordSection, p, &dof));
868         PetscCall(PetscSectionGetOffset(coordSection, p, &off));
869         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
870         PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
871         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
872       }
873       PetscCall(PetscViewerFlush(viewer));
874       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
875       PetscCall(VecRestoreArrayRead(coordinates, &array));
876     }
877     PetscCall(DMGetNumLabels(dm, &numLabels));
878     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
879     for (l = 0; l < numLabels; ++l) {
880       DMLabel     label;
881       PetscBool   isdepth;
882       const char *name;
883 
884       PetscCall(DMGetLabelName(dm, l, &name));
885       PetscCall(PetscStrcmp(name, "depth", &isdepth));
886       if (isdepth) continue;
887       PetscCall(DMGetLabel(dm, name, &label));
888       PetscCall(DMLabelView(label, viewer));
889     }
890     if (size > 1) {
891       PetscSF sf;
892 
893       PetscCall(DMGetPointSF(dm, &sf));
894       PetscCall(PetscSFView(sf, viewer));
895     }
896     PetscCall(PetscViewerFlush(viewer));
897   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
898     const char  *name, *color;
899     const char  *defcolors[3]  = {"gray", "orange", "green"};
900     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
901     char         lname[PETSC_MAX_PATH_LEN];
902     PetscReal    scale         = 2.0;
903     PetscReal    tikzscale     = 1.0;
904     PetscBool    useNumbers    = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
905     double       tcoords[3];
906     PetscScalar *coords;
907     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
908     PetscMPIInt  rank, size;
909     char         **names, **colors, **lcolors;
910     PetscBool    flg, lflg;
911     PetscBT      wp = NULL;
912     PetscInt     pEnd, pStart;
913 
914     PetscCall(DMGetDimension(dm, &dim));
915     PetscCall(DMPlexGetDepth(dm, &depth));
916     PetscCall(DMGetNumLabels(dm, &numLabels));
917     numLabels  = PetscMax(numLabels, 10);
918     numColors  = 10;
919     numLColors = 10;
920     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
921     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
922     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
923     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
924     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
925     for (d = 0; d < 4; ++d) drawColors[d]  = PETSC_TRUE;
926     n = 4;
927     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
928     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
929     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
930     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
931     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
932     if (!useLabels) numLabels = 0;
933     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
934     if (!useColors) {
935       numColors = 3;
936       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
937     }
938     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
939     if (!useColors) {
940       numLColors = 4;
941       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
942     }
943     PetscCall(PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
944     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
945     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
946     PetscCheck(!flg || !plotEdges || depth >= dim,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
947     if (depth < dim) plotEdges = PETSC_FALSE;
948     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
949 
950     /* filter points with labelvalue != labeldefaultvalue */
951     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
952     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
953     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
954     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
955     if (lflg) {
956       DMLabel lbl;
957 
958       PetscCall(DMGetLabel(dm, lname, &lbl));
959       if (lbl) {
960         PetscInt val, defval;
961 
962         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
963         PetscCall(PetscBTCreate(pEnd-pStart, &wp));
964         for (c = pStart;  c < pEnd; c++) {
965           PetscInt *closure = NULL;
966           PetscInt  closureSize;
967 
968           PetscCall(DMLabelGetValue(lbl, c, &val));
969           if (val == defval) continue;
970 
971           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
972           for (p = 0; p < closureSize*2; p += 2) {
973             PetscCall(PetscBTSet(wp, closure[p] - pStart));
974           }
975           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
976         }
977       }
978     }
979 
980     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
981     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
982     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
983     PetscCall(PetscViewerASCIIPrintf(viewer, "\
984 \\documentclass[tikz]{standalone}\n\n\
985 \\usepackage{pgflibraryshapes}\n\
986 \\usetikzlibrary{backgrounds}\n\
987 \\usetikzlibrary{arrows}\n\
988 \\begin{document}\n"));
989     if (size > 1) {
990       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
991       for (p = 0; p < size; ++p) {
992         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size-1) ? ", and " :  ", "));
993         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p%numColors], p));
994       }
995       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
996     }
997     if (drawHasse) {
998       PetscInt maxStratum = PetscMax(vEnd-vStart, PetscMax(eEnd-eStart, cEnd-cStart));
999 
1000       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1001       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd-1));
1002       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd-vStart));
1003       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum-(vEnd-vStart))/2.));
1004       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1005       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd-1));
1006       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum-(eEnd-eStart))/2.));
1007       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd-eStart));
1008       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1009       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd-1));
1010       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd-cStart));
1011       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum-(cEnd-cStart))/2.));
1012     }
1013     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale));
1014 
1015     /* Plot vertices */
1016     PetscCall(VecGetArray(coordinates, &coords));
1017     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1018     for (v = vStart; v < vEnd; ++v) {
1019       PetscInt  off, dof, d;
1020       PetscBool isLabeled = PETSC_FALSE;
1021 
1022       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
1023       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1024       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1025       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1026       PetscCheck(dof <= 3,PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3",v,dof);
1027       for (d = 0; d < dof; ++d) {
1028         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1029         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1030       }
1031       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1032       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1033       for (d = 0; d < dof; ++d) {
1034         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1035         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]));
1036       }
1037       if (drawHasse) color = colors[0%numColors];
1038       else           color = colors[rank%numColors];
1039       for (l = 0; l < numLabels; ++l) {
1040         PetscInt val;
1041         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1042         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1043       }
1044       if (drawNumbers[0]) {
1045         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1046       } else if (drawColors[0]) {
1047         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1048       } else {
1049         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1050       }
1051     }
1052     PetscCall(VecRestoreArray(coordinates, &coords));
1053     PetscCall(PetscViewerFlush(viewer));
1054     /* Plot edges */
1055     if (plotEdges) {
1056       PetscCall(VecGetArray(coordinates, &coords));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1058       for (e = eStart; e < eEnd; ++e) {
1059         const PetscInt *cone;
1060         PetscInt        coneSize, offA, offB, dof, d;
1061 
1062         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1063         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1064         PetscCheck(coneSize == 2,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1065         PetscCall(DMPlexGetCone(dm, e, &cone));
1066         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1067         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1068         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1069         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1070         for (d = 0; d < dof; ++d) {
1071           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
1072           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1073         }
1074         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1075         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1076         for (d = 0; d < dof; ++d) {
1077           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1078           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1079         }
1080         if (drawHasse) color = colors[1%numColors];
1081         else           color = colors[rank%numColors];
1082         for (l = 0; l < numLabels; ++l) {
1083           PetscInt val;
1084           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1085           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1086         }
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1088       }
1089       PetscCall(VecRestoreArray(coordinates, &coords));
1090       PetscCall(PetscViewerFlush(viewer));
1091       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1092     }
1093     /* Plot cells */
1094     if (dim == 3 || !drawNumbers[1]) {
1095       for (e = eStart; e < eEnd; ++e) {
1096         const PetscInt *cone;
1097 
1098         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1099         color = colors[rank%numColors];
1100         for (l = 0; l < numLabels; ++l) {
1101           PetscInt val;
1102           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1103           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1104         }
1105         PetscCall(DMPlexGetCone(dm, e, &cone));
1106         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1107       }
1108     } else {
1109        DMPolytopeType ct;
1110 
1111       /* Drawing a 2D polygon */
1112       for (c = cStart; c < cEnd; ++c) {
1113         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1114         PetscCall(DMPlexGetCellType(dm, c, &ct));
1115         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR ||
1116             ct == DM_POLYTOPE_TRI_PRISM_TENSOR ||
1117             ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1118           const PetscInt *cone;
1119           PetscInt        coneSize, e;
1120 
1121           PetscCall(DMPlexGetCone(dm, c, &cone));
1122           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1123           for (e = 0; e < coneSize; ++e) {
1124             const PetscInt *econe;
1125 
1126             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1127             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));
1128           }
1129         } else {
1130           PetscInt *closure = NULL;
1131           PetscInt  closureSize, Nv = 0, v;
1132 
1133           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1134           for (p = 0; p < closureSize*2; p += 2) {
1135             const PetscInt point = closure[p];
1136 
1137             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1138           }
1139           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]));
1140           for (v = 0; v <= Nv; ++v) {
1141             const PetscInt vertex = closure[v%Nv];
1142 
1143             if (v > 0) {
1144               if (plotEdges) {
1145                 const PetscInt *edge;
1146                 PetscInt        endpoints[2], ne;
1147 
1148                 endpoints[0] = closure[v-1]; endpoints[1] = vertex;
1149                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1150                 PetscCheck(ne == 1,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1151                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1152                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1153               } else {
1154                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1155               }
1156             }
1157             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1158           }
1159           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1160           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1161         }
1162       }
1163     }
1164     PetscCall(VecGetArray(coordinates, &coords));
1165     for (c = cStart; c < cEnd; ++c) {
1166       double    ccoords[3] = {0.0, 0.0, 0.0};
1167       PetscBool isLabeled  = PETSC_FALSE;
1168       PetscInt *closure    = NULL;
1169       PetscInt  closureSize, dof, d, n = 0;
1170 
1171       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
1172       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1173       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1174       for (p = 0; p < closureSize*2; p += 2) {
1175         const PetscInt point = closure[p];
1176         PetscInt       off;
1177 
1178         if ((point < vStart) || (point >= vEnd)) continue;
1179         PetscCall(PetscSectionGetDof(coordSection, point, &dof));
1180         PetscCall(PetscSectionGetOffset(coordSection, point, &off));
1181         for (d = 0; d < dof; ++d) {
1182           tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1183           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1184         }
1185         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1186         if (dof == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1187         for (d = 0; d < dof; ++d) {ccoords[d] += tcoords[d];}
1188         ++n;
1189       }
1190       for (d = 0; d < dof; ++d) {ccoords[d] /= n;}
1191       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1192       for (d = 0; d < dof; ++d) {
1193         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1194         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]));
1195       }
1196       if (drawHasse) color = colors[depth%numColors];
1197       else           color = colors[rank%numColors];
1198       for (l = 0; l < numLabels; ++l) {
1199         PetscInt val;
1200         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1201         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1202       }
1203       if (drawNumbers[dim]) {
1204         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1205       } else if (drawColors[dim]) {
1206         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1207       } else {
1208         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1209       }
1210     }
1211     PetscCall(VecRestoreArray(coordinates, &coords));
1212     if (drawHasse) {
1213       color = colors[depth%numColors];
1214       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1215       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1216       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1217       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1218       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1219 
1220       color = colors[1%numColors];
1221       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1222       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1223       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1224       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1225       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1226 
1227       color = colors[0%numColors];
1228       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1229       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1230       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1231       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1232       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1233 
1234       for (p = pStart; p < pEnd; ++p) {
1235         const PetscInt *cone;
1236         PetscInt        coneSize, cp;
1237 
1238         PetscCall(DMPlexGetCone(dm, p, &cone));
1239         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1240         for (cp = 0; cp < coneSize; ++cp) {
1241           PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1242         }
1243       }
1244     }
1245     PetscCall(PetscViewerFlush(viewer));
1246     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1247     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1248     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1249     for (l = 0; l < numLabels;  ++l) PetscCall(PetscFree(names[l]));
1250     for (c = 0; c < numColors;  ++c) PetscCall(PetscFree(colors[c]));
1251     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1252     PetscCall(PetscFree3(names, colors, lcolors));
1253     PetscCall(PetscBTDestroy(&wp));
1254   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1255     Vec                    cown,acown;
1256     VecScatter             sct;
1257     ISLocalToGlobalMapping g2l;
1258     IS                     gid,acis;
1259     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
1260     MPI_Group              ggroup,ngroup;
1261     PetscScalar            *array,nid;
1262     const PetscInt         *idxs;
1263     PetscInt               *idxs2,*start,*adjacency,*work;
1264     PetscInt64             lm[3],gm[3];
1265     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
1266     PetscMPIInt            d1,d2,rank;
1267 
1268     PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
1269     PetscCallMPI(MPI_Comm_rank(comm,&rank));
1270 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1271     PetscCallMPI(MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm));
1272 #endif
1273     if (ncomm != MPI_COMM_NULL) {
1274       PetscCallMPI(MPI_Comm_group(comm,&ggroup));
1275       PetscCallMPI(MPI_Comm_group(ncomm,&ngroup));
1276       d1   = 0;
1277       PetscCallMPI(MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2));
1278       nid  = d2;
1279       PetscCallMPI(MPI_Group_free(&ggroup));
1280       PetscCallMPI(MPI_Group_free(&ngroup));
1281       PetscCallMPI(MPI_Comm_free(&ncomm));
1282     } else nid = 0.0;
1283 
1284     /* Get connectivity */
1285     PetscCall(DMPlexGetVTKCellHeight(dm,&cellHeight));
1286     PetscCall(DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid));
1287 
1288     /* filter overlapped local cells */
1289     PetscCall(DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd));
1290     PetscCall(ISGetIndices(gid,&idxs));
1291     PetscCall(ISGetLocalSize(gid,&cum));
1292     PetscCall(PetscMalloc1(cum,&idxs2));
1293     for (c = cStart, cum = 0; c < cEnd; c++) {
1294       if (idxs[c-cStart] < 0) continue;
1295       idxs2[cum++] = idxs[c-cStart];
1296     }
1297     PetscCall(ISRestoreIndices(gid,&idxs));
1298     PetscCheck(numVertices == cum,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %" PetscInt_FMT " != %" PetscInt_FMT,numVertices,cum);
1299     PetscCall(ISDestroy(&gid));
1300     PetscCall(ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid));
1301 
1302     /* support for node-aware cell locality */
1303     PetscCall(ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis));
1304     PetscCall(VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown));
1305     PetscCall(VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown));
1306     PetscCall(VecGetArray(cown,&array));
1307     for (c = 0; c < numVertices; c++) array[c] = nid;
1308     PetscCall(VecRestoreArray(cown,&array));
1309     PetscCall(VecScatterCreate(cown,acis,acown,NULL,&sct));
1310     PetscCall(VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1311     PetscCall(VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1312     PetscCall(ISDestroy(&acis));
1313     PetscCall(VecScatterDestroy(&sct));
1314     PetscCall(VecDestroy(&cown));
1315 
1316     /* compute edgeCut */
1317     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
1318     PetscCall(PetscMalloc1(cum,&work));
1319     PetscCall(ISLocalToGlobalMappingCreateIS(gid,&g2l));
1320     PetscCall(ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH));
1321     PetscCall(ISDestroy(&gid));
1322     PetscCall(VecGetArray(acown,&array));
1323     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1324       PetscInt totl;
1325 
1326       totl = start[c+1]-start[c];
1327       PetscCall(ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work));
1328       for (i = 0; i < totl; i++) {
1329         if (work[i] < 0) {
1330           ect  += 1;
1331           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1332         }
1333       }
1334     }
1335     PetscCall(PetscFree(work));
1336     PetscCall(VecRestoreArray(acown,&array));
1337     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1338     lm[1] = -numVertices;
1339     PetscCall(MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm));
1340     PetscCall(PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT,-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]));
1341     lm[0] = ect; /* edgeCut */
1342     lm[1] = ectn; /* node-aware edgeCut */
1343     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1344     PetscCall(MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm));
1345     PetscCall(PetscViewerASCIIPrintf(viewer,", empty %" PetscInt_FMT ")\n",(PetscInt)gm[2]));
1346 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1347     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),gm[0] ? ((double)(gm[1]))/((double)gm[0]) : 1.));
1348 #else
1349     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0));
1350 #endif
1351     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1352     PetscCall(PetscFree(start));
1353     PetscCall(PetscFree(adjacency));
1354     PetscCall(VecDestroy(&acown));
1355   } else {
1356     const char    *name;
1357     PetscInt      *sizes, *hybsizes, *ghostsizes;
1358     PetscInt       locDepth, depth, cellHeight, dim, d;
1359     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1360     PetscInt       numLabels, l, maxSize = 17;
1361     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1362     MPI_Comm       comm;
1363     PetscMPIInt    size, rank;
1364 
1365     PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
1366     PetscCallMPI(MPI_Comm_size(comm, &size));
1367     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1368     PetscCall(DMGetDimension(dm, &dim));
1369     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1370     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1371     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1372     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1373     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1374     PetscCall(DMPlexGetDepth(dm, &locDepth));
1375     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1376     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1377     gcNum = gcEnd - gcStart;
1378     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1379     else                PetscCall(PetscCalloc3(3,    &sizes, 3,    &hybsizes, 3,    &ghostsizes));
1380     for (d = 0; d <= depth; d++) {
1381       PetscInt Nc[2] = {0, 0}, ict;
1382 
1383       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1384       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1385       ict  = ct0;
1386       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1387       ct0  = (DMPolytopeType) ict;
1388       for (p = pStart; p < pEnd; ++p) {
1389         DMPolytopeType ct;
1390 
1391         PetscCall(DMPlexGetCellType(dm, p, &ct));
1392         if (ct == ct0) ++Nc[0];
1393         else           ++Nc[1];
1394       }
1395       if (size < maxSize) {
1396         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm));
1397         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1398         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1399         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1400         for (p = 0; p < size; ++p) {
1401           if (rank == 0) {
1402             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p]+hybsizes[p]));
1403             if (hybsizes[p]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1404             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1405           }
1406         }
1407       } else {
1408         PetscInt locMinMax[2];
1409 
1410         locMinMax[0] = Nc[0]+Nc[1]; locMinMax[1] = Nc[0]+Nc[1];
1411         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1412         locMinMax[0] = Nc[1]; locMinMax[1] = Nc[1];
1413         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1414         if (d == depth) {
1415           locMinMax[0] = gcNum; locMinMax[1] = gcNum;
1416           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1417         }
1418         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1419         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1420         if (hybsizes[0]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1421         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1422       }
1423       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1424     }
1425     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1426     {
1427       const PetscReal      *maxCell;
1428       const PetscReal      *L;
1429       const DMBoundaryType *bd;
1430       PetscBool             per, localized;
1431 
1432       PetscCall(DMGetPeriodicity(dm, &per, &maxCell, &L, &bd));
1433       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1434       if (per) {
1435         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh ("));
1436         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1437         for (d = 0; d < dim; ++d) {
1438           if (bd && d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1439           if (bd)    PetscCall(PetscViewerASCIIPrintf(viewer, "%s", DMBoundaryTypes[bd[d]]));
1440         }
1441         PetscCall(PetscViewerASCIIPrintf(viewer, ") coordinates %s\n", localized ? "localized" : "not localized"));
1442         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1443       }
1444     }
1445     PetscCall(DMGetNumLabels(dm, &numLabels));
1446     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1447     for (l = 0; l < numLabels; ++l) {
1448       DMLabel         label;
1449       const char     *name;
1450       IS              valueIS;
1451       const PetscInt *values;
1452       PetscInt        numValues, v;
1453 
1454       PetscCall(DMGetLabelName(dm, l, &name));
1455       PetscCall(DMGetLabel(dm, name, &label));
1456       PetscCall(DMLabelGetNumValues(label, &numValues));
1457       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1458       PetscCall(DMLabelGetValueIS(label, &valueIS));
1459       PetscCall(ISGetIndices(valueIS, &values));
1460       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1461       for (v = 0; v < numValues; ++v) {
1462         PetscInt size;
1463 
1464         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1465         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1466         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1467       }
1468       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1469       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1470       PetscCall(ISRestoreIndices(valueIS, &values));
1471       PetscCall(ISDestroy(&valueIS));
1472     }
1473     {
1474       char    **labelNames;
1475       PetscInt  Nl = numLabels;
1476       PetscBool flg;
1477 
1478       PetscCall(PetscMalloc1(Nl, &labelNames));
1479       PetscCall(PetscOptionsGetStringArray(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1480       for (l = 0; l < Nl; ++l) {
1481         DMLabel label;
1482 
1483         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1484         if (flg) {
1485           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1486           PetscCall(DMLabelView(label, viewer));
1487         }
1488         PetscCall(PetscFree(labelNames[l]));
1489       }
1490       PetscCall(PetscFree(labelNames));
1491     }
1492     /* If no fields are specified, people do not want to see adjacency */
1493     if (dm->Nf) {
1494       PetscInt f;
1495 
1496       for (f = 0; f < dm->Nf; ++f) {
1497         const char *name;
1498 
1499         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1500         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1501         PetscCall(PetscViewerASCIIPushTab(viewer));
1502         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1503         if (dm->fields[f].adjacency[0]) {
1504           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1505           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1506         } else {
1507           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1508           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1509         }
1510         PetscCall(PetscViewerASCIIPopTab(viewer));
1511       }
1512     }
1513     PetscCall(DMGetCoarseDM(dm, &cdm));
1514     if (cdm) {
1515       PetscCall(PetscViewerASCIIPushTab(viewer));
1516       PetscCall(DMPlexView_Ascii(cdm, viewer));
1517       PetscCall(PetscViewerASCIIPopTab(viewer));
1518     }
1519   }
1520   PetscFunctionReturn(0);
1521 }
1522 
1523 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1524 {
1525   DMPolytopeType ct;
1526   PetscMPIInt    rank;
1527   PetscInt       cdim;
1528 
1529   PetscFunctionBegin;
1530   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1531   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1532   PetscCall(DMGetCoordinateDim(dm, &cdim));
1533   switch (ct) {
1534   case DM_POLYTOPE_SEGMENT:
1535   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1536     switch (cdim) {
1537     case 1:
1538     {
1539       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1540       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1541 
1542       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y,    PetscRealPart(coords[1]), y,    PETSC_DRAW_BLACK));
1543       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y+dy, PetscRealPart(coords[0]), y-dy, PETSC_DRAW_BLACK));
1544       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y+dy, PetscRealPart(coords[1]), y-dy, PETSC_DRAW_BLACK));
1545     }
1546     break;
1547     case 2:
1548     {
1549       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1550       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1551       const PetscReal l  = 0.1/PetscSqrtReal(dx*dx + dy*dy);
1552 
1553       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1554       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));
1555       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));
1556     }
1557     break;
1558     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1559     }
1560     break;
1561   case DM_POLYTOPE_TRIANGLE:
1562     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1563                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1564                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1565                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1566     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1567     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1568     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1569     break;
1570   case DM_POLYTOPE_QUADRILATERAL:
1571     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1572                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1573                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1574                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1575     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1576                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1577                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1578                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1579     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1580     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1581     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1582     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1583     break;
1584   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1585   }
1586   PetscFunctionReturn(0);
1587 }
1588 
1589 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1590 {
1591   DMPolytopeType ct;
1592   PetscReal      centroid[2] = {0., 0.};
1593   PetscMPIInt    rank;
1594   PetscInt       fillColor, v, e, d;
1595 
1596   PetscFunctionBegin;
1597   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1598   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1599   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2;
1600   switch (ct) {
1601   case DM_POLYTOPE_TRIANGLE:
1602     {
1603       PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1604 
1605       for (v = 0; v < 3; ++v) {centroid[0] += PetscRealPart(coords[v*2+0])/3.;centroid[1] += PetscRealPart(coords[v*2+1])/3.;}
1606       for (e = 0; e < 3; ++e) {
1607         refCoords[0] = refVertices[e*2+0];
1608         refCoords[1] = refVertices[e*2+1];
1609         for (d = 1; d <= edgeDiv; ++d) {
1610           refCoords[d*2+0] = refCoords[0] + (refVertices[(e+1)%3 * 2 + 0] - refCoords[0])*d/edgeDiv;
1611           refCoords[d*2+1] = refCoords[1] + (refVertices[(e+1)%3 * 2 + 1] - refCoords[1])*d/edgeDiv;
1612         }
1613         PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv+1, refCoords, edgeCoords));
1614         for (d = 0; d < edgeDiv; ++d) {
1615           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));
1616           PetscCall(PetscDrawLine(draw, edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], PETSC_DRAW_BLACK));
1617         }
1618       }
1619     }
1620     break;
1621   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1622   }
1623   PetscFunctionReturn(0);
1624 }
1625 
1626 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1627 {
1628   PetscDraw          draw;
1629   DM                 cdm;
1630   PetscSection       coordSection;
1631   Vec                coordinates;
1632   const PetscScalar *coords;
1633   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1634   PetscReal         *refCoords, *edgeCoords;
1635   PetscBool          isnull, drawAffine = PETSC_TRUE;
1636   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1637 
1638   PetscFunctionBegin;
1639   PetscCall(DMGetCoordinateDim(dm, &dim));
1640   PetscCheck(dim <= 2,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1641   PetscCall(PetscOptionsGetBool(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1642   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv+1)*dim, &refCoords, (edgeDiv+1)*dim, &edgeCoords));
1643   PetscCall(DMGetCoordinateDM(dm, &cdm));
1644   PetscCall(DMGetLocalSection(cdm, &coordSection));
1645   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1646   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1647   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1648 
1649   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1650   PetscCall(PetscDrawIsNull(draw, &isnull));
1651   if (isnull) PetscFunctionReturn(0);
1652   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1653 
1654   PetscCall(VecGetLocalSize(coordinates, &N));
1655   PetscCall(VecGetArrayRead(coordinates, &coords));
1656   for (c = 0; c < N; c += dim) {
1657     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1658     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1659   }
1660   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1661   PetscCall(MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm)));
1662   PetscCall(MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm)));
1663   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1664   PetscCall(PetscDrawClear(draw));
1665 
1666   for (c = cStart; c < cEnd; ++c) {
1667     PetscScalar *coords = NULL;
1668     PetscInt     numCoords;
1669 
1670     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1671     if (drawAffine) {
1672       PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1673     } else {
1674       PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1675     }
1676     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1677   }
1678   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1679   PetscCall(PetscDrawFlush(draw));
1680   PetscCall(PetscDrawPause(draw));
1681   PetscCall(PetscDrawSave(draw));
1682   PetscFunctionReturn(0);
1683 }
1684 
1685 #if defined(PETSC_HAVE_EXODUSII)
1686 #include <exodusII.h>
1687 #include <petscviewerexodusii.h>
1688 #endif
1689 
1690 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1691 {
1692   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus;
1693   char           name[PETSC_MAX_PATH_LEN];
1694 
1695   PetscFunctionBegin;
1696   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1697   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1698   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii));
1699   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
1700   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
1701   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
1702   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
1703   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus));
1704   if (iascii) {
1705     PetscViewerFormat format;
1706     PetscCall(PetscViewerGetFormat(viewer, &format));
1707     if (format == PETSC_VIEWER_ASCII_GLVIS) {
1708       PetscCall(DMPlexView_GLVis(dm, viewer));
1709     } else {
1710       PetscCall(DMPlexView_Ascii(dm, viewer));
1711     }
1712   } else if (ishdf5) {
1713 #if defined(PETSC_HAVE_HDF5)
1714     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1715 #else
1716     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1717 #endif
1718   } else if (isvtk) {
1719     PetscCall(DMPlexVTKWriteAll((PetscObject) dm,viewer));
1720   } else if (isdraw) {
1721     PetscCall(DMPlexView_Draw(dm, viewer));
1722   } else if (isglvis) {
1723     PetscCall(DMPlexView_GLVis(dm, viewer));
1724 #if defined(PETSC_HAVE_EXODUSII)
1725   } else if (isexodus) {
1726 /*
1727       exodusII requires that all sets be part of exactly one cell set.
1728       If the dm does not have a "Cell Sets" label defined, we create one
1729       with ID 1, containig all cells.
1730       Note that if the Cell Sets label is defined but does not cover all cells,
1731       we may still have a problem. This should probably be checked here or in the viewer;
1732     */
1733     PetscInt numCS;
1734     PetscCall(DMGetLabelSize(dm,"Cell Sets",&numCS));
1735     if (!numCS) {
1736       PetscInt cStart, cEnd, c;
1737       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1738       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1739       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1740     }
1741     PetscCall(DMView_PlexExodusII(dm, viewer));
1742 #endif
1743   } else {
1744     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1745   }
1746   /* Optionally view the partition */
1747   PetscCall(PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg));
1748   if (flg) {
1749     Vec ranks;
1750     PetscCall(DMPlexCreateRankField(dm, &ranks));
1751     PetscCall(VecView(ranks, viewer));
1752     PetscCall(VecDestroy(&ranks));
1753   }
1754   /* Optionally view a label */
1755   PetscCall(PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1756   if (flg) {
1757     DMLabel label;
1758     Vec     val;
1759 
1760     PetscCall(DMGetLabel(dm, name, &label));
1761     PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1762     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1763     PetscCall(VecView(val, viewer));
1764     PetscCall(VecDestroy(&val));
1765   }
1766   PetscFunctionReturn(0);
1767 }
1768 
1769 /*@
1770   DMPlexTopologyView - Saves a DMPlex topology into a file
1771 
1772   Collective on DM
1773 
1774   Input Parameters:
1775 + dm     - The DM whose topology is to be saved
1776 - viewer - The PetscViewer for saving
1777 
1778   Level: advanced
1779 
1780 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1781 @*/
1782 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1783 {
1784   PetscBool      ishdf5;
1785 
1786   PetscFunctionBegin;
1787   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1788   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1789   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1790   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView,viewer,0,0,0));
1791   if (ishdf5) {
1792 #if defined(PETSC_HAVE_HDF5)
1793     PetscViewerFormat format;
1794     PetscCall(PetscViewerGetFormat(viewer, &format));
1795     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1796       IS globalPointNumbering;
1797 
1798       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1799       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1800       PetscCall(ISDestroy(&globalPointNumbering));
1801     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1802 #else
1803     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1804 #endif
1805   }
1806   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView,viewer,0,0,0));
1807   PetscFunctionReturn(0);
1808 }
1809 
1810 /*@
1811   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1812 
1813   Collective on DM
1814 
1815   Input Parameters:
1816 + dm     - The DM whose coordinates are to be saved
1817 - viewer - The PetscViewer for saving
1818 
1819   Level: advanced
1820 
1821 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1822 @*/
1823 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1824 {
1825   PetscBool      ishdf5;
1826 
1827   PetscFunctionBegin;
1828   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1829   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1830   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1831   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView,viewer,0,0,0));
1832   if (ishdf5) {
1833 #if defined(PETSC_HAVE_HDF5)
1834     PetscViewerFormat format;
1835     PetscCall(PetscViewerGetFormat(viewer, &format));
1836     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1837       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1838     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1839 #else
1840     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1841 #endif
1842   }
1843   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView,viewer,0,0,0));
1844   PetscFunctionReturn(0);
1845 }
1846 
1847 /*@
1848   DMPlexLabelsView - Saves DMPlex labels into a file
1849 
1850   Collective on DM
1851 
1852   Input Parameters:
1853 + dm     - The DM whose labels are to be saved
1854 - viewer - The PetscViewer for saving
1855 
1856   Level: advanced
1857 
1858 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1859 @*/
1860 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1861 {
1862   PetscBool      ishdf5;
1863 
1864   PetscFunctionBegin;
1865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1866   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1867   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1868   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView,viewer,0,0,0));
1869   if (ishdf5) {
1870 #if defined(PETSC_HAVE_HDF5)
1871     IS                globalPointNumbering;
1872     PetscViewerFormat format;
1873 
1874     PetscCall(PetscViewerGetFormat(viewer, &format));
1875     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1876       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1877       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1878       PetscCall(ISDestroy(&globalPointNumbering));
1879     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1880 #else
1881     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1882 #endif
1883   }
1884   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView,viewer,0,0,0));
1885   PetscFunctionReturn(0);
1886 }
1887 
1888 /*@
1889   DMPlexSectionView - Saves a section associated with a DMPlex
1890 
1891   Collective on DM
1892 
1893   Input Parameters:
1894 + dm         - The DM that contains the topology on which the section to be saved is defined
1895 . viewer     - The PetscViewer for saving
1896 - sectiondm  - The DM that contains the section to be saved
1897 
1898   Level: advanced
1899 
1900   Notes:
1901   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.
1902 
1903   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.
1904 
1905 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1906 @*/
1907 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1908 {
1909   PetscBool      ishdf5;
1910 
1911   PetscFunctionBegin;
1912   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1913   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1914   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1915   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
1916   PetscCall(PetscLogEventBegin(DMPLEX_SectionView,viewer,0,0,0));
1917   if (ishdf5) {
1918 #if defined(PETSC_HAVE_HDF5)
1919     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1920 #else
1921     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1922 #endif
1923   }
1924   PetscCall(PetscLogEventEnd(DMPLEX_SectionView,viewer,0,0,0));
1925   PetscFunctionReturn(0);
1926 }
1927 
1928 /*@
1929   DMPlexGlobalVectorView - Saves a global vector
1930 
1931   Collective on DM
1932 
1933   Input Parameters:
1934 + dm        - The DM that represents the topology
1935 . viewer    - The PetscViewer to save data with
1936 . sectiondm - The DM that contains the global section on which vec is defined
1937 - vec       - The global vector to be saved
1938 
1939   Level: advanced
1940 
1941   Notes:
1942   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.
1943 
1944   Typical calling sequence
1945 $       DMCreate(PETSC_COMM_WORLD, &dm);
1946 $       DMSetType(dm, DMPLEX);
1947 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1948 $       DMClone(dm, &sectiondm);
1949 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1950 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1951 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1952 $       PetscSectionSetChart(section, pStart, pEnd);
1953 $       PetscSectionSetUp(section);
1954 $       DMSetLocalSection(sectiondm, section);
1955 $       PetscSectionDestroy(&section);
1956 $       DMGetGlobalVector(sectiondm, &vec);
1957 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1958 $       DMPlexTopologyView(dm, viewer);
1959 $       DMPlexSectionView(dm, viewer, sectiondm);
1960 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1961 $       DMRestoreGlobalVector(sectiondm, &vec);
1962 $       DMDestroy(&sectiondm);
1963 $       DMDestroy(&dm);
1964 
1965 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1966 @*/
1967 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
1968 {
1969   PetscBool       ishdf5;
1970 
1971   PetscFunctionBegin;
1972   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1973   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1974   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1975   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1976   /* Check consistency */
1977   {
1978     PetscSection  section;
1979     PetscBool     includesConstraints;
1980     PetscInt      m, m1;
1981 
1982     PetscCall(VecGetLocalSize(vec, &m1));
1983     PetscCall(DMGetGlobalSection(sectiondm, &section));
1984     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1985     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
1986     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
1987     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
1988   }
1989   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1990   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView,viewer,0,0,0));
1991   if (ishdf5) {
1992 #if defined(PETSC_HAVE_HDF5)
1993     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
1994 #else
1995     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1996 #endif
1997   }
1998   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView,viewer,0,0,0));
1999   PetscFunctionReturn(0);
2000 }
2001 
2002 /*@
2003   DMPlexLocalVectorView - Saves a local vector
2004 
2005   Collective on DM
2006 
2007   Input Parameters:
2008 + dm        - The DM that represents the topology
2009 . viewer    - The PetscViewer to save data with
2010 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2011 - vec       - The local vector to be saved
2012 
2013   Level: advanced
2014 
2015   Notes:
2016   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.
2017 
2018   Typical calling sequence
2019 $       DMCreate(PETSC_COMM_WORLD, &dm);
2020 $       DMSetType(dm, DMPLEX);
2021 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2022 $       DMClone(dm, &sectiondm);
2023 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2024 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2025 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2026 $       PetscSectionSetChart(section, pStart, pEnd);
2027 $       PetscSectionSetUp(section);
2028 $       DMSetLocalSection(sectiondm, section);
2029 $       DMGetLocalVector(sectiondm, &vec);
2030 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2031 $       DMPlexTopologyView(dm, viewer);
2032 $       DMPlexSectionView(dm, viewer, sectiondm);
2033 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2034 $       DMRestoreLocalVector(sectiondm, &vec);
2035 $       DMDestroy(&sectiondm);
2036 $       DMDestroy(&dm);
2037 
2038 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2039 @*/
2040 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2041 {
2042   PetscBool       ishdf5;
2043 
2044   PetscFunctionBegin;
2045   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2046   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2047   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2048   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2049   /* Check consistency */
2050   {
2051     PetscSection  section;
2052     PetscBool     includesConstraints;
2053     PetscInt      m, m1;
2054 
2055     PetscCall(VecGetLocalSize(vec, &m1));
2056     PetscCall(DMGetLocalSection(sectiondm, &section));
2057     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2058     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2059     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2060     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2061   }
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2063   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView,viewer,0,0,0));
2064   if (ishdf5) {
2065 #if defined(PETSC_HAVE_HDF5)
2066     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2067 #else
2068     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2069 #endif
2070   }
2071   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView,viewer,0,0,0));
2072   PetscFunctionReturn(0);
2073 }
2074 
2075 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2076 {
2077   PetscBool      ishdf5;
2078 
2079   PetscFunctionBegin;
2080   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2081   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2082   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5));
2083   if (ishdf5) {
2084 #if defined(PETSC_HAVE_HDF5)
2085     PetscViewerFormat format;
2086     PetscCall(PetscViewerGetFormat(viewer, &format));
2087     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2088       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2089     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2090       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2091     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2092     PetscFunctionReturn(0);
2093 #else
2094     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2095 #endif
2096   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2097 }
2098 
2099 /*@
2100   DMPlexTopologyLoad - Loads a topology into a DMPlex
2101 
2102   Collective on DM
2103 
2104   Input Parameters:
2105 + dm     - The DM into which the topology is loaded
2106 - viewer - The PetscViewer for the saved topology
2107 
2108   Output Parameters:
2109 . 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
2110 
2111   Level: advanced
2112 
2113 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2114 @*/
2115 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2116 {
2117   PetscBool      ishdf5;
2118 
2119   PetscFunctionBegin;
2120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2121   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2122   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2123   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2124   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad,viewer,0,0,0));
2125   if (ishdf5) {
2126 #if defined(PETSC_HAVE_HDF5)
2127     PetscViewerFormat format;
2128     PetscCall(PetscViewerGetFormat(viewer, &format));
2129     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2130       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2131     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2132 #else
2133     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2134 #endif
2135   }
2136   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad,viewer,0,0,0));
2137   PetscFunctionReturn(0);
2138 }
2139 
2140 /*@
2141   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2142 
2143   Collective on DM
2144 
2145   Input Parameters:
2146 + dm     - The DM into which the coordinates are loaded
2147 . viewer - The PetscViewer for the saved coordinates
2148 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2149 
2150   Level: advanced
2151 
2152 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2153 @*/
2154 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2155 {
2156   PetscBool      ishdf5;
2157 
2158   PetscFunctionBegin;
2159   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2160   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2161   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2162   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2163   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2164   if (ishdf5) {
2165 #if defined(PETSC_HAVE_HDF5)
2166     PetscViewerFormat format;
2167     PetscCall(PetscViewerGetFormat(viewer, &format));
2168     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2169       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2170     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2171 #else
2172     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2173 #endif
2174   }
2175   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2176   PetscFunctionReturn(0);
2177 }
2178 
2179 /*@
2180   DMPlexLabelsLoad - Loads labels into a DMPlex
2181 
2182   Collective on DM
2183 
2184   Input Parameters:
2185 + dm     - The DM into which the labels are loaded
2186 . viewer - The PetscViewer for the saved labels
2187 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2188 
2189   Level: advanced
2190 
2191   Notes:
2192   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2193 
2194 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2195 @*/
2196 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2197 {
2198   PetscBool      ishdf5;
2199 
2200   PetscFunctionBegin;
2201   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2202   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2203   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2204   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2205   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad,viewer,0,0,0));
2206   if (ishdf5) {
2207 #if defined(PETSC_HAVE_HDF5)
2208     PetscViewerFormat format;
2209 
2210     PetscCall(PetscViewerGetFormat(viewer, &format));
2211     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2212       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2213     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2214 #else
2215     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2216 #endif
2217   }
2218   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad,viewer,0,0,0));
2219   PetscFunctionReturn(0);
2220 }
2221 
2222 /*@
2223   DMPlexSectionLoad - Loads section into a DMPlex
2224 
2225   Collective on DM
2226 
2227   Input Parameters:
2228 + dm          - The DM that represents the topology
2229 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2230 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2231 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2232 
2233   Output Parameters
2234 + 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)
2235 - 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)
2236 
2237   Level: advanced
2238 
2239   Notes:
2240   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.
2241 
2242   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.
2243 
2244   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.
2245 
2246   Example using 2 processes:
2247 $  NX (number of points on dm): 4
2248 $  sectionA                   : the on-disk section
2249 $  vecA                       : a vector associated with sectionA
2250 $  sectionB                   : sectiondm's local section constructed in this function
2251 $  vecB (local)               : a vector associated with sectiondm's local section
2252 $  vecB (global)              : a vector associated with sectiondm's global section
2253 $
2254 $                                     rank 0    rank 1
2255 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2256 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2257 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2258 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2259 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2260 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2261 $  sectionB->atlasDof             :     1 0 1 | 1 3
2262 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2263 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2264 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2265 $
2266 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2267 
2268 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2269 @*/
2270 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2271 {
2272   PetscBool      ishdf5;
2273 
2274   PetscFunctionBegin;
2275   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2276   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2277   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2278   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2279   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2280   if (localDofSF) PetscValidPointer(localDofSF, 6);
2281   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2282   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad,viewer,0,0,0));
2283   if (ishdf5) {
2284 #if defined(PETSC_HAVE_HDF5)
2285     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2286 #else
2287     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2288 #endif
2289   }
2290   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad,viewer,0,0,0));
2291   PetscFunctionReturn(0);
2292 }
2293 
2294 /*@
2295   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2296 
2297   Collective on DM
2298 
2299   Input Parameters:
2300 + dm        - The DM that represents the topology
2301 . viewer    - The PetscViewer that represents the on-disk vector data
2302 . sectiondm - The DM that contains the global section on which vec is defined
2303 . sf        - The SF that migrates the on-disk vector data into vec
2304 - vec       - The global vector to set values of
2305 
2306   Level: advanced
2307 
2308   Notes:
2309   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.
2310 
2311   Typical calling sequence
2312 $       DMCreate(PETSC_COMM_WORLD, &dm);
2313 $       DMSetType(dm, DMPLEX);
2314 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2315 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2316 $       DMClone(dm, &sectiondm);
2317 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2318 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2319 $       DMGetGlobalVector(sectiondm, &vec);
2320 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2321 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2322 $       DMRestoreGlobalVector(sectiondm, &vec);
2323 $       PetscSFDestroy(&gsf);
2324 $       PetscSFDestroy(&sfX);
2325 $       DMDestroy(&sectiondm);
2326 $       DMDestroy(&dm);
2327 
2328 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2329 @*/
2330 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2331 {
2332   PetscBool       ishdf5;
2333 
2334   PetscFunctionBegin;
2335   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2336   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2337   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2338   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2339   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2340   /* Check consistency */
2341   {
2342     PetscSection  section;
2343     PetscBool     includesConstraints;
2344     PetscInt      m, m1;
2345 
2346     PetscCall(VecGetLocalSize(vec, &m1));
2347     PetscCall(DMGetGlobalSection(sectiondm, &section));
2348     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2349     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2350     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2351     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2352   }
2353   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2354   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2355   if (ishdf5) {
2356 #if defined(PETSC_HAVE_HDF5)
2357     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2358 #else
2359     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2360 #endif
2361   }
2362   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2363   PetscFunctionReturn(0);
2364 }
2365 
2366 /*@
2367   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2368 
2369   Collective on DM
2370 
2371   Input Parameters:
2372 + dm        - The DM that represents the topology
2373 . viewer    - The PetscViewer that represents the on-disk vector data
2374 . sectiondm - The DM that contains the local section on which vec is defined
2375 . sf        - The SF that migrates the on-disk vector data into vec
2376 - vec       - The local vector to set values of
2377 
2378   Level: advanced
2379 
2380   Notes:
2381   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.
2382 
2383   Typical calling sequence
2384 $       DMCreate(PETSC_COMM_WORLD, &dm);
2385 $       DMSetType(dm, DMPLEX);
2386 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2387 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2388 $       DMClone(dm, &sectiondm);
2389 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2390 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2391 $       DMGetLocalVector(sectiondm, &vec);
2392 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2393 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2394 $       DMRestoreLocalVector(sectiondm, &vec);
2395 $       PetscSFDestroy(&lsf);
2396 $       PetscSFDestroy(&sfX);
2397 $       DMDestroy(&sectiondm);
2398 $       DMDestroy(&dm);
2399 
2400 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2401 @*/
2402 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2403 {
2404   PetscBool       ishdf5;
2405 
2406   PetscFunctionBegin;
2407   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2408   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2409   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2410   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2411   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2412   /* Check consistency */
2413   {
2414     PetscSection  section;
2415     PetscBool     includesConstraints;
2416     PetscInt      m, m1;
2417 
2418     PetscCall(VecGetLocalSize(vec, &m1));
2419     PetscCall(DMGetLocalSection(sectiondm, &section));
2420     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2421     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2422     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2423     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2424   }
2425   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2426   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2427   if (ishdf5) {
2428 #if defined(PETSC_HAVE_HDF5)
2429     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2430 #else
2431     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2432 #endif
2433   }
2434   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2435   PetscFunctionReturn(0);
2436 }
2437 
2438 PetscErrorCode DMDestroy_Plex(DM dm)
2439 {
2440   DM_Plex       *mesh = (DM_Plex*) dm->data;
2441 
2442   PetscFunctionBegin;
2443   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL));
2444   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL));
2445   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL));
2446   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMInterpolateSolution_C", NULL));
2447   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2448   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C", NULL));
2449   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeGetDefault_C", NULL));
2450   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeSetDefault_C", NULL));
2451   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"MatComputeNeumannOverlap_C",NULL));
2452   if (--mesh->refct > 0) PetscFunctionReturn(0);
2453   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2454   PetscCall(PetscFree(mesh->cones));
2455   PetscCall(PetscFree(mesh->coneOrientations));
2456   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2457   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2458   PetscCall(PetscFree(mesh->supports));
2459   PetscCall(PetscFree(mesh->facesTmp));
2460   PetscCall(PetscFree(mesh->tetgenOpts));
2461   PetscCall(PetscFree(mesh->triangleOpts));
2462   PetscCall(PetscFree(mesh->transformType));
2463   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2464   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2465   PetscCall(ISDestroy(&mesh->subpointIS));
2466   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2467   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2468   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2469   PetscCall(ISDestroy(&mesh->anchorIS));
2470   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2471   PetscCall(PetscFree(mesh->parents));
2472   PetscCall(PetscFree(mesh->childIDs));
2473   PetscCall(PetscSectionDestroy(&mesh->childSection));
2474   PetscCall(PetscFree(mesh->children));
2475   PetscCall(DMDestroy(&mesh->referenceTree));
2476   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2477   PetscCall(PetscFree(mesh->neighbors));
2478   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2479   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2480   PetscCall(PetscFree(mesh));
2481   PetscFunctionReturn(0);
2482 }
2483 
2484 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2485 {
2486   PetscSection           sectionGlobal;
2487   PetscInt               bs = -1, mbs;
2488   PetscInt               localSize;
2489   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2490   MatType                mtype;
2491   ISLocalToGlobalMapping ltog;
2492 
2493   PetscFunctionBegin;
2494   PetscCall(MatInitializePackage());
2495   mtype = dm->mattype;
2496   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2497   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2498   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2499   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2500   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2501   PetscCall(MatSetType(*J, mtype));
2502   PetscCall(MatSetFromOptions(*J));
2503   PetscCall(MatGetBlockSize(*J, &mbs));
2504   if (mbs > 1) bs = mbs;
2505   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2506   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2507   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2508   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2509   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2510   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2511   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2512   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2513   if (!isShell) {
2514     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2515     PetscInt  *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2];
2516     PetscInt  pStart, pEnd, p, dof, cdof;
2517 
2518     PetscCall(DMGetLocalToGlobalMapping(dm,&ltog));
2519     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2520     for (p = pStart; p < pEnd; ++p) {
2521       PetscInt bdof;
2522 
2523       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2524       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2525       dof  = dof < 0 ? -(dof+1) : dof;
2526       bdof = cdof && (dof-cdof) ? 1 : dof;
2527       if (dof) {
2528         if (bs < 0)          {bs = bdof;}
2529         else if (bs != bdof) {bs = 1; break;}
2530       }
2531     }
2532     /* Must have same blocksize on all procs (some might have no points) */
2533     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2534     bsLocal[1] = bs;
2535     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
2536     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2537     else bs = bsMinMax[0];
2538     bs = PetscMax(1,bs);
2539     PetscCall(MatSetLocalToGlobalMapping(*J,ltog,ltog));
2540     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2541       PetscCall(MatSetBlockSize(*J, bs));
2542       PetscCall(MatSetUp(*J));
2543     } else {
2544       PetscCall(PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu));
2545       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2546       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2547     }
2548   }
2549   PetscCall(MatSetDM(*J, dm));
2550   PetscFunctionReturn(0);
2551 }
2552 
2553 /*@
2554   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2555 
2556   Not collective
2557 
2558   Input Parameter:
2559 . mesh - The DMPlex
2560 
2561   Output Parameters:
2562 . subsection - The subdomain section
2563 
2564   Level: developer
2565 
2566 .seealso:
2567 @*/
2568 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2569 {
2570   DM_Plex       *mesh = (DM_Plex*) dm->data;
2571 
2572   PetscFunctionBegin;
2573   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2574   if (!mesh->subdomainSection) {
2575     PetscSection section;
2576     PetscSF      sf;
2577 
2578     PetscCall(PetscSFCreate(PETSC_COMM_SELF,&sf));
2579     PetscCall(DMGetLocalSection(dm,&section));
2580     PetscCall(PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection));
2581     PetscCall(PetscSFDestroy(&sf));
2582   }
2583   *subsection = mesh->subdomainSection;
2584   PetscFunctionReturn(0);
2585 }
2586 
2587 /*@
2588   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2589 
2590   Not collective
2591 
2592   Input Parameter:
2593 . mesh - The DMPlex
2594 
2595   Output Parameters:
2596 + pStart - The first mesh point
2597 - pEnd   - The upper bound for mesh points
2598 
2599   Level: beginner
2600 
2601 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2602 @*/
2603 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2604 {
2605   DM_Plex       *mesh = (DM_Plex*) dm->data;
2606 
2607   PetscFunctionBegin;
2608   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2609   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2610   PetscFunctionReturn(0);
2611 }
2612 
2613 /*@
2614   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2615 
2616   Not collective
2617 
2618   Input Parameters:
2619 + mesh - The DMPlex
2620 . pStart - The first mesh point
2621 - pEnd   - The upper bound for mesh points
2622 
2623   Output Parameters:
2624 
2625   Level: beginner
2626 
2627 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2628 @*/
2629 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2630 {
2631   DM_Plex       *mesh = (DM_Plex*) dm->data;
2632 
2633   PetscFunctionBegin;
2634   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2635   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2636   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2637   PetscFunctionReturn(0);
2638 }
2639 
2640 /*@
2641   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2642 
2643   Not collective
2644 
2645   Input Parameters:
2646 + mesh - The DMPlex
2647 - p - The point, which must lie in the chart set with DMPlexSetChart()
2648 
2649   Output Parameter:
2650 . size - The cone size for point p
2651 
2652   Level: beginner
2653 
2654 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2655 @*/
2656 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2657 {
2658   DM_Plex       *mesh = (DM_Plex*) dm->data;
2659 
2660   PetscFunctionBegin;
2661   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2662   PetscValidIntPointer(size, 3);
2663   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2664   PetscFunctionReturn(0);
2665 }
2666 
2667 /*@
2668   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2669 
2670   Not collective
2671 
2672   Input Parameters:
2673 + mesh - The DMPlex
2674 . p - The point, which must lie in the chart set with DMPlexSetChart()
2675 - size - The cone size for point p
2676 
2677   Output Parameter:
2678 
2679   Note:
2680   This should be called after DMPlexSetChart().
2681 
2682   Level: beginner
2683 
2684 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2685 @*/
2686 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2687 {
2688   DM_Plex       *mesh = (DM_Plex*) dm->data;
2689 
2690   PetscFunctionBegin;
2691   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2692   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2693   PetscFunctionReturn(0);
2694 }
2695 
2696 /*@
2697   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2698 
2699   Not collective
2700 
2701   Input Parameters:
2702 + mesh - The DMPlex
2703 . p - The point, which must lie in the chart set with DMPlexSetChart()
2704 - size - The additional cone size for point p
2705 
2706   Output Parameter:
2707 
2708   Note:
2709   This should be called after DMPlexSetChart().
2710 
2711   Level: beginner
2712 
2713 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2714 @*/
2715 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2716 {
2717   DM_Plex       *mesh = (DM_Plex*) dm->data;
2718   PetscFunctionBegin;
2719   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2720   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2721   PetscFunctionReturn(0);
2722 }
2723 
2724 /*@C
2725   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2726 
2727   Not collective
2728 
2729   Input Parameters:
2730 + dm - The DMPlex
2731 - p - The point, which must lie in the chart set with DMPlexSetChart()
2732 
2733   Output Parameter:
2734 . cone - An array of points which are on the in-edges for point p
2735 
2736   Level: beginner
2737 
2738   Fortran Notes:
2739   Since it returns an array, this routine is only available in Fortran 90, and you must
2740   include petsc.h90 in your code.
2741   You must also call DMPlexRestoreCone() after you finish using the returned array.
2742   DMPlexRestoreCone() is not needed/available in C.
2743 
2744 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2745 @*/
2746 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2747 {
2748   DM_Plex       *mesh = (DM_Plex*) dm->data;
2749   PetscInt       off;
2750 
2751   PetscFunctionBegin;
2752   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2753   PetscValidPointer(cone, 3);
2754   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2755   *cone = &mesh->cones[off];
2756   PetscFunctionReturn(0);
2757 }
2758 
2759 /*@C
2760   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2761 
2762   Not collective
2763 
2764   Input Parameters:
2765 + dm - The DMPlex
2766 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2767 
2768   Output Parameters:
2769 + pConesSection - PetscSection describing the layout of pCones
2770 - pCones - An array of points which are on the in-edges for the point set p
2771 
2772   Level: intermediate
2773 
2774 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2775 @*/
2776 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2777 {
2778   PetscSection        cs, newcs;
2779   PetscInt            *cones;
2780   PetscInt            *newarr=NULL;
2781   PetscInt            n;
2782 
2783   PetscFunctionBegin;
2784   PetscCall(DMPlexGetCones(dm, &cones));
2785   PetscCall(DMPlexGetConeSection(dm, &cs));
2786   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL));
2787   if (pConesSection) *pConesSection = newcs;
2788   if (pCones) {
2789     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2790     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2791   }
2792   PetscFunctionReturn(0);
2793 }
2794 
2795 /*@
2796   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2797 
2798   Not collective
2799 
2800   Input Parameters:
2801 + dm - The DMPlex
2802 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2803 
2804   Output Parameter:
2805 . expandedPoints - An array of vertices recursively expanded from input points
2806 
2807   Level: advanced
2808 
2809   Notes:
2810   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2811   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2812 
2813 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2814 @*/
2815 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2816 {
2817   IS                  *expandedPointsAll;
2818   PetscInt            depth;
2819 
2820   PetscFunctionBegin;
2821   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2822   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2823   PetscValidPointer(expandedPoints, 3);
2824   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2825   *expandedPoints = expandedPointsAll[0];
2826   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2827   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2828   PetscFunctionReturn(0);
2829 }
2830 
2831 /*@
2832   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).
2833 
2834   Not collective
2835 
2836   Input Parameters:
2837 + dm - The DMPlex
2838 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2839 
2840   Output Parameters:
2841 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2842 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2843 - sections - (optional) An array of sections which describe mappings from points to their cone points
2844 
2845   Level: advanced
2846 
2847   Notes:
2848   Like DMPlexGetConeTuple() but recursive.
2849 
2850   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.
2851   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2852 
2853   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:
2854   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2855   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2856 
2857 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2858 @*/
2859 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2860 {
2861   const PetscInt      *arr0=NULL, *cone=NULL;
2862   PetscInt            *arr=NULL, *newarr=NULL;
2863   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
2864   IS                  *expandedPoints_;
2865   PetscSection        *sections_;
2866 
2867   PetscFunctionBegin;
2868   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2869   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2870   if (depth) PetscValidIntPointer(depth, 3);
2871   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2872   if (sections) PetscValidPointer(sections, 5);
2873   PetscCall(ISGetLocalSize(points, &n));
2874   PetscCall(ISGetIndices(points, &arr0));
2875   PetscCall(DMPlexGetDepth(dm, &depth_));
2876   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2877   PetscCall(PetscCalloc1(depth_, &sections_));
2878   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
2879   for (d=depth_-1; d>=0; d--) {
2880     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2881     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2882     for (i=0; i<n; i++) {
2883       PetscCall(DMPlexGetDepthStratum(dm, d+1, &start, &end));
2884       if (arr[i] >= start && arr[i] < end) {
2885         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2886         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2887       } else {
2888         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2889       }
2890     }
2891     PetscCall(PetscSectionSetUp(sections_[d]));
2892     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2893     PetscCall(PetscMalloc1(newn, &newarr));
2894     for (i=0; i<n; i++) {
2895       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2896       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2897       if (cn > 1) {
2898         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2899         PetscCall(PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt)));
2900       } else {
2901         newarr[co] = arr[i];
2902       }
2903     }
2904     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2905     arr = newarr;
2906     n = newn;
2907   }
2908   PetscCall(ISRestoreIndices(points, &arr0));
2909   *depth = depth_;
2910   if (expandedPoints) *expandedPoints = expandedPoints_;
2911   else {
2912     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2913     PetscCall(PetscFree(expandedPoints_));
2914   }
2915   if (sections) *sections = sections_;
2916   else {
2917     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2918     PetscCall(PetscFree(sections_));
2919   }
2920   PetscFunctionReturn(0);
2921 }
2922 
2923 /*@
2924   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2925 
2926   Not collective
2927 
2928   Input Parameters:
2929 + dm - The DMPlex
2930 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2931 
2932   Output Parameters:
2933 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2934 . expandedPoints - (optional) An array of recursively expanded cones
2935 - sections - (optional) An array of sections which describe mappings from points to their cone points
2936 
2937   Level: advanced
2938 
2939   Notes:
2940   See DMPlexGetConeRecursive() for details.
2941 
2942 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2943 @*/
2944 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2945 {
2946   PetscInt            d, depth_;
2947 
2948   PetscFunctionBegin;
2949   PetscCall(DMPlexGetDepth(dm, &depth_));
2950   PetscCheck(!depth || *depth == depth_,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2951   if (depth) *depth = 0;
2952   if (expandedPoints) {
2953     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2954     PetscCall(PetscFree(*expandedPoints));
2955   }
2956   if (sections)  {
2957     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2958     PetscCall(PetscFree(*sections));
2959   }
2960   PetscFunctionReturn(0);
2961 }
2962 
2963 /*@
2964   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
2965 
2966   Not collective
2967 
2968   Input Parameters:
2969 + mesh - The DMPlex
2970 . p - The point, which must lie in the chart set with DMPlexSetChart()
2971 - cone - An array of points which are on the in-edges for point p
2972 
2973   Output Parameter:
2974 
2975   Note:
2976   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
2977 
2978   Level: beginner
2979 
2980 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
2981 @*/
2982 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
2983 {
2984   DM_Plex       *mesh = (DM_Plex*) dm->data;
2985   PetscInt       pStart, pEnd;
2986   PetscInt       dof, off, c;
2987 
2988   PetscFunctionBegin;
2989   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2990   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
2991   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
2992   if (dof) PetscValidIntPointer(cone, 3);
2993   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2994   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);
2995   for (c = 0; c < dof; ++c) {
2996     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);
2997     mesh->cones[off+c] = cone[c];
2998   }
2999   PetscFunctionReturn(0);
3000 }
3001 
3002 /*@C
3003   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3004 
3005   Not collective
3006 
3007   Input Parameters:
3008 + mesh - The DMPlex
3009 - p - The point, which must lie in the chart set with DMPlexSetChart()
3010 
3011   Output Parameter:
3012 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3013                     integer giving the prescription for cone traversal.
3014 
3015   Level: beginner
3016 
3017   Notes:
3018   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3019   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3020   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3021   with the identity.
3022 
3023   Fortran Notes:
3024   Since it returns an array, this routine is only available in Fortran 90, and you must
3025   include petsc.h90 in your code.
3026   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3027   DMPlexRestoreConeOrientation() is not needed/available in C.
3028 
3029 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3030 @*/
3031 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3032 {
3033   DM_Plex       *mesh = (DM_Plex*) dm->data;
3034   PetscInt       off;
3035 
3036   PetscFunctionBegin;
3037   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3038   if (PetscDefined(USE_DEBUG)) {
3039     PetscInt dof;
3040     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3041     if (dof) PetscValidPointer(coneOrientation, 3);
3042   }
3043   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3044 
3045   *coneOrientation = &mesh->coneOrientations[off];
3046   PetscFunctionReturn(0);
3047 }
3048 
3049 /*@
3050   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3051 
3052   Not collective
3053 
3054   Input Parameters:
3055 + mesh - The DMPlex
3056 . p - The point, which must lie in the chart set with DMPlexSetChart()
3057 - coneOrientation - An array of orientations
3058   Output Parameter:
3059 
3060   Notes:
3061   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3062 
3063   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3064 
3065   Level: beginner
3066 
3067 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3068 @*/
3069 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3070 {
3071   DM_Plex       *mesh = (DM_Plex*) dm->data;
3072   PetscInt       pStart, pEnd;
3073   PetscInt       dof, off, c;
3074 
3075   PetscFunctionBegin;
3076   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3077   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3078   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3079   if (dof) PetscValidIntPointer(coneOrientation, 3);
3080   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3081   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);
3082   for (c = 0; c < dof; ++c) {
3083     PetscInt cdof, o = coneOrientation[c];
3084 
3085     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof));
3086     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);
3087     mesh->coneOrientations[off+c] = o;
3088   }
3089   PetscFunctionReturn(0);
3090 }
3091 
3092 /*@
3093   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3094 
3095   Not collective
3096 
3097   Input Parameters:
3098 + mesh - The DMPlex
3099 . p - The point, which must lie in the chart set with DMPlexSetChart()
3100 . conePos - The local index in the cone where the point should be put
3101 - conePoint - The mesh point to insert
3102 
3103   Level: beginner
3104 
3105 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3106 @*/
3107 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3108 {
3109   DM_Plex       *mesh = (DM_Plex*) dm->data;
3110   PetscInt       pStart, pEnd;
3111   PetscInt       dof, off;
3112 
3113   PetscFunctionBegin;
3114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3115   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3116   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);
3117   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);
3118   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3119   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3120   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);
3121   mesh->cones[off+conePos] = conePoint;
3122   PetscFunctionReturn(0);
3123 }
3124 
3125 /*@
3126   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3127 
3128   Not collective
3129 
3130   Input Parameters:
3131 + mesh - The DMPlex
3132 . p - The point, which must lie in the chart set with DMPlexSetChart()
3133 . conePos - The local index in the cone where the point should be put
3134 - coneOrientation - The point orientation to insert
3135 
3136   Level: beginner
3137 
3138   Notes:
3139   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3140 
3141 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3142 @*/
3143 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3144 {
3145   DM_Plex       *mesh = (DM_Plex*) dm->data;
3146   PetscInt       pStart, pEnd;
3147   PetscInt       dof, off;
3148 
3149   PetscFunctionBegin;
3150   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3151   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3152   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);
3153   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3154   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3155   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);
3156   mesh->coneOrientations[off+conePos] = coneOrientation;
3157   PetscFunctionReturn(0);
3158 }
3159 
3160 /*@
3161   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3162 
3163   Not collective
3164 
3165   Input Parameters:
3166 + mesh - The DMPlex
3167 - p - The point, which must lie in the chart set with DMPlexSetChart()
3168 
3169   Output Parameter:
3170 . size - The support size for point p
3171 
3172   Level: beginner
3173 
3174 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3175 @*/
3176 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3177 {
3178   DM_Plex       *mesh = (DM_Plex*) dm->data;
3179 
3180   PetscFunctionBegin;
3181   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3182   PetscValidIntPointer(size, 3);
3183   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3184   PetscFunctionReturn(0);
3185 }
3186 
3187 /*@
3188   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3189 
3190   Not collective
3191 
3192   Input Parameters:
3193 + mesh - The DMPlex
3194 . p - The point, which must lie in the chart set with DMPlexSetChart()
3195 - size - The support size for point p
3196 
3197   Output Parameter:
3198 
3199   Note:
3200   This should be called after DMPlexSetChart().
3201 
3202   Level: beginner
3203 
3204 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3205 @*/
3206 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3207 {
3208   DM_Plex       *mesh = (DM_Plex*) dm->data;
3209 
3210   PetscFunctionBegin;
3211   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3212   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3213   PetscFunctionReturn(0);
3214 }
3215 
3216 /*@C
3217   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3218 
3219   Not collective
3220 
3221   Input Parameters:
3222 + mesh - The DMPlex
3223 - p - The point, which must lie in the chart set with DMPlexSetChart()
3224 
3225   Output Parameter:
3226 . support - An array of points which are on the out-edges for point p
3227 
3228   Level: beginner
3229 
3230   Fortran Notes:
3231   Since it returns an array, this routine is only available in Fortran 90, and you must
3232   include petsc.h90 in your code.
3233   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3234   DMPlexRestoreSupport() is not needed/available in C.
3235 
3236 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3237 @*/
3238 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3239 {
3240   DM_Plex       *mesh = (DM_Plex*) dm->data;
3241   PetscInt       off;
3242 
3243   PetscFunctionBegin;
3244   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3245   PetscValidPointer(support, 3);
3246   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3247   *support = &mesh->supports[off];
3248   PetscFunctionReturn(0);
3249 }
3250 
3251 /*@
3252   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3253 
3254   Not collective
3255 
3256   Input Parameters:
3257 + mesh - The DMPlex
3258 . p - The point, which must lie in the chart set with DMPlexSetChart()
3259 - support - An array of points which are on the out-edges for point p
3260 
3261   Output Parameter:
3262 
3263   Note:
3264   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3265 
3266   Level: beginner
3267 
3268 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3269 @*/
3270 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3271 {
3272   DM_Plex       *mesh = (DM_Plex*) dm->data;
3273   PetscInt       pStart, pEnd;
3274   PetscInt       dof, off, c;
3275 
3276   PetscFunctionBegin;
3277   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3278   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3279   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3280   if (dof) PetscValidIntPointer(support, 3);
3281   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3282   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);
3283   for (c = 0; c < dof; ++c) {
3284     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);
3285     mesh->supports[off+c] = support[c];
3286   }
3287   PetscFunctionReturn(0);
3288 }
3289 
3290 /*@
3291   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3292 
3293   Not collective
3294 
3295   Input Parameters:
3296 + mesh - The DMPlex
3297 . p - The point, which must lie in the chart set with DMPlexSetChart()
3298 . supportPos - The local index in the cone where the point should be put
3299 - supportPoint - The mesh point to insert
3300 
3301   Level: beginner
3302 
3303 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3304 @*/
3305 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3306 {
3307   DM_Plex       *mesh = (DM_Plex*) dm->data;
3308   PetscInt       pStart, pEnd;
3309   PetscInt       dof, off;
3310 
3311   PetscFunctionBegin;
3312   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3313   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3314   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3315   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3316   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);
3317   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);
3318   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);
3319   mesh->supports[off+supportPos] = supportPoint;
3320   PetscFunctionReturn(0);
3321 }
3322 
3323 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3324 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3325 {
3326   switch (ct) {
3327     case DM_POLYTOPE_SEGMENT:
3328       if (o == -1) return -2;
3329       break;
3330     case DM_POLYTOPE_TRIANGLE:
3331       if (o == -3) return -1;
3332       if (o == -2) return -3;
3333       if (o == -1) return -2;
3334       break;
3335     case DM_POLYTOPE_QUADRILATERAL:
3336       if (o == -4) return -2;
3337       if (o == -3) return -1;
3338       if (o == -2) return -4;
3339       if (o == -1) return -3;
3340       break;
3341     default: return o;
3342   }
3343   return o;
3344 }
3345 
3346 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3347 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3348 {
3349   switch (ct) {
3350     case DM_POLYTOPE_SEGMENT:
3351       if ((o == -2) || (o == 1)) return -1;
3352       if (o == -1) return 0;
3353       break;
3354     case DM_POLYTOPE_TRIANGLE:
3355       if (o == -3) return -2;
3356       if (o == -2) return -1;
3357       if (o == -1) return -3;
3358       break;
3359     case DM_POLYTOPE_QUADRILATERAL:
3360       if (o == -4) return -2;
3361       if (o == -3) return -1;
3362       if (o == -2) return -4;
3363       if (o == -1) return -3;
3364       break;
3365     default: return o;
3366   }
3367   return o;
3368 }
3369 
3370 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3371 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3372 {
3373   PetscInt       pStart, pEnd, p;
3374 
3375   PetscFunctionBegin;
3376   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3377   for (p = pStart; p < pEnd; ++p) {
3378     const PetscInt *cone, *ornt;
3379     PetscInt        coneSize, c;
3380 
3381     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3382     PetscCall(DMPlexGetCone(dm, p, &cone));
3383     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3384     for (c = 0; c < coneSize; ++c) {
3385       DMPolytopeType ct;
3386       const PetscInt o = ornt[c];
3387 
3388       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3389       switch (ct) {
3390         case DM_POLYTOPE_SEGMENT:
3391           if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3392           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3393           break;
3394         case DM_POLYTOPE_TRIANGLE:
3395           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3396           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3397           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3398           break;
3399         case DM_POLYTOPE_QUADRILATERAL:
3400           if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3401           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3402           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3403           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3404           break;
3405         default: break;
3406       }
3407     }
3408   }
3409   PetscFunctionReturn(0);
3410 }
3411 
3412 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3413 {
3414   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3415   PetscInt       *closure;
3416   const PetscInt *tmp = NULL, *tmpO = NULL;
3417   PetscInt        off = 0, tmpSize, t;
3418 
3419   PetscFunctionBeginHot;
3420   if (ornt) {
3421     PetscCall(DMPlexGetCellType(dm, p, &ct));
3422     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3423   }
3424   if (*points) {
3425     closure = *points;
3426   } else {
3427     PetscInt maxConeSize, maxSupportSize;
3428     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3429     PetscCall(DMGetWorkArray(dm, 2*(PetscMax(maxConeSize, maxSupportSize)+1), MPIU_INT, &closure));
3430   }
3431   if (useCone) {
3432     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3433     PetscCall(DMPlexGetCone(dm, p, &tmp));
3434     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3435   } else {
3436     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3437     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3438   }
3439   if (ct == DM_POLYTOPE_UNKNOWN) {
3440     closure[off++] = p;
3441     closure[off++] = 0;
3442     for (t = 0; t < tmpSize; ++t) {
3443       closure[off++] = tmp[t];
3444       closure[off++] = tmpO ? tmpO[t] : 0;
3445     }
3446   } else {
3447     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3448 
3449     /* We assume that cells with a valid type have faces with a valid type */
3450     closure[off++] = p;
3451     closure[off++] = ornt;
3452     for (t = 0; t < tmpSize; ++t) {
3453       DMPolytopeType ft;
3454 
3455       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3456       closure[off++] = tmp[arr[t]];
3457       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3458     }
3459   }
3460   if (numPoints) *numPoints = tmpSize+1;
3461   if (points)    *points    = closure;
3462   PetscFunctionReturn(0);
3463 }
3464 
3465 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3466 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3467 {
3468   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3469   const PetscInt *cone, *ornt;
3470   PetscInt       *pts,  *closure = NULL;
3471   DMPolytopeType  ft;
3472   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3473   PetscInt        dim, coneSize, c, d, clSize, cl;
3474 
3475   PetscFunctionBeginHot;
3476   PetscCall(DMGetDimension(dm, &dim));
3477   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3478   PetscCall(DMPlexGetCone(dm, point, &cone));
3479   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3480   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3481   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    dim+1)-1)/(maxConeSize-1))    : dim+1;
3482   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim+1)-1)/(maxSupportSize-1)) : dim+1;
3483   maxSize       = PetscMax(coneSeries, supportSeries);
3484   if (*points) {pts  = *points;}
3485   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &pts));
3486   c    = 0;
3487   pts[c++] = point;
3488   pts[c++] = o;
3489   PetscCall(DMPlexGetCellType(dm, cone[arr[0*2+0]], &ft));
3490   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[0*2+1], ornt[0]), useCone, &clSize, &closure));
3491   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3492   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[1*2+1], ornt[1]), useCone, &clSize, &closure));
3493   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3494   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3495   for (d = 2; d < coneSize; ++d) {
3496     PetscCall(DMPlexGetCellType(dm, cone[arr[d*2+0]], &ft));
3497     pts[c++] = cone[arr[d*2+0]];
3498     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]);
3499   }
3500   if (dim >= 3) {
3501     for (d = 2; d < coneSize; ++d) {
3502       const PetscInt  fpoint = cone[arr[d*2+0]];
3503       const PetscInt *fcone, *fornt;
3504       PetscInt        fconeSize, fc, i;
3505 
3506       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3507       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]));
3508       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3509       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3510       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3511       for (fc = 0; fc < fconeSize; ++fc) {
3512         const PetscInt cp = fcone[farr[fc*2+0]];
3513         const PetscInt co = farr[fc*2+1];
3514 
3515         for (i = 0; i < c; i += 2) if (pts[i] == cp) break;
3516         if (i == c) {
3517           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3518           pts[c++] = cp;
3519           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc*2+0]]);
3520         }
3521       }
3522     }
3523   }
3524   *numPoints = c/2;
3525   *points    = pts;
3526   PetscFunctionReturn(0);
3527 }
3528 
3529 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3530 {
3531   DMPolytopeType ct;
3532   PetscInt      *closure, *fifo;
3533   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3534   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3535   PetscInt       depth, maxSize;
3536 
3537   PetscFunctionBeginHot;
3538   PetscCall(DMPlexGetDepth(dm, &depth));
3539   if (depth == 1) {
3540     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3541     PetscFunctionReturn(0);
3542   }
3543   PetscCall(DMPlexGetCellType(dm, p, &ct));
3544   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3545   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3546     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3547     PetscFunctionReturn(0);
3548   }
3549   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3550   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    depth+1)-1)/(maxConeSize-1))    : depth+1;
3551   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth+1)-1)/(maxSupportSize-1)) : depth+1;
3552   maxSize       = PetscMax(coneSeries, supportSeries);
3553   PetscCall(DMGetWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3554   if (*points) {closure = *points;}
3555   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &closure));
3556   closure[closureSize++] = p;
3557   closure[closureSize++] = ornt;
3558   fifo[fifoSize++]       = p;
3559   fifo[fifoSize++]       = ornt;
3560   fifo[fifoSize++]       = ct;
3561   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3562   while (fifoSize - fifoStart) {
3563     const PetscInt       q    = fifo[fifoStart++];
3564     const PetscInt       o    = fifo[fifoStart++];
3565     const DMPolytopeType qt   = (DMPolytopeType) fifo[fifoStart++];
3566     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3567     const PetscInt      *tmp, *tmpO;
3568     PetscInt             tmpSize, t;
3569 
3570     if (PetscDefined(USE_DEBUG)) {
3571       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt)/2;
3572       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);
3573     }
3574     if (useCone) {
3575       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3576       PetscCall(DMPlexGetCone(dm, q, &tmp));
3577       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3578     } else {
3579       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3580       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3581       tmpO = NULL;
3582     }
3583     for (t = 0; t < tmpSize; ++t) {
3584       const PetscInt ip = useCone && qarr ? qarr[t*2]   : t;
3585       const PetscInt io = useCone && qarr ? qarr[t*2+1] : 0;
3586       const PetscInt cp = tmp[ip];
3587       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3588       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3589       PetscInt       c;
3590 
3591       /* Check for duplicate */
3592       for (c = 0; c < closureSize; c += 2) {
3593         if (closure[c] == cp) break;
3594       }
3595       if (c == closureSize) {
3596         closure[closureSize++] = cp;
3597         closure[closureSize++] = co;
3598         fifo[fifoSize++]       = cp;
3599         fifo[fifoSize++]       = co;
3600         fifo[fifoSize++]       = ct;
3601       }
3602     }
3603   }
3604   PetscCall(DMRestoreWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3605   if (numPoints) *numPoints = closureSize/2;
3606   if (points)    *points    = closure;
3607   PetscFunctionReturn(0);
3608 }
3609 
3610 /*@C
3611   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3612 
3613   Not collective
3614 
3615   Input Parameters:
3616 + dm      - The DMPlex
3617 . p       - The mesh point
3618 - useCone - PETSC_TRUE for the closure, otherwise return the star
3619 
3620   Input/Output Parameter:
3621 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3622            if NULL on input, internal storage will be returned, otherwise the provided array is used
3623 
3624   Output Parameter:
3625 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3626 
3627   Note:
3628   If using internal storage (points is NULL on input), each call overwrites the last output.
3629 
3630   Fortran Notes:
3631   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3632 
3633   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3634 
3635   Level: beginner
3636 
3637 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3638 @*/
3639 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3640 {
3641   PetscFunctionBeginHot;
3642   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3643   if (numPoints) PetscValidIntPointer(numPoints, 4);
3644   if (points)    PetscValidPointer(points, 5);
3645   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3646   PetscFunctionReturn(0);
3647 }
3648 
3649 /*@C
3650   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3651 
3652   Not collective
3653 
3654   Input Parameters:
3655 + dm        - The DMPlex
3656 . p         - The mesh point
3657 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3658 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3659 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3660 
3661   Note:
3662   If not using internal storage (points is not NULL on input), this call is unnecessary
3663 
3664   Fortran Notes:
3665   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3666 
3667   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3668 
3669   Level: beginner
3670 
3671 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3672 @*/
3673 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3674 {
3675   PetscFunctionBeginHot;
3676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3677   if (numPoints) *numPoints = 0;
3678   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3679   PetscFunctionReturn(0);
3680 }
3681 
3682 /*@
3683   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3684 
3685   Not collective
3686 
3687   Input Parameter:
3688 . mesh - The DMPlex
3689 
3690   Output Parameters:
3691 + maxConeSize - The maximum number of in-edges
3692 - maxSupportSize - The maximum number of out-edges
3693 
3694   Level: beginner
3695 
3696 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3697 @*/
3698 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3699 {
3700   DM_Plex *mesh = (DM_Plex*) dm->data;
3701 
3702   PetscFunctionBegin;
3703   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3704   if (maxConeSize) {
3705     PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3706   }
3707   if (maxSupportSize) {
3708     PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3709   }
3710   PetscFunctionReturn(0);
3711 }
3712 
3713 PetscErrorCode DMSetUp_Plex(DM dm)
3714 {
3715   DM_Plex       *mesh = (DM_Plex*) dm->data;
3716   PetscInt       size, maxSupportSize;
3717 
3718   PetscFunctionBegin;
3719   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3720   PetscCall(PetscSectionSetUp(mesh->coneSection));
3721   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3722   PetscCall(PetscMalloc1(size, &mesh->cones));
3723   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3724   PetscCall(PetscLogObjectMemory((PetscObject) dm, size*2*sizeof(PetscInt)));
3725   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3726   if (maxSupportSize) {
3727     PetscCall(PetscSectionSetUp(mesh->supportSection));
3728     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3729     PetscCall(PetscMalloc1(size, &mesh->supports));
3730     PetscCall(PetscLogObjectMemory((PetscObject) dm, size*sizeof(PetscInt)));
3731   }
3732   PetscFunctionReturn(0);
3733 }
3734 
3735 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3736 {
3737   PetscFunctionBegin;
3738   if (subdm) PetscCall(DMClone(dm, subdm));
3739   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3740   if (subdm) {(*subdm)->useNatural = dm->useNatural;}
3741   if (dm->useNatural && dm->sfMigration) {
3742     PetscSF        sfMigrationInv,sfNatural;
3743     PetscSection   section, sectionSeq;
3744 
3745     (*subdm)->sfMigration = dm->sfMigration;
3746     PetscCall(PetscObjectReference((PetscObject) dm->sfMigration));
3747     PetscCall(DMGetLocalSection((*subdm), &section));
3748     PetscCall(PetscSFCreateInverseSF((*subdm)->sfMigration, &sfMigrationInv));
3749     PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*subdm)), &sectionSeq));
3750     PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3751 
3752     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, sectionSeq, (*subdm)->sfMigration, &sfNatural));
3753     (*subdm)->sfNatural = sfNatural;
3754     PetscCall(PetscSectionDestroy(&sectionSeq));
3755     PetscCall(PetscSFDestroy(&sfMigrationInv));
3756   }
3757   PetscFunctionReturn(0);
3758 }
3759 
3760 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3761 {
3762   PetscInt       i = 0;
3763 
3764   PetscFunctionBegin;
3765   PetscCall(DMClone(dms[0], superdm));
3766   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3767   (*superdm)->useNatural = PETSC_FALSE;
3768   for (i = 0; i < len; i++) {
3769     if (dms[i]->useNatural && dms[i]->sfMigration) {
3770       PetscSF        sfMigrationInv,sfNatural;
3771       PetscSection   section, sectionSeq;
3772 
3773       (*superdm)->sfMigration = dms[i]->sfMigration;
3774       PetscCall(PetscObjectReference((PetscObject) dms[i]->sfMigration));
3775       (*superdm)->useNatural = PETSC_TRUE;
3776       PetscCall(DMGetLocalSection((*superdm), &section));
3777       PetscCall(PetscSFCreateInverseSF((*superdm)->sfMigration, &sfMigrationInv));
3778       PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*superdm)), &sectionSeq));
3779       PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3780 
3781       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, sectionSeq, (*superdm)->sfMigration, &sfNatural));
3782       (*superdm)->sfNatural = sfNatural;
3783       PetscCall(PetscSectionDestroy(&sectionSeq));
3784       PetscCall(PetscSFDestroy(&sfMigrationInv));
3785       break;
3786     }
3787   }
3788   PetscFunctionReturn(0);
3789 }
3790 
3791 /*@
3792   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3793 
3794   Not collective
3795 
3796   Input Parameter:
3797 . mesh - The DMPlex
3798 
3799   Output Parameter:
3800 
3801   Note:
3802   This should be called after all calls to DMPlexSetCone()
3803 
3804   Level: beginner
3805 
3806 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3807 @*/
3808 PetscErrorCode DMPlexSymmetrize(DM dm)
3809 {
3810   DM_Plex       *mesh = (DM_Plex*) dm->data;
3811   PetscInt      *offsets;
3812   PetscInt       supportSize;
3813   PetscInt       pStart, pEnd, p;
3814 
3815   PetscFunctionBegin;
3816   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3817   PetscCheck(!mesh->supports,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3818   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0));
3819   /* Calculate support sizes */
3820   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3821   for (p = pStart; p < pEnd; ++p) {
3822     PetscInt dof, off, c;
3823 
3824     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3825     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3826     for (c = off; c < off+dof; ++c) {
3827       PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3828     }
3829   }
3830   PetscCall(PetscSectionSetUp(mesh->supportSection));
3831   /* Calculate supports */
3832   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3833   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3834   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3835   for (p = pStart; p < pEnd; ++p) {
3836     PetscInt dof, off, c;
3837 
3838     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3839     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3840     for (c = off; c < off+dof; ++c) {
3841       const PetscInt q = mesh->cones[c];
3842       PetscInt       offS;
3843 
3844       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3845 
3846       mesh->supports[offS+offsets[q]] = p;
3847       ++offsets[q];
3848     }
3849   }
3850   PetscCall(PetscFree(offsets));
3851   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0));
3852   PetscFunctionReturn(0);
3853 }
3854 
3855 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3856 {
3857   IS             stratumIS;
3858 
3859   PetscFunctionBegin;
3860   if (pStart >= pEnd) PetscFunctionReturn(0);
3861   if (PetscDefined(USE_DEBUG)) {
3862     PetscInt  qStart, qEnd, numLevels, level;
3863     PetscBool overlap = PETSC_FALSE;
3864     PetscCall(DMLabelGetNumValues(label, &numLevels));
3865     for (level = 0; level < numLevels; level++) {
3866       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3867       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
3868     }
3869     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);
3870   }
3871   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS));
3872   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3873   PetscCall(ISDestroy(&stratumIS));
3874   PetscFunctionReturn(0);
3875 }
3876 
3877 /*@
3878   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3879   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3880   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3881   the DAG.
3882 
3883   Collective on dm
3884 
3885   Input Parameter:
3886 . mesh - The DMPlex
3887 
3888   Output Parameter:
3889 
3890   Notes:
3891   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3892   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3893   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3894   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3895   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3896 
3897   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3898   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3899   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
3900   to interpolate only that one (e0), so that
3901 $  cone(c0) = {e0, v2}
3902 $  cone(e0) = {v0, v1}
3903   If DMPlexStratify() is run on this mesh, it will give depths
3904 $  depth 0 = {v0, v1, v2}
3905 $  depth 1 = {e0, c0}
3906   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3907 
3908   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3909 
3910   Level: beginner
3911 
3912 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3913 @*/
3914 PetscErrorCode DMPlexStratify(DM dm)
3915 {
3916   DM_Plex       *mesh = (DM_Plex*) dm->data;
3917   DMLabel        label;
3918   PetscInt       pStart, pEnd, p;
3919   PetscInt       numRoots = 0, numLeaves = 0;
3920 
3921   PetscFunctionBegin;
3922   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3923   PetscCall(PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0));
3924 
3925   /* Create depth label */
3926   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3927   PetscCall(DMCreateLabel(dm, "depth"));
3928   PetscCall(DMPlexGetDepthLabel(dm, &label));
3929 
3930   {
3931     /* Initialize roots and count leaves */
3932     PetscInt sMin = PETSC_MAX_INT;
3933     PetscInt sMax = PETSC_MIN_INT;
3934     PetscInt coneSize, supportSize;
3935 
3936     for (p = pStart; p < pEnd; ++p) {
3937       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3938       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3939       if (!coneSize && supportSize) {
3940         sMin = PetscMin(p, sMin);
3941         sMax = PetscMax(p, sMax);
3942         ++numRoots;
3943       } else if (!supportSize && coneSize) {
3944         ++numLeaves;
3945       } else if (!supportSize && !coneSize) {
3946         /* Isolated points */
3947         sMin = PetscMin(p, sMin);
3948         sMax = PetscMax(p, sMax);
3949       }
3950     }
3951     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1));
3952   }
3953 
3954   if (numRoots + numLeaves == (pEnd - pStart)) {
3955     PetscInt sMin = PETSC_MAX_INT;
3956     PetscInt sMax = PETSC_MIN_INT;
3957     PetscInt coneSize, supportSize;
3958 
3959     for (p = pStart; p < pEnd; ++p) {
3960       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3961       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3962       if (!supportSize && coneSize) {
3963         sMin = PetscMin(p, sMin);
3964         sMax = PetscMax(p, sMax);
3965       }
3966     }
3967     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1));
3968   } else {
3969     PetscInt level = 0;
3970     PetscInt qStart, qEnd, q;
3971 
3972     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3973     while (qEnd > qStart) {
3974       PetscInt sMin = PETSC_MAX_INT;
3975       PetscInt sMax = PETSC_MIN_INT;
3976 
3977       for (q = qStart; q < qEnd; ++q) {
3978         const PetscInt *support;
3979         PetscInt        supportSize, s;
3980 
3981         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
3982         PetscCall(DMPlexGetSupport(dm, q, &support));
3983         for (s = 0; s < supportSize; ++s) {
3984           sMin = PetscMin(support[s], sMin);
3985           sMax = PetscMax(support[s], sMax);
3986         }
3987       }
3988       PetscCall(DMLabelGetNumValues(label, &level));
3989       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1));
3990       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3991     }
3992   }
3993   { /* just in case there is an empty process */
3994     PetscInt numValues, maxValues = 0, v;
3995 
3996     PetscCall(DMLabelGetNumValues(label, &numValues));
3997     PetscCallMPI(MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm)));
3998     for (v = numValues; v < maxValues; v++) {
3999       PetscCall(DMLabelAddStratum(label, v));
4000     }
4001   }
4002   PetscCall(PetscObjectStateGet((PetscObject) label, &mesh->depthState));
4003   PetscCall(PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0));
4004   PetscFunctionReturn(0);
4005 }
4006 
4007 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4008 {
4009   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4010   PetscInt       dim, depth, pheight, coneSize;
4011 
4012   PetscFunctionBeginHot;
4013   PetscCall(DMGetDimension(dm, &dim));
4014   PetscCall(DMPlexGetDepth(dm, &depth));
4015   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4016   pheight = depth - pdepth;
4017   if (depth <= 1) {
4018     switch (pdepth) {
4019       case 0: ct = DM_POLYTOPE_POINT;break;
4020       case 1:
4021         switch (coneSize) {
4022           case 2: ct = DM_POLYTOPE_SEGMENT;break;
4023           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4024           case 4:
4025           switch (dim) {
4026             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
4027             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
4028             default: break;
4029           }
4030           break;
4031         case 5: ct = DM_POLYTOPE_PYRAMID;break;
4032         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4033         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
4034         default: break;
4035       }
4036     }
4037   } else {
4038     if (pdepth == 0) {
4039       ct = DM_POLYTOPE_POINT;
4040     } else if (pheight == 0) {
4041       switch (dim) {
4042         case 1:
4043           switch (coneSize) {
4044             case 2: ct = DM_POLYTOPE_SEGMENT;break;
4045             default: break;
4046           }
4047           break;
4048         case 2:
4049           switch (coneSize) {
4050             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4051             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4052             default: break;
4053           }
4054           break;
4055         case 3:
4056           switch (coneSize) {
4057             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
4058             case 5:
4059             {
4060               const PetscInt *cone;
4061               PetscInt        faceConeSize;
4062 
4063               PetscCall(DMPlexGetCone(dm, p, &cone));
4064               PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4065               switch (faceConeSize) {
4066                 case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4067                 case 4: ct = DM_POLYTOPE_PYRAMID;break;
4068               }
4069             }
4070             break;
4071             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
4072             default: break;
4073           }
4074           break;
4075         default: break;
4076       }
4077     } else if (pheight > 0) {
4078       switch (coneSize) {
4079         case 2: ct = DM_POLYTOPE_SEGMENT;break;
4080         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4081         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4082         default: break;
4083       }
4084     }
4085   }
4086   *pt = ct;
4087   PetscFunctionReturn(0);
4088 }
4089 
4090 /*@
4091   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4092 
4093   Collective on dm
4094 
4095   Input Parameter:
4096 . mesh - The DMPlex
4097 
4098   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4099 
4100   Level: developer
4101 
4102   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4103   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4104   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4105 
4106 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4107 @*/
4108 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4109 {
4110   DM_Plex       *mesh;
4111   DMLabel        ctLabel;
4112   PetscInt       pStart, pEnd, p;
4113 
4114   PetscFunctionBegin;
4115   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4116   mesh = (DM_Plex *) dm->data;
4117   PetscCall(DMCreateLabel(dm, "celltype"));
4118   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4119   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4120   for (p = pStart; p < pEnd; ++p) {
4121     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4122     PetscInt       pdepth;
4123 
4124     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4125     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4126     PetscCheck(ct != DM_POLYTOPE_UNKNOWN,PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4127     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4128   }
4129   PetscCall(PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState));
4130   PetscCall(PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view"));
4131   PetscFunctionReturn(0);
4132 }
4133 
4134 /*@C
4135   DMPlexGetJoin - Get an array for the join of the set of points
4136 
4137   Not Collective
4138 
4139   Input Parameters:
4140 + dm - The DMPlex object
4141 . numPoints - The number of input points for the join
4142 - points - The input points
4143 
4144   Output Parameters:
4145 + numCoveredPoints - The number of points in the join
4146 - coveredPoints - The points in the join
4147 
4148   Level: intermediate
4149 
4150   Note: Currently, this is restricted to a single level join
4151 
4152   Fortran Notes:
4153   Since it returns an array, this routine is only available in Fortran 90, and you must
4154   include petsc.h90 in your code.
4155 
4156   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4157 
4158 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4159 @*/
4160 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4161 {
4162   DM_Plex       *mesh = (DM_Plex*) dm->data;
4163   PetscInt      *join[2];
4164   PetscInt       joinSize, i = 0;
4165   PetscInt       dof, off, p, c, m;
4166   PetscInt       maxSupportSize;
4167 
4168   PetscFunctionBegin;
4169   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4170   PetscValidIntPointer(points, 3);
4171   PetscValidIntPointer(numCoveredPoints, 4);
4172   PetscValidPointer(coveredPoints, 5);
4173   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4174   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4175   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4176   /* Copy in support of first point */
4177   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4178   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4179   for (joinSize = 0; joinSize < dof; ++joinSize) {
4180     join[i][joinSize] = mesh->supports[off+joinSize];
4181   }
4182   /* Check each successive support */
4183   for (p = 1; p < numPoints; ++p) {
4184     PetscInt newJoinSize = 0;
4185 
4186     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4187     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4188     for (c = 0; c < dof; ++c) {
4189       const PetscInt point = mesh->supports[off+c];
4190 
4191       for (m = 0; m < joinSize; ++m) {
4192         if (point == join[i][m]) {
4193           join[1-i][newJoinSize++] = point;
4194           break;
4195         }
4196       }
4197     }
4198     joinSize = newJoinSize;
4199     i        = 1-i;
4200   }
4201   *numCoveredPoints = joinSize;
4202   *coveredPoints    = join[i];
4203   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1-i]));
4204   PetscFunctionReturn(0);
4205 }
4206 
4207 /*@C
4208   DMPlexRestoreJoin - Restore an array for the join of the set of points
4209 
4210   Not Collective
4211 
4212   Input Parameters:
4213 + dm - The DMPlex object
4214 . numPoints - The number of input points for the join
4215 - points - The input points
4216 
4217   Output Parameters:
4218 + numCoveredPoints - The number of points in the join
4219 - coveredPoints - The points in the join
4220 
4221   Fortran Notes:
4222   Since it returns an array, this routine is only available in Fortran 90, and you must
4223   include petsc.h90 in your code.
4224 
4225   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4226 
4227   Level: intermediate
4228 
4229 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4230 @*/
4231 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4232 {
4233   PetscFunctionBegin;
4234   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4235   if (points) PetscValidIntPointer(points,3);
4236   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4237   PetscValidPointer(coveredPoints, 5);
4238   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4239   if (numCoveredPoints) *numCoveredPoints = 0;
4240   PetscFunctionReturn(0);
4241 }
4242 
4243 /*@C
4244   DMPlexGetFullJoin - Get an array for the join of the set of points
4245 
4246   Not Collective
4247 
4248   Input Parameters:
4249 + dm - The DMPlex object
4250 . numPoints - The number of input points for the join
4251 - points - The input points
4252 
4253   Output Parameters:
4254 + numCoveredPoints - The number of points in the join
4255 - coveredPoints - The points in the join
4256 
4257   Fortran Notes:
4258   Since it returns an array, this routine is only available in Fortran 90, and you must
4259   include petsc.h90 in your code.
4260 
4261   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4262 
4263   Level: intermediate
4264 
4265 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4266 @*/
4267 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4268 {
4269   PetscInt      *offsets, **closures;
4270   PetscInt      *join[2];
4271   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
4272   PetscInt       p, d, c, m, ms;
4273 
4274   PetscFunctionBegin;
4275   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4276   PetscValidIntPointer(points, 3);
4277   PetscValidIntPointer(numCoveredPoints, 4);
4278   PetscValidPointer(coveredPoints, 5);
4279 
4280   PetscCall(DMPlexGetDepth(dm, &depth));
4281   PetscCall(PetscCalloc1(numPoints, &closures));
4282   PetscCall(DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4283   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4284   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
4285   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4286   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4287 
4288   for (p = 0; p < numPoints; ++p) {
4289     PetscInt closureSize;
4290 
4291     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4292 
4293     offsets[p*(depth+2)+0] = 0;
4294     for (d = 0; d < depth+1; ++d) {
4295       PetscInt pStart, pEnd, i;
4296 
4297       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4298       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
4299         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4300           offsets[p*(depth+2)+d+1] = i;
4301           break;
4302         }
4303       }
4304       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
4305     }
4306     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);
4307   }
4308   for (d = 0; d < depth+1; ++d) {
4309     PetscInt dof;
4310 
4311     /* Copy in support of first point */
4312     dof = offsets[d+1] - offsets[d];
4313     for (joinSize = 0; joinSize < dof; ++joinSize) {
4314       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
4315     }
4316     /* Check each successive cone */
4317     for (p = 1; p < numPoints && joinSize; ++p) {
4318       PetscInt newJoinSize = 0;
4319 
4320       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
4321       for (c = 0; c < dof; ++c) {
4322         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
4323 
4324         for (m = 0; m < joinSize; ++m) {
4325           if (point == join[i][m]) {
4326             join[1-i][newJoinSize++] = point;
4327             break;
4328           }
4329         }
4330       }
4331       joinSize = newJoinSize;
4332       i        = 1-i;
4333     }
4334     if (joinSize) break;
4335   }
4336   *numCoveredPoints = joinSize;
4337   *coveredPoints    = join[i];
4338   for (p = 0; p < numPoints; ++p) {
4339     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4340   }
4341   PetscCall(PetscFree(closures));
4342   PetscCall(DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4343   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1-i]));
4344   PetscFunctionReturn(0);
4345 }
4346 
4347 /*@C
4348   DMPlexGetMeet - Get an array for the meet of the set of points
4349 
4350   Not Collective
4351 
4352   Input Parameters:
4353 + dm - The DMPlex object
4354 . numPoints - The number of input points for the meet
4355 - points - The input points
4356 
4357   Output Parameters:
4358 + numCoveredPoints - The number of points in the meet
4359 - coveredPoints - The points in the meet
4360 
4361   Level: intermediate
4362 
4363   Note: Currently, this is restricted to a single level meet
4364 
4365   Fortran Notes:
4366   Since it returns an array, this routine is only available in Fortran 90, and you must
4367   include petsc.h90 in your code.
4368 
4369   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4370 
4371 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4372 @*/
4373 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4374 {
4375   DM_Plex       *mesh = (DM_Plex*) dm->data;
4376   PetscInt      *meet[2];
4377   PetscInt       meetSize, i = 0;
4378   PetscInt       dof, off, p, c, m;
4379   PetscInt       maxConeSize;
4380 
4381   PetscFunctionBegin;
4382   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4383   PetscValidIntPointer(points, 3);
4384   PetscValidIntPointer(numCoveringPoints, 4);
4385   PetscValidPointer(coveringPoints, 5);
4386   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4387   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4388   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4389   /* Copy in cone of first point */
4390   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4391   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4392   for (meetSize = 0; meetSize < dof; ++meetSize) {
4393     meet[i][meetSize] = mesh->cones[off+meetSize];
4394   }
4395   /* Check each successive cone */
4396   for (p = 1; p < numPoints; ++p) {
4397     PetscInt newMeetSize = 0;
4398 
4399     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4400     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4401     for (c = 0; c < dof; ++c) {
4402       const PetscInt point = mesh->cones[off+c];
4403 
4404       for (m = 0; m < meetSize; ++m) {
4405         if (point == meet[i][m]) {
4406           meet[1-i][newMeetSize++] = point;
4407           break;
4408         }
4409       }
4410     }
4411     meetSize = newMeetSize;
4412     i        = 1-i;
4413   }
4414   *numCoveringPoints = meetSize;
4415   *coveringPoints    = meet[i];
4416   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1-i]));
4417   PetscFunctionReturn(0);
4418 }
4419 
4420 /*@C
4421   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4422 
4423   Not Collective
4424 
4425   Input Parameters:
4426 + dm - The DMPlex object
4427 . numPoints - The number of input points for the meet
4428 - points - The input points
4429 
4430   Output Parameters:
4431 + numCoveredPoints - The number of points in the meet
4432 - coveredPoints - The points in the meet
4433 
4434   Level: intermediate
4435 
4436   Fortran Notes:
4437   Since it returns an array, this routine is only available in Fortran 90, and you must
4438   include petsc.h90 in your code.
4439 
4440   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4441 
4442 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4443 @*/
4444 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4445 {
4446   PetscFunctionBegin;
4447   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4448   if (points) PetscValidIntPointer(points,3);
4449   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4450   PetscValidPointer(coveredPoints,5);
4451   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4452   if (numCoveredPoints) *numCoveredPoints = 0;
4453   PetscFunctionReturn(0);
4454 }
4455 
4456 /*@C
4457   DMPlexGetFullMeet - Get an array for the meet of the set of points
4458 
4459   Not Collective
4460 
4461   Input Parameters:
4462 + dm - The DMPlex object
4463 . numPoints - The number of input points for the meet
4464 - points - The input points
4465 
4466   Output Parameters:
4467 + numCoveredPoints - The number of points in the meet
4468 - coveredPoints - The points in the meet
4469 
4470   Level: intermediate
4471 
4472   Fortran Notes:
4473   Since it returns an array, this routine is only available in Fortran 90, and you must
4474   include petsc.h90 in your code.
4475 
4476   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4477 
4478 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4479 @*/
4480 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4481 {
4482   PetscInt      *offsets, **closures;
4483   PetscInt      *meet[2];
4484   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
4485   PetscInt       p, h, c, m, mc;
4486 
4487   PetscFunctionBegin;
4488   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4489   PetscValidIntPointer(points, 3);
4490   PetscValidIntPointer(numCoveredPoints, 4);
4491   PetscValidPointer(coveredPoints, 5);
4492 
4493   PetscCall(DMPlexGetDepth(dm, &height));
4494   PetscCall(PetscMalloc1(numPoints, &closures));
4495   PetscCall(DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4496   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4497   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
4498   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4499   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4500 
4501   for (p = 0; p < numPoints; ++p) {
4502     PetscInt closureSize;
4503 
4504     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4505 
4506     offsets[p*(height+2)+0] = 0;
4507     for (h = 0; h < height+1; ++h) {
4508       PetscInt pStart, pEnd, i;
4509 
4510       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4511       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
4512         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4513           offsets[p*(height+2)+h+1] = i;
4514           break;
4515         }
4516       }
4517       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
4518     }
4519     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);
4520   }
4521   for (h = 0; h < height+1; ++h) {
4522     PetscInt dof;
4523 
4524     /* Copy in cone of first point */
4525     dof = offsets[h+1] - offsets[h];
4526     for (meetSize = 0; meetSize < dof; ++meetSize) {
4527       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
4528     }
4529     /* Check each successive cone */
4530     for (p = 1; p < numPoints && meetSize; ++p) {
4531       PetscInt newMeetSize = 0;
4532 
4533       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
4534       for (c = 0; c < dof; ++c) {
4535         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
4536 
4537         for (m = 0; m < meetSize; ++m) {
4538           if (point == meet[i][m]) {
4539             meet[1-i][newMeetSize++] = point;
4540             break;
4541           }
4542         }
4543       }
4544       meetSize = newMeetSize;
4545       i        = 1-i;
4546     }
4547     if (meetSize) break;
4548   }
4549   *numCoveredPoints = meetSize;
4550   *coveredPoints    = meet[i];
4551   for (p = 0; p < numPoints; ++p) {
4552     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4553   }
4554   PetscCall(PetscFree(closures));
4555   PetscCall(DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4556   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1-i]));
4557   PetscFunctionReturn(0);
4558 }
4559 
4560 /*@C
4561   DMPlexEqual - Determine if two DMs have the same topology
4562 
4563   Not Collective
4564 
4565   Input Parameters:
4566 + dmA - A DMPlex object
4567 - dmB - A DMPlex object
4568 
4569   Output Parameters:
4570 . equal - PETSC_TRUE if the topologies are identical
4571 
4572   Level: intermediate
4573 
4574   Notes:
4575   We are not solving graph isomorphism, so we do not permutation.
4576 
4577 .seealso: `DMPlexGetCone()`
4578 @*/
4579 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4580 {
4581   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4582 
4583   PetscFunctionBegin;
4584   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4585   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4586   PetscValidBoolPointer(equal, 3);
4587 
4588   *equal = PETSC_FALSE;
4589   PetscCall(DMPlexGetDepth(dmA, &depth));
4590   PetscCall(DMPlexGetDepth(dmB, &depthB));
4591   if (depth != depthB) PetscFunctionReturn(0);
4592   PetscCall(DMPlexGetChart(dmA, &pStart,  &pEnd));
4593   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4594   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4595   for (p = pStart; p < pEnd; ++p) {
4596     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4597     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4598 
4599     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4600     PetscCall(DMPlexGetCone(dmA, p, &cone));
4601     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4602     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4603     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4604     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4605     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4606     for (c = 0; c < coneSize; ++c) {
4607       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4608       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4609     }
4610     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4611     PetscCall(DMPlexGetSupport(dmA, p, &support));
4612     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4613     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4614     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4615     for (s = 0; s < supportSize; ++s) {
4616       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4617     }
4618   }
4619   *equal = PETSC_TRUE;
4620   PetscFunctionReturn(0);
4621 }
4622 
4623 /*@C
4624   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4625 
4626   Not Collective
4627 
4628   Input Parameters:
4629 + dm         - The DMPlex
4630 . cellDim    - The cell dimension
4631 - numCorners - The number of vertices on a cell
4632 
4633   Output Parameters:
4634 . numFaceVertices - The number of vertices on a face
4635 
4636   Level: developer
4637 
4638   Notes:
4639   Of course this can only work for a restricted set of symmetric shapes
4640 
4641 .seealso: `DMPlexGetCone()`
4642 @*/
4643 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4644 {
4645   MPI_Comm       comm;
4646 
4647   PetscFunctionBegin;
4648   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
4649   PetscValidIntPointer(numFaceVertices,4);
4650   switch (cellDim) {
4651   case 0:
4652     *numFaceVertices = 0;
4653     break;
4654   case 1:
4655     *numFaceVertices = 1;
4656     break;
4657   case 2:
4658     switch (numCorners) {
4659     case 3: /* triangle */
4660       *numFaceVertices = 2; /* Edge has 2 vertices */
4661       break;
4662     case 4: /* quadrilateral */
4663       *numFaceVertices = 2; /* Edge has 2 vertices */
4664       break;
4665     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
4666       *numFaceVertices = 3; /* Edge has 3 vertices */
4667       break;
4668     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4669       *numFaceVertices = 3; /* Edge has 3 vertices */
4670       break;
4671     default:
4672       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4673     }
4674     break;
4675   case 3:
4676     switch (numCorners) {
4677     case 4: /* tetradehdron */
4678       *numFaceVertices = 3; /* Face has 3 vertices */
4679       break;
4680     case 6: /* tet cohesive cells */
4681       *numFaceVertices = 4; /* Face has 4 vertices */
4682       break;
4683     case 8: /* hexahedron */
4684       *numFaceVertices = 4; /* Face has 4 vertices */
4685       break;
4686     case 9: /* tet cohesive Lagrange cells */
4687       *numFaceVertices = 6; /* Face has 6 vertices */
4688       break;
4689     case 10: /* quadratic tetrahedron */
4690       *numFaceVertices = 6; /* Face has 6 vertices */
4691       break;
4692     case 12: /* hex cohesive Lagrange cells */
4693       *numFaceVertices = 6; /* Face has 6 vertices */
4694       break;
4695     case 18: /* quadratic tet cohesive Lagrange cells */
4696       *numFaceVertices = 6; /* Face has 6 vertices */
4697       break;
4698     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4699       *numFaceVertices = 9; /* Face has 9 vertices */
4700       break;
4701     default:
4702       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4703     }
4704     break;
4705   default:
4706     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4707   }
4708   PetscFunctionReturn(0);
4709 }
4710 
4711 /*@
4712   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4713 
4714   Not Collective
4715 
4716   Input Parameter:
4717 . dm    - The DMPlex object
4718 
4719   Output Parameter:
4720 . depthLabel - The DMLabel recording point depth
4721 
4722   Level: developer
4723 
4724 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4725 @*/
4726 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4727 {
4728   PetscFunctionBegin;
4729   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4730   PetscValidPointer(depthLabel, 2);
4731   *depthLabel = dm->depthLabel;
4732   PetscFunctionReturn(0);
4733 }
4734 
4735 /*@
4736   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4737 
4738   Not Collective
4739 
4740   Input Parameter:
4741 . dm    - The DMPlex object
4742 
4743   Output Parameter:
4744 . depth - The number of strata (breadth first levels) in the DAG
4745 
4746   Level: developer
4747 
4748   Notes:
4749   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4750   The point depth is described more in detail in DMPlexGetDepthStratum().
4751   An empty mesh gives -1.
4752 
4753 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4754 @*/
4755 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4756 {
4757   DMLabel        label;
4758   PetscInt       d = 0;
4759 
4760   PetscFunctionBegin;
4761   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4762   PetscValidIntPointer(depth, 2);
4763   PetscCall(DMPlexGetDepthLabel(dm, &label));
4764   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4765   *depth = d-1;
4766   PetscFunctionReturn(0);
4767 }
4768 
4769 /*@
4770   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4771 
4772   Not Collective
4773 
4774   Input Parameters:
4775 + dm    - The DMPlex object
4776 - depth - The requested depth
4777 
4778   Output Parameters:
4779 + start - The first point at this depth
4780 - end   - One beyond the last point at this depth
4781 
4782   Notes:
4783   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4784   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4785   higher dimension, e.g., "edges".
4786 
4787   Level: developer
4788 
4789 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4790 @*/
4791 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4792 {
4793   DMLabel        label;
4794   PetscInt       pStart, pEnd;
4795 
4796   PetscFunctionBegin;
4797   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4798   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4799   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4800   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4801   if (pStart == pEnd) PetscFunctionReturn(0);
4802   if (depth < 0) {
4803     if (start) *start = pStart;
4804     if (end)   *end   = pEnd;
4805     PetscFunctionReturn(0);
4806   }
4807   PetscCall(DMPlexGetDepthLabel(dm, &label));
4808   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4809   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4810   PetscFunctionReturn(0);
4811 }
4812 
4813 /*@
4814   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4815 
4816   Not Collective
4817 
4818   Input Parameters:
4819 + dm     - The DMPlex object
4820 - height - The requested height
4821 
4822   Output Parameters:
4823 + start - The first point at this height
4824 - end   - One beyond the last point at this height
4825 
4826   Notes:
4827   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4828   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4829   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4830 
4831   Level: developer
4832 
4833 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4834 @*/
4835 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4836 {
4837   DMLabel        label;
4838   PetscInt       depth, pStart, pEnd;
4839 
4840   PetscFunctionBegin;
4841   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4842   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4843   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4844   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4845   if (pStart == pEnd) PetscFunctionReturn(0);
4846   if (height < 0) {
4847     if (start) *start = pStart;
4848     if (end)   *end   = pEnd;
4849     PetscFunctionReturn(0);
4850   }
4851   PetscCall(DMPlexGetDepthLabel(dm, &label));
4852   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4853   PetscCall(DMLabelGetNumValues(label, &depth));
4854   PetscCall(DMLabelGetStratumBounds(label, depth-1-height, start, end));
4855   PetscFunctionReturn(0);
4856 }
4857 
4858 /*@
4859   DMPlexGetPointDepth - Get the depth of a given point
4860 
4861   Not Collective
4862 
4863   Input Parameters:
4864 + dm    - The DMPlex object
4865 - point - The point
4866 
4867   Output Parameter:
4868 . depth - The depth of the point
4869 
4870   Level: intermediate
4871 
4872 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4873 @*/
4874 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4875 {
4876   PetscFunctionBegin;
4877   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4878   PetscValidIntPointer(depth, 3);
4879   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4880   PetscFunctionReturn(0);
4881 }
4882 
4883 /*@
4884   DMPlexGetPointHeight - Get the height of a given point
4885 
4886   Not Collective
4887 
4888   Input Parameters:
4889 + dm    - The DMPlex object
4890 - point - The point
4891 
4892   Output Parameter:
4893 . height - The height of the point
4894 
4895   Level: intermediate
4896 
4897 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4898 @*/
4899 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4900 {
4901   PetscInt       n, pDepth;
4902 
4903   PetscFunctionBegin;
4904   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4905   PetscValidIntPointer(height, 3);
4906   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4907   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4908   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
4909   PetscFunctionReturn(0);
4910 }
4911 
4912 /*@
4913   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4914 
4915   Not Collective
4916 
4917   Input Parameter:
4918 . dm - The DMPlex object
4919 
4920   Output Parameter:
4921 . celltypeLabel - The DMLabel recording cell polytope type
4922 
4923   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4924   DMCreateLabel(dm, "celltype") beforehand.
4925 
4926   Level: developer
4927 
4928 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4929 @*/
4930 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
4931 {
4932   PetscFunctionBegin;
4933   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4934   PetscValidPointer(celltypeLabel, 2);
4935   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4936   *celltypeLabel = dm->celltypeLabel;
4937   PetscFunctionReturn(0);
4938 }
4939 
4940 /*@
4941   DMPlexGetCellType - Get the polytope type of a given cell
4942 
4943   Not Collective
4944 
4945   Input Parameters:
4946 + dm   - The DMPlex object
4947 - cell - The cell
4948 
4949   Output Parameter:
4950 . celltype - The polytope type of the cell
4951 
4952   Level: intermediate
4953 
4954 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4955 @*/
4956 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
4957 {
4958   DMLabel        label;
4959   PetscInt       ct;
4960 
4961   PetscFunctionBegin;
4962   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4963   PetscValidPointer(celltype, 3);
4964   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4965   PetscCall(DMLabelGetValue(label, cell, &ct));
4966   PetscCheck(ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4967   *celltype = (DMPolytopeType) ct;
4968   PetscFunctionReturn(0);
4969 }
4970 
4971 /*@
4972   DMPlexSetCellType - Set the polytope type of a given cell
4973 
4974   Not Collective
4975 
4976   Input Parameters:
4977 + dm   - The DMPlex object
4978 . cell - The cell
4979 - celltype - The polytope type of the cell
4980 
4981   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
4982   is executed. This function will override the computed type. However, if automatic classification will not succeed
4983   and a user wants to manually specify all types, the classification must be disabled by calling
4984   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
4985 
4986   Level: advanced
4987 
4988 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
4989 @*/
4990 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
4991 {
4992   DMLabel        label;
4993 
4994   PetscFunctionBegin;
4995   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4996   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4997   PetscCall(DMLabelSetValue(label, cell, celltype));
4998   PetscFunctionReturn(0);
4999 }
5000 
5001 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5002 {
5003   PetscSection   section, s;
5004   Mat            m;
5005   PetscInt       maxHeight;
5006 
5007   PetscFunctionBegin;
5008   PetscCall(DMClone(dm, cdm));
5009   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5010   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5011   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5012   PetscCall(DMSetLocalSection(*cdm, section));
5013   PetscCall(PetscSectionDestroy(&section));
5014   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5015   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5016   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5017   PetscCall(PetscSectionDestroy(&s));
5018   PetscCall(MatDestroy(&m));
5019 
5020   PetscCall(DMSetNumFields(*cdm, 1));
5021   PetscCall(DMCreateDS(*cdm));
5022   PetscFunctionReturn(0);
5023 }
5024 
5025 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5026 {
5027   Vec            coordsLocal;
5028   DM             coordsDM;
5029 
5030   PetscFunctionBegin;
5031   *field = NULL;
5032   PetscCall(DMGetCoordinatesLocal(dm,&coordsLocal));
5033   PetscCall(DMGetCoordinateDM(dm,&coordsDM));
5034   if (coordsLocal && coordsDM) {
5035     PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5036   }
5037   PetscFunctionReturn(0);
5038 }
5039 
5040 /*@C
5041   DMPlexGetConeSection - Return a section which describes the layout of cone data
5042 
5043   Not Collective
5044 
5045   Input Parameters:
5046 . dm        - The DMPlex object
5047 
5048   Output Parameter:
5049 . section - The PetscSection object
5050 
5051   Level: developer
5052 
5053 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5054 @*/
5055 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5056 {
5057   DM_Plex *mesh = (DM_Plex*) dm->data;
5058 
5059   PetscFunctionBegin;
5060   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5061   if (section) *section = mesh->coneSection;
5062   PetscFunctionReturn(0);
5063 }
5064 
5065 /*@C
5066   DMPlexGetSupportSection - Return a section which describes the layout of support data
5067 
5068   Not Collective
5069 
5070   Input Parameters:
5071 . dm        - The DMPlex object
5072 
5073   Output Parameter:
5074 . section - The PetscSection object
5075 
5076   Level: developer
5077 
5078 .seealso: `DMPlexGetConeSection()`
5079 @*/
5080 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5081 {
5082   DM_Plex *mesh = (DM_Plex*) dm->data;
5083 
5084   PetscFunctionBegin;
5085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5086   if (section) *section = mesh->supportSection;
5087   PetscFunctionReturn(0);
5088 }
5089 
5090 /*@C
5091   DMPlexGetCones - Return cone data
5092 
5093   Not Collective
5094 
5095   Input Parameters:
5096 . dm        - The DMPlex object
5097 
5098   Output Parameter:
5099 . cones - The cone for each point
5100 
5101   Level: developer
5102 
5103 .seealso: `DMPlexGetConeSection()`
5104 @*/
5105 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5106 {
5107   DM_Plex *mesh = (DM_Plex*) dm->data;
5108 
5109   PetscFunctionBegin;
5110   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5111   if (cones) *cones = mesh->cones;
5112   PetscFunctionReturn(0);
5113 }
5114 
5115 /*@C
5116   DMPlexGetConeOrientations - Return cone orientation data
5117 
5118   Not Collective
5119 
5120   Input Parameters:
5121 . dm        - The DMPlex object
5122 
5123   Output Parameter:
5124 . coneOrientations - The array of cone orientations for all points
5125 
5126   Level: developer
5127 
5128   Notes:
5129   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5130 
5131   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5132 
5133 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5134 @*/
5135 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5136 {
5137   DM_Plex *mesh = (DM_Plex*) dm->data;
5138 
5139   PetscFunctionBegin;
5140   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5141   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5142   PetscFunctionReturn(0);
5143 }
5144 
5145 /******************************** FEM Support **********************************/
5146 
5147 /*
5148  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5149  representing a line in the section.
5150 */
5151 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
5152 {
5153   PetscFunctionBeginHot;
5154   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5155   if (line < 0) {
5156     *k = 0;
5157     *Nc = 0;
5158   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
5159     *k = 1;
5160   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
5161     /* An order k SEM disc has k-1 dofs on an edge */
5162     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5163     *k = *k / *Nc + 1;
5164   }
5165   PetscFunctionReturn(0);
5166 }
5167 
5168 /*@
5169 
5170   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5171   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5172   section provided (or the section of the DM).
5173 
5174   Input Parameters:
5175 + dm      - The DM
5176 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5177 - section - The PetscSection to reorder, or NULL for the default section
5178 
5179   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5180   degree of the basis.
5181 
5182   Example:
5183   A typical interpolated single-quad mesh might order points as
5184 .vb
5185   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5186 
5187   v4 -- e6 -- v3
5188   |           |
5189   e7    c0    e8
5190   |           |
5191   v1 -- e5 -- v2
5192 .ve
5193 
5194   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5195   dofs in the order of points, e.g.,
5196 .vb
5197     c0 -> [0,1,2,3]
5198     v1 -> [4]
5199     ...
5200     e5 -> [8, 9]
5201 .ve
5202 
5203   which corresponds to the dofs
5204 .vb
5205     6   10  11  7
5206     13  2   3   15
5207     12  0   1   14
5208     4   8   9   5
5209 .ve
5210 
5211   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5212 .vb
5213   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5214 .ve
5215 
5216   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5217 .vb
5218    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5219 .ve
5220 
5221   Level: developer
5222 
5223 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5224 @*/
5225 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5226 {
5227   DMLabel        label;
5228   PetscInt       dim, depth = -1, eStart = -1, Nf;
5229   PetscBool      vertexchart;
5230 
5231   PetscFunctionBegin;
5232   PetscCall(DMGetDimension(dm, &dim));
5233   if (dim < 1) PetscFunctionReturn(0);
5234   if (point < 0) {
5235     PetscInt sStart,sEnd;
5236 
5237     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5238     point = sEnd-sStart ? sStart : point;
5239   }
5240   PetscCall(DMPlexGetDepthLabel(dm, &label));
5241   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5242   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5243   if (depth == 1) {eStart = point;}
5244   else if  (depth == dim) {
5245     const PetscInt *cone;
5246 
5247     PetscCall(DMPlexGetCone(dm, point, &cone));
5248     if (dim == 2) eStart = cone[0];
5249     else if (dim == 3) {
5250       const PetscInt *cone2;
5251       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5252       eStart = cone2[0];
5253     } 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);
5254   } 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);
5255   {                             /* Determine whether the chart covers all points or just vertices. */
5256     PetscInt pStart,pEnd,cStart,cEnd;
5257     PetscCall(DMPlexGetDepthStratum(dm,0,&pStart,&pEnd));
5258     PetscCall(PetscSectionGetChart(section,&cStart,&cEnd));
5259     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5260     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5261     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5262   }
5263   PetscCall(PetscSectionGetNumFields(section, &Nf));
5264   for (PetscInt d=1; d<=dim; d++) {
5265     PetscInt k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5266     PetscInt *perm;
5267 
5268     for (f = 0; f < Nf; ++f) {
5269       PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5270       size += PetscPowInt(k+1, d)*Nc;
5271     }
5272     PetscCall(PetscMalloc1(size, &perm));
5273     for (f = 0; f < Nf; ++f) {
5274       switch (d) {
5275       case 1:
5276         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5277         /*
5278          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5279          We want              [ vtx0; edge of length k-1; vtx1 ]
5280          */
5281         for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
5282         for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
5283         for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
5284         foffset = offset;
5285         break;
5286       case 2:
5287         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5288         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5289         /* The SEM order is
5290 
5291          v_lb, {e_b}, v_rb,
5292          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5293          v_lt, reverse {e_t}, v_rt
5294          */
5295         {
5296           const PetscInt of   = 0;
5297           const PetscInt oeb  = of   + PetscSqr(k-1);
5298           const PetscInt oer  = oeb  + (k-1);
5299           const PetscInt oet  = oer  + (k-1);
5300           const PetscInt oel  = oet  + (k-1);
5301           const PetscInt ovlb = oel  + (k-1);
5302           const PetscInt ovrb = ovlb + 1;
5303           const PetscInt ovrt = ovrb + 1;
5304           const PetscInt ovlt = ovrt + 1;
5305           PetscInt       o;
5306 
5307           /* bottom */
5308           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
5309           for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5310           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
5311           /* middle */
5312           for (i = 0; i < k-1; ++i) {
5313             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
5314             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;
5315             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
5316           }
5317           /* top */
5318           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
5319           for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5320           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
5321           foffset = offset;
5322         }
5323         break;
5324       case 3:
5325         /* The original hex closure is
5326 
5327          {c,
5328          f_b, f_t, f_f, f_b, f_r, f_l,
5329          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5330          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5331          */
5332         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5333         /* The SEM order is
5334          Bottom Slice
5335          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5336          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5337          v_blb, {e_bb}, v_brb,
5338 
5339          Middle Slice (j)
5340          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5341          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5342          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5343 
5344          Top Slice
5345          v_tlf, {e_tf}, v_trf,
5346          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5347          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5348          */
5349         {
5350           const PetscInt oc    = 0;
5351           const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
5352           const PetscInt oft   = ofb   + PetscSqr(k-1);
5353           const PetscInt off   = oft   + PetscSqr(k-1);
5354           const PetscInt ofk   = off   + PetscSqr(k-1);
5355           const PetscInt ofr   = ofk   + PetscSqr(k-1);
5356           const PetscInt ofl   = ofr   + PetscSqr(k-1);
5357           const PetscInt oebl  = ofl   + PetscSqr(k-1);
5358           const PetscInt oebb  = oebl  + (k-1);
5359           const PetscInt oebr  = oebb  + (k-1);
5360           const PetscInt oebf  = oebr  + (k-1);
5361           const PetscInt oetf  = oebf  + (k-1);
5362           const PetscInt oetr  = oetf  + (k-1);
5363           const PetscInt oetb  = oetr  + (k-1);
5364           const PetscInt oetl  = oetb  + (k-1);
5365           const PetscInt oerf  = oetl  + (k-1);
5366           const PetscInt oelf  = oerf  + (k-1);
5367           const PetscInt oelb  = oelf  + (k-1);
5368           const PetscInt oerb  = oelb  + (k-1);
5369           const PetscInt ovblf = oerb  + (k-1);
5370           const PetscInt ovblb = ovblf + 1;
5371           const PetscInt ovbrb = ovblb + 1;
5372           const PetscInt ovbrf = ovbrb + 1;
5373           const PetscInt ovtlf = ovbrf + 1;
5374           const PetscInt ovtrf = ovtlf + 1;
5375           const PetscInt ovtrb = ovtrf + 1;
5376           const PetscInt ovtlb = ovtrb + 1;
5377           PetscInt       o, n;
5378 
5379           /* Bottom Slice */
5380           /*   bottom */
5381           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
5382           for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5383           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
5384           /*   middle */
5385           for (i = 0; i < k-1; ++i) {
5386             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
5387             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;}
5388             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
5389           }
5390           /*   top */
5391           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
5392           for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5393           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
5394 
5395           /* Middle Slice */
5396           for (j = 0; j < k-1; ++j) {
5397             /*   bottom */
5398             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
5399             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;
5400             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
5401             /*   middle */
5402             for (i = 0; i < k-1; ++i) {
5403               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
5404               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;
5405               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
5406             }
5407             /*   top */
5408             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
5409             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;
5410             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
5411           }
5412 
5413           /* Top Slice */
5414           /*   bottom */
5415           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
5416           for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5417           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
5418           /*   middle */
5419           for (i = 0; i < k-1; ++i) {
5420             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
5421             for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
5422             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
5423           }
5424           /*   top */
5425           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
5426           for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5427           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
5428 
5429           foffset = offset;
5430         }
5431         break;
5432       default: SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5433       }
5434     }
5435     PetscCheck(offset == size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5436     /* Check permutation */
5437     {
5438       PetscInt *check;
5439 
5440       PetscCall(PetscMalloc1(size, &check));
5441       for (i = 0; i < size; ++i) {
5442         check[i] = -1;
5443         PetscCheck(perm[i] >= 0 && perm[i] < size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5444       }
5445       for (i = 0; i < size; ++i) check[perm[i]] = i;
5446       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5447       PetscCall(PetscFree(check));
5448     }
5449     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size, PETSC_OWN_POINTER, perm));
5450     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5451       PetscInt *loc_perm;
5452       PetscCall(PetscMalloc1(size*2, &loc_perm));
5453       for (PetscInt i=0; i<size; i++) {
5454         loc_perm[i] = perm[i];
5455         loc_perm[size+i] = size + perm[i];
5456       }
5457       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size*2, PETSC_OWN_POINTER, loc_perm));
5458     }
5459   }
5460   PetscFunctionReturn(0);
5461 }
5462 
5463 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5464 {
5465   PetscDS        prob;
5466   PetscInt       depth, Nf, h;
5467   DMLabel        label;
5468 
5469   PetscFunctionBeginHot;
5470   PetscCall(DMGetDS(dm, &prob));
5471   Nf      = prob->Nf;
5472   label   = dm->depthLabel;
5473   *dspace = NULL;
5474   if (field < Nf) {
5475     PetscObject disc = prob->disc[field];
5476 
5477     if (disc->classid == PETSCFE_CLASSID) {
5478       PetscDualSpace dsp;
5479 
5480       PetscCall(PetscFEGetDualSpace((PetscFE)disc,&dsp));
5481       PetscCall(DMLabelGetNumValues(label,&depth));
5482       PetscCall(DMLabelGetValue(label,point,&h));
5483       h    = depth - 1 - h;
5484       if (h) {
5485         PetscCall(PetscDualSpaceGetHeightSubspace(dsp,h,dspace));
5486       } else {
5487         *dspace = dsp;
5488       }
5489     }
5490   }
5491   PetscFunctionReturn(0);
5492 }
5493 
5494 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5495 {
5496   PetscScalar    *array, *vArray;
5497   const PetscInt *cone, *coneO;
5498   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
5499 
5500   PetscFunctionBeginHot;
5501   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5502   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5503   PetscCall(DMPlexGetCone(dm, point, &cone));
5504   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5505   if (!values || !*values) {
5506     if ((point >= pStart) && (point < pEnd)) {
5507       PetscInt dof;
5508 
5509       PetscCall(PetscSectionGetDof(section, point, &dof));
5510       size += dof;
5511     }
5512     for (p = 0; p < numPoints; ++p) {
5513       const PetscInt cp = cone[p];
5514       PetscInt       dof;
5515 
5516       if ((cp < pStart) || (cp >= pEnd)) continue;
5517       PetscCall(PetscSectionGetDof(section, cp, &dof));
5518       size += dof;
5519     }
5520     if (!values) {
5521       if (csize) *csize = size;
5522       PetscFunctionReturn(0);
5523     }
5524     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5525   } else {
5526     array = *values;
5527   }
5528   size = 0;
5529   PetscCall(VecGetArray(v, &vArray));
5530   if ((point >= pStart) && (point < pEnd)) {
5531     PetscInt     dof, off, d;
5532     PetscScalar *varr;
5533 
5534     PetscCall(PetscSectionGetDof(section, point, &dof));
5535     PetscCall(PetscSectionGetOffset(section, point, &off));
5536     varr = &vArray[off];
5537     for (d = 0; d < dof; ++d, ++offset) {
5538       array[offset] = varr[d];
5539     }
5540     size += dof;
5541   }
5542   for (p = 0; p < numPoints; ++p) {
5543     const PetscInt cp = cone[p];
5544     PetscInt       o  = coneO[p];
5545     PetscInt       dof, off, d;
5546     PetscScalar   *varr;
5547 
5548     if ((cp < pStart) || (cp >= pEnd)) continue;
5549     PetscCall(PetscSectionGetDof(section, cp, &dof));
5550     PetscCall(PetscSectionGetOffset(section, cp, &off));
5551     varr = &vArray[off];
5552     if (o >= 0) {
5553       for (d = 0; d < dof; ++d, ++offset) {
5554         array[offset] = varr[d];
5555       }
5556     } else {
5557       for (d = dof-1; d >= 0; --d, ++offset) {
5558         array[offset] = varr[d];
5559       }
5560     }
5561     size += dof;
5562   }
5563   PetscCall(VecRestoreArray(v, &vArray));
5564   if (!*values) {
5565     if (csize) *csize = size;
5566     *values = array;
5567   } else {
5568     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5569     *csize = size;
5570   }
5571   PetscFunctionReturn(0);
5572 }
5573 
5574 /* Compress out points not in the section */
5575 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5576 {
5577   const PetscInt np = *numPoints;
5578   PetscInt       pStart, pEnd, p, q;
5579 
5580   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5581   for (p = 0, q = 0; p < np; ++p) {
5582     const PetscInt r = points[p*2];
5583     if ((r >= pStart) && (r < pEnd)) {
5584       points[q*2]   = r;
5585       points[q*2+1] = points[p*2+1];
5586       ++q;
5587     }
5588   }
5589   *numPoints = q;
5590   return 0;
5591 }
5592 
5593 /* Compressed closure does not apply closure permutation */
5594 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5595 {
5596   const PetscInt *cla = NULL;
5597   PetscInt       np, *pts = NULL;
5598 
5599   PetscFunctionBeginHot;
5600   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints));
5601   if (*clPoints) {
5602     PetscInt dof, off;
5603 
5604     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5605     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5606     PetscCall(ISGetIndices(*clPoints, &cla));
5607     np   = dof/2;
5608     pts  = (PetscInt *) &cla[off];
5609   } else {
5610     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5611     PetscCall(CompressPoints_Private(section, &np, pts));
5612   }
5613   *numPoints = np;
5614   *points    = pts;
5615   *clp       = cla;
5616   PetscFunctionReturn(0);
5617 }
5618 
5619 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5620 {
5621   PetscFunctionBeginHot;
5622   if (!*clPoints) {
5623     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5624   } else {
5625     PetscCall(ISRestoreIndices(*clPoints, clp));
5626   }
5627   *numPoints = 0;
5628   *points    = NULL;
5629   *clSec     = NULL;
5630   *clPoints  = NULL;
5631   *clp       = NULL;
5632   PetscFunctionReturn(0);
5633 }
5634 
5635 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5636 {
5637   PetscInt          offset = 0, p;
5638   const PetscInt    **perms = NULL;
5639   const PetscScalar **flips = NULL;
5640 
5641   PetscFunctionBeginHot;
5642   *size = 0;
5643   PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
5644   for (p = 0; p < numPoints; p++) {
5645     const PetscInt    point = points[2*p];
5646     const PetscInt    *perm = perms ? perms[p] : NULL;
5647     const PetscScalar *flip = flips ? flips[p] : NULL;
5648     PetscInt          dof, off, d;
5649     const PetscScalar *varr;
5650 
5651     PetscCall(PetscSectionGetDof(section, point, &dof));
5652     PetscCall(PetscSectionGetOffset(section, point, &off));
5653     varr = &vArray[off];
5654     if (clperm) {
5655       if (perm) {
5656         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5657       } else {
5658         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5659       }
5660       if (flip) {
5661         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5662       }
5663     } else {
5664       if (perm) {
5665         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5666       } else {
5667         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5668       }
5669       if (flip) {
5670         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5671       }
5672     }
5673     offset += dof;
5674   }
5675   PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
5676   *size = offset;
5677   PetscFunctionReturn(0);
5678 }
5679 
5680 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[])
5681 {
5682   PetscInt          offset = 0, f;
5683 
5684   PetscFunctionBeginHot;
5685   *size = 0;
5686   for (f = 0; f < numFields; ++f) {
5687     PetscInt          p;
5688     const PetscInt    **perms = NULL;
5689     const PetscScalar **flips = NULL;
5690 
5691     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5692     for (p = 0; p < numPoints; p++) {
5693       const PetscInt    point = points[2*p];
5694       PetscInt          fdof, foff, b;
5695       const PetscScalar *varr;
5696       const PetscInt    *perm = perms ? perms[p] : NULL;
5697       const PetscScalar *flip = flips ? flips[p] : NULL;
5698 
5699       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5700       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5701       varr = &vArray[foff];
5702       if (clperm) {
5703         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5704         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5705         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5706       } else {
5707         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5708         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5709         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5710       }
5711       offset += fdof;
5712     }
5713     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5714   }
5715   *size = offset;
5716   PetscFunctionReturn(0);
5717 }
5718 
5719 /*@C
5720   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5721 
5722   Not collective
5723 
5724   Input Parameters:
5725 + dm - The DM
5726 . section - The section describing the layout in v, or NULL to use the default section
5727 . v - The local vector
5728 - point - The point in the DM
5729 
5730   Input/Output Parameters:
5731 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5732 - values - An array to use for the values, or NULL to have it allocated automatically;
5733            if the user provided NULL, it is a borrowed array and should not be freed
5734 
5735 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5736 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5737 $ assembly function, and a user may already have allocated storage for this operation.
5738 $
5739 $ A typical use could be
5740 $
5741 $  values = NULL;
5742 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5743 $  for (cl = 0; cl < clSize; ++cl) {
5744 $    <Compute on closure>
5745 $  }
5746 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5747 $
5748 $ or
5749 $
5750 $  PetscMalloc1(clMaxSize, &values);
5751 $  for (p = pStart; p < pEnd; ++p) {
5752 $    clSize = clMaxSize;
5753 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5754 $    for (cl = 0; cl < clSize; ++cl) {
5755 $      <Compute on closure>
5756 $    }
5757 $  }
5758 $  PetscFree(values);
5759 
5760   Fortran Notes:
5761   Since it returns an array, this routine is only available in Fortran 90, and you must
5762   include petsc.h90 in your code.
5763 
5764   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5765 
5766   Level: intermediate
5767 
5768 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5769 @*/
5770 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5771 {
5772   PetscSection       clSection;
5773   IS                 clPoints;
5774   PetscInt          *points = NULL;
5775   const PetscInt    *clp, *perm;
5776   PetscInt           depth, numFields, numPoints, asize;
5777 
5778   PetscFunctionBeginHot;
5779   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5780   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5781   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5782   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5783   PetscCall(DMPlexGetDepth(dm, &depth));
5784   PetscCall(PetscSectionGetNumFields(section, &numFields));
5785   if (depth == 1 && numFields < 2) {
5786     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5787     PetscFunctionReturn(0);
5788   }
5789   /* Get points */
5790   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5791   /* Get sizes */
5792   asize = 0;
5793   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5794     PetscInt dof;
5795     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5796     asize += dof;
5797   }
5798   if (values) {
5799     const PetscScalar *vArray;
5800     PetscInt          size;
5801 
5802     if (*values) {
5803       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);
5804     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5805     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm));
5806     PetscCall(VecGetArrayRead(v, &vArray));
5807     /* Get values */
5808     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5809     else               PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5810     PetscCheck(asize == size,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5811     /* Cleanup array */
5812     PetscCall(VecRestoreArrayRead(v, &vArray));
5813   }
5814   if (csize) *csize = asize;
5815   /* Cleanup points */
5816   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5817   PetscFunctionReturn(0);
5818 }
5819 
5820 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5821 {
5822   DMLabel            depthLabel;
5823   PetscSection       clSection;
5824   IS                 clPoints;
5825   PetscScalar       *array;
5826   const PetscScalar *vArray;
5827   PetscInt          *points = NULL;
5828   const PetscInt    *clp, *perm = NULL;
5829   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5830 
5831   PetscFunctionBeginHot;
5832   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5833   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5834   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5835   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5836   PetscCall(DMPlexGetDepth(dm, &mdepth));
5837   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5838   PetscCall(PetscSectionGetNumFields(section, &numFields));
5839   if (mdepth == 1 && numFields < 2) {
5840     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5841     PetscFunctionReturn(0);
5842   }
5843   /* Get points */
5844   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5845   for (clsize=0,p=0; p<Np; p++) {
5846     PetscInt dof;
5847     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
5848     clsize += dof;
5849   }
5850   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm));
5851   /* Filter points */
5852   for (p = 0; p < numPoints*2; p += 2) {
5853     PetscInt dep;
5854 
5855     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5856     if (dep != depth) continue;
5857     points[Np*2+0] = points[p];
5858     points[Np*2+1] = points[p+1];
5859     ++Np;
5860   }
5861   /* Get array */
5862   if (!values || !*values) {
5863     PetscInt asize = 0, dof;
5864 
5865     for (p = 0; p < Np*2; p += 2) {
5866       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5867       asize += dof;
5868     }
5869     if (!values) {
5870       PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5871       if (csize) *csize = asize;
5872       PetscFunctionReturn(0);
5873     }
5874     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5875   } else {
5876     array = *values;
5877   }
5878   PetscCall(VecGetArrayRead(v, &vArray));
5879   /* Get values */
5880   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5881   else               PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5882   /* Cleanup points */
5883   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5884   /* Cleanup array */
5885   PetscCall(VecRestoreArrayRead(v, &vArray));
5886   if (!*values) {
5887     if (csize) *csize = size;
5888     *values = array;
5889   } else {
5890     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5891     *csize = size;
5892   }
5893   PetscFunctionReturn(0);
5894 }
5895 
5896 /*@C
5897   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5898 
5899   Not collective
5900 
5901   Input Parameters:
5902 + dm - The DM
5903 . section - The section describing the layout in v, or NULL to use the default section
5904 . v - The local vector
5905 . point - The point in the DM
5906 . csize - The number of values in the closure, or NULL
5907 - values - The array of values, which is a borrowed array and should not be freed
5908 
5909   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5910 
5911   Fortran Notes:
5912   Since it returns an array, this routine is only available in Fortran 90, and you must
5913   include petsc.h90 in your code.
5914 
5915   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5916 
5917   Level: intermediate
5918 
5919 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5920 @*/
5921 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5922 {
5923   PetscInt       size = 0;
5924 
5925   PetscFunctionBegin;
5926   /* Should work without recalculating size */
5927   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values));
5928   *values = NULL;
5929   PetscFunctionReturn(0);
5930 }
5931 
5932 static inline void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5933 static inline void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5934 
5935 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[])
5936 {
5937   PetscInt        cdof;   /* The number of constraints on this point */
5938   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5939   PetscScalar    *a;
5940   PetscInt        off, cind = 0, k;
5941 
5942   PetscFunctionBegin;
5943   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5944   PetscCall(PetscSectionGetOffset(section, point, &off));
5945   a    = &array[off];
5946   if (!cdof || setBC) {
5947     if (clperm) {
5948       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5949       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5950     } else {
5951       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5952       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5953     }
5954   } else {
5955     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5956     if (clperm) {
5957       if (perm) {for (k = 0; k < dof; ++k) {
5958           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5959           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5960         }
5961       } else {
5962         for (k = 0; k < dof; ++k) {
5963           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5964           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5965         }
5966       }
5967     } else {
5968       if (perm) {
5969         for (k = 0; k < dof; ++k) {
5970           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5971           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
5972         }
5973       } else {
5974         for (k = 0; k < dof; ++k) {
5975           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5976           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
5977         }
5978       }
5979     }
5980   }
5981   PetscFunctionReturn(0);
5982 }
5983 
5984 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[])
5985 {
5986   PetscInt        cdof;   /* The number of constraints on this point */
5987   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5988   PetscScalar    *a;
5989   PetscInt        off, cind = 0, k;
5990 
5991   PetscFunctionBegin;
5992   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5993   PetscCall(PetscSectionGetOffset(section, point, &off));
5994   a    = &array[off];
5995   if (cdof) {
5996     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5997     if (clperm) {
5998       if (perm) {
5999         for (k = 0; k < dof; ++k) {
6000           if ((cind < cdof) && (k == cdofs[cind])) {
6001             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
6002             cind++;
6003           }
6004         }
6005       } else {
6006         for (k = 0; k < dof; ++k) {
6007           if ((cind < cdof) && (k == cdofs[cind])) {
6008             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
6009             cind++;
6010           }
6011         }
6012       }
6013     } else {
6014       if (perm) {
6015         for (k = 0; k < dof; ++k) {
6016           if ((cind < cdof) && (k == cdofs[cind])) {
6017             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6018             cind++;
6019           }
6020         }
6021       } else {
6022         for (k = 0; k < dof; ++k) {
6023           if ((cind < cdof) && (k == cdofs[cind])) {
6024             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6025             cind++;
6026           }
6027         }
6028       }
6029     }
6030   }
6031   PetscFunctionReturn(0);
6032 }
6033 
6034 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[])
6035 {
6036   PetscScalar    *a;
6037   PetscInt        fdof, foff, fcdof, foffset = *offset;
6038   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6039   PetscInt        cind = 0, b;
6040 
6041   PetscFunctionBegin;
6042   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6043   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6044   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6045   a    = &array[foff];
6046   if (!fcdof || setBC) {
6047     if (clperm) {
6048       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
6049       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
6050     } else {
6051       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
6052       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
6053     }
6054   } else {
6055     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6056     if (clperm) {
6057       if (perm) {
6058         for (b = 0; b < fdof; b++) {
6059           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6060           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6061         }
6062       } else {
6063         for (b = 0; b < fdof; b++) {
6064           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6065           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6066         }
6067       }
6068     } else {
6069       if (perm) {
6070         for (b = 0; b < fdof; b++) {
6071           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6072           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6073         }
6074       } else {
6075         for (b = 0; b < fdof; b++) {
6076           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6077           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6078         }
6079       }
6080     }
6081   }
6082   *offset += fdof;
6083   PetscFunctionReturn(0);
6084 }
6085 
6086 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[])
6087 {
6088   PetscScalar    *a;
6089   PetscInt        fdof, foff, fcdof, foffset = *offset;
6090   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6091   PetscInt        Nc, cind = 0, ncind = 0, b;
6092   PetscBool       ncSet, fcSet;
6093 
6094   PetscFunctionBegin;
6095   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6096   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6097   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6098   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6099   a    = &array[foff];
6100   if (fcdof) {
6101     /* We just override fcdof and fcdofs with Ncc and comps */
6102     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6103     if (clperm) {
6104       if (perm) {
6105         if (comps) {
6106           for (b = 0; b < fdof; b++) {
6107             ncSet = fcSet = PETSC_FALSE;
6108             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6109             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6110             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
6111           }
6112         } else {
6113           for (b = 0; b < fdof; b++) {
6114             if ((cind < fcdof) && (b == fcdofs[cind])) {
6115               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6116               ++cind;
6117             }
6118           }
6119         }
6120       } else {
6121         if (comps) {
6122           for (b = 0; b < fdof; b++) {
6123             ncSet = fcSet = PETSC_FALSE;
6124             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6125             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6126             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
6127           }
6128         } else {
6129           for (b = 0; b < fdof; b++) {
6130             if ((cind < fcdof) && (b == fcdofs[cind])) {
6131               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6132               ++cind;
6133             }
6134           }
6135         }
6136       }
6137     } else {
6138       if (perm) {
6139         if (comps) {
6140           for (b = 0; b < fdof; b++) {
6141             ncSet = fcSet = PETSC_FALSE;
6142             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6143             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6144             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
6145           }
6146         } else {
6147           for (b = 0; b < fdof; b++) {
6148             if ((cind < fcdof) && (b == fcdofs[cind])) {
6149               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6150               ++cind;
6151             }
6152           }
6153         }
6154       } else {
6155         if (comps) {
6156           for (b = 0; b < fdof; b++) {
6157             ncSet = fcSet = PETSC_FALSE;
6158             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6159             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6160             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
6161           }
6162         } else {
6163           for (b = 0; b < fdof; b++) {
6164             if ((cind < fcdof) && (b == fcdofs[cind])) {
6165               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6166               ++cind;
6167             }
6168           }
6169         }
6170       }
6171     }
6172   }
6173   *offset += fdof;
6174   PetscFunctionReturn(0);
6175 }
6176 
6177 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6178 {
6179   PetscScalar    *array;
6180   const PetscInt *cone, *coneO;
6181   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6182 
6183   PetscFunctionBeginHot;
6184   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6185   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6186   PetscCall(DMPlexGetCone(dm, point, &cone));
6187   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6188   PetscCall(VecGetArray(v, &array));
6189   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6190     const PetscInt cp = !p ? point : cone[p-1];
6191     const PetscInt o  = !p ? 0     : coneO[p-1];
6192 
6193     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
6194     PetscCall(PetscSectionGetDof(section, cp, &dof));
6195     /* ADD_VALUES */
6196     {
6197       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6198       PetscScalar    *a;
6199       PetscInt        cdof, coff, cind = 0, k;
6200 
6201       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6202       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6203       a    = &array[coff];
6204       if (!cdof) {
6205         if (o >= 0) {
6206           for (k = 0; k < dof; ++k) {
6207             a[k] += values[off+k];
6208           }
6209         } else {
6210           for (k = 0; k < dof; ++k) {
6211             a[k] += values[off+dof-k-1];
6212           }
6213         }
6214       } else {
6215         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6216         if (o >= 0) {
6217           for (k = 0; k < dof; ++k) {
6218             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6219             a[k] += values[off+k];
6220           }
6221         } else {
6222           for (k = 0; k < dof; ++k) {
6223             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6224             a[k] += values[off+dof-k-1];
6225           }
6226         }
6227       }
6228     }
6229   }
6230   PetscCall(VecRestoreArray(v, &array));
6231   PetscFunctionReturn(0);
6232 }
6233 
6234 /*@C
6235   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6236 
6237   Not collective
6238 
6239   Input Parameters:
6240 + dm - The DM
6241 . section - The section describing the layout in v, or NULL to use the default section
6242 . v - The local vector
6243 . point - The point in the DM
6244 . values - The array of values
6245 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6246          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6247 
6248   Fortran Notes:
6249   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6250 
6251   Level: intermediate
6252 
6253 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6254 @*/
6255 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6256 {
6257   PetscSection    clSection;
6258   IS              clPoints;
6259   PetscScalar    *array;
6260   PetscInt       *points = NULL;
6261   const PetscInt *clp, *clperm = NULL;
6262   PetscInt        depth, numFields, numPoints, p, clsize;
6263 
6264   PetscFunctionBeginHot;
6265   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6266   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6267   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6268   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6269   PetscCall(DMPlexGetDepth(dm, &depth));
6270   PetscCall(PetscSectionGetNumFields(section, &numFields));
6271   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6272     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6273     PetscFunctionReturn(0);
6274   }
6275   /* Get points */
6276   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6277   for (clsize=0,p=0; p<numPoints; p++) {
6278     PetscInt dof;
6279     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
6280     clsize += dof;
6281   }
6282   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
6283   /* Get array */
6284   PetscCall(VecGetArray(v, &array));
6285   /* Get values */
6286   if (numFields > 0) {
6287     PetscInt offset = 0, f;
6288     for (f = 0; f < numFields; ++f) {
6289       const PetscInt    **perms = NULL;
6290       const PetscScalar **flips = NULL;
6291 
6292       PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6293       switch (mode) {
6294       case INSERT_VALUES:
6295         for (p = 0; p < numPoints; p++) {
6296           const PetscInt    point = points[2*p];
6297           const PetscInt    *perm = perms ? perms[p] : NULL;
6298           const PetscScalar *flip = flips ? flips[p] : NULL;
6299           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6300         } break;
6301       case INSERT_ALL_VALUES:
6302         for (p = 0; p < numPoints; p++) {
6303           const PetscInt    point = points[2*p];
6304           const PetscInt    *perm = perms ? perms[p] : NULL;
6305           const PetscScalar *flip = flips ? flips[p] : NULL;
6306           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6307         } break;
6308       case INSERT_BC_VALUES:
6309         for (p = 0; p < numPoints; p++) {
6310           const PetscInt    point = points[2*p];
6311           const PetscInt    *perm = perms ? perms[p] : NULL;
6312           const PetscScalar *flip = flips ? flips[p] : NULL;
6313           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6314         } break;
6315       case ADD_VALUES:
6316         for (p = 0; p < numPoints; p++) {
6317           const PetscInt    point = points[2*p];
6318           const PetscInt    *perm = perms ? perms[p] : NULL;
6319           const PetscScalar *flip = flips ? flips[p] : NULL;
6320           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6321         } break;
6322       case ADD_ALL_VALUES:
6323         for (p = 0; p < numPoints; p++) {
6324           const PetscInt    point = points[2*p];
6325           const PetscInt    *perm = perms ? perms[p] : NULL;
6326           const PetscScalar *flip = flips ? flips[p] : NULL;
6327           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6328         } break;
6329       case ADD_BC_VALUES:
6330         for (p = 0; p < numPoints; p++) {
6331           const PetscInt    point = points[2*p];
6332           const PetscInt    *perm = perms ? perms[p] : NULL;
6333           const PetscScalar *flip = flips ? flips[p] : NULL;
6334           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6335         } break;
6336       default:
6337         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6338       }
6339       PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6340     }
6341   } else {
6342     PetscInt dof, off;
6343     const PetscInt    **perms = NULL;
6344     const PetscScalar **flips = NULL;
6345 
6346     PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
6347     switch (mode) {
6348     case INSERT_VALUES:
6349       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6350         const PetscInt    point = points[2*p];
6351         const PetscInt    *perm = perms ? perms[p] : NULL;
6352         const PetscScalar *flip = flips ? flips[p] : NULL;
6353         PetscCall(PetscSectionGetDof(section, point, &dof));
6354         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6355       } break;
6356     case INSERT_ALL_VALUES:
6357       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6358         const PetscInt    point = points[2*p];
6359         const PetscInt    *perm = perms ? perms[p] : NULL;
6360         const PetscScalar *flip = flips ? flips[p] : NULL;
6361         PetscCall(PetscSectionGetDof(section, point, &dof));
6362         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
6363       } break;
6364     case INSERT_BC_VALUES:
6365       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6366         const PetscInt    point = points[2*p];
6367         const PetscInt    *perm = perms ? perms[p] : NULL;
6368         const PetscScalar *flip = flips ? flips[p] : NULL;
6369         PetscCall(PetscSectionGetDof(section, point, &dof));
6370         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
6371       } break;
6372     case ADD_VALUES:
6373       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6374         const PetscInt    point = points[2*p];
6375         const PetscInt    *perm = perms ? perms[p] : NULL;
6376         const PetscScalar *flip = flips ? flips[p] : NULL;
6377         PetscCall(PetscSectionGetDof(section, point, &dof));
6378         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
6379       } break;
6380     case ADD_ALL_VALUES:
6381       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6382         const PetscInt    point = points[2*p];
6383         const PetscInt    *perm = perms ? perms[p] : NULL;
6384         const PetscScalar *flip = flips ? flips[p] : NULL;
6385         PetscCall(PetscSectionGetDof(section, point, &dof));
6386         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
6387       } break;
6388     case ADD_BC_VALUES:
6389       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6390         const PetscInt    point = points[2*p];
6391         const PetscInt    *perm = perms ? perms[p] : NULL;
6392         const PetscScalar *flip = flips ? flips[p] : NULL;
6393         PetscCall(PetscSectionGetDof(section, point, &dof));
6394         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
6395       } break;
6396     default:
6397       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6398     }
6399     PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
6400   }
6401   /* Cleanup points */
6402   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6403   /* Cleanup array */
6404   PetscCall(VecRestoreArray(v, &array));
6405   PetscFunctionReturn(0);
6406 }
6407 
6408 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6409 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
6410 {
6411   PetscFunctionBegin;
6412   if (label) {
6413     PetscInt       val, fdof;
6414 
6415     /* There is a problem with this:
6416          Suppose we have two label values, defining surfaces, interecting along a line in 3D. When we add cells to the label, the cells that
6417        touch both surfaces must pick a label value. Thus we miss setting values for the surface with that other value intersecting that cell.
6418        Thus I am only going to check val != -1, not val != labelId
6419     */
6420     PetscCall(DMLabelGetValue(label, point, &val));
6421     if (val < 0) {
6422       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6423       *offset += fdof;
6424       PetscFunctionReturn(1);
6425     }
6426   }
6427   PetscFunctionReturn(0);
6428 }
6429 
6430 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6431 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)
6432 {
6433   PetscSection    clSection;
6434   IS              clPoints;
6435   PetscScalar    *array;
6436   PetscInt       *points = NULL;
6437   const PetscInt *clp;
6438   PetscInt        numFields, numPoints, p;
6439   PetscInt        offset = 0, f;
6440 
6441   PetscFunctionBeginHot;
6442   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6443   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6444   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6445   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6446   PetscCall(PetscSectionGetNumFields(section, &numFields));
6447   /* Get points */
6448   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6449   /* Get array */
6450   PetscCall(VecGetArray(v, &array));
6451   /* Get values */
6452   for (f = 0; f < numFields; ++f) {
6453     const PetscInt    **perms = NULL;
6454     const PetscScalar **flips = NULL;
6455 
6456     if (!fieldActive[f]) {
6457       for (p = 0; p < numPoints*2; p += 2) {
6458         PetscInt fdof;
6459         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6460         offset += fdof;
6461       }
6462       continue;
6463     }
6464     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6465     switch (mode) {
6466     case INSERT_VALUES:
6467       for (p = 0; p < numPoints; p++) {
6468         const PetscInt    point = points[2*p];
6469         const PetscInt    *perm = perms ? perms[p] : NULL;
6470         const PetscScalar *flip = flips ? flips[p] : NULL;
6471         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6472         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6473       } break;
6474     case INSERT_ALL_VALUES:
6475       for (p = 0; p < numPoints; p++) {
6476         const PetscInt    point = points[2*p];
6477         const PetscInt    *perm = perms ? perms[p] : NULL;
6478         const PetscScalar *flip = flips ? flips[p] : NULL;
6479         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6480         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6481       } break;
6482     case INSERT_BC_VALUES:
6483       for (p = 0; p < numPoints; p++) {
6484         const PetscInt    point = points[2*p];
6485         const PetscInt    *perm = perms ? perms[p] : NULL;
6486         const PetscScalar *flip = flips ? flips[p] : NULL;
6487         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6488         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6489       } break;
6490     case ADD_VALUES:
6491       for (p = 0; p < numPoints; p++) {
6492         const PetscInt    point = points[2*p];
6493         const PetscInt    *perm = perms ? perms[p] : NULL;
6494         const PetscScalar *flip = flips ? flips[p] : NULL;
6495         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6496         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6497       } break;
6498     case ADD_ALL_VALUES:
6499       for (p = 0; p < numPoints; p++) {
6500         const PetscInt    point = points[2*p];
6501         const PetscInt    *perm = perms ? perms[p] : NULL;
6502         const PetscScalar *flip = flips ? flips[p] : NULL;
6503         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6504         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6505       } break;
6506     default:
6507       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6508     }
6509     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6510   }
6511   /* Cleanup points */
6512   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6513   /* Cleanup array */
6514   PetscCall(VecRestoreArray(v, &array));
6515   PetscFunctionReturn(0);
6516 }
6517 
6518 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6519 {
6520   PetscMPIInt    rank;
6521   PetscInt       i, j;
6522 
6523   PetscFunctionBegin;
6524   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6525   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6526   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6527   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6528   numCIndices = numCIndices ? numCIndices : numRIndices;
6529   if (!values) PetscFunctionReturn(0);
6530   for (i = 0; i < numRIndices; i++) {
6531     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6532     for (j = 0; j < numCIndices; j++) {
6533 #if defined(PETSC_USE_COMPLEX)
6534       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j])));
6535 #else
6536       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]));
6537 #endif
6538     }
6539     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6540   }
6541   PetscFunctionReturn(0);
6542 }
6543 
6544 /*
6545   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6546 
6547   Input Parameters:
6548 + section - The section for this data layout
6549 . islocal - Is the section (and thus indices being requested) local or global?
6550 . point   - The point contributing dofs with these indices
6551 . off     - The global offset of this point
6552 . loff    - The local offset of each field
6553 . setBC   - The flag determining whether to include indices of boundary values
6554 . perm    - A permutation of the dofs on this point, or NULL
6555 - indperm - A permutation of the entire indices array, or NULL
6556 
6557   Output Parameter:
6558 . indices - Indices for dofs on this point
6559 
6560   Level: developer
6561 
6562   Note: The indices could be local or global, depending on the value of 'off'.
6563 */
6564 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6565 {
6566   PetscInt        dof;   /* The number of unknowns on this point */
6567   PetscInt        cdof;  /* The number of constraints on this point */
6568   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6569   PetscInt        cind = 0, k;
6570 
6571   PetscFunctionBegin;
6572   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6573   PetscCall(PetscSectionGetDof(section, point, &dof));
6574   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6575   if (!cdof || setBC) {
6576     for (k = 0; k < dof; ++k) {
6577       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6578       const PetscInt ind    = indperm ? indperm[preind] : preind;
6579 
6580       indices[ind] = off + k;
6581     }
6582   } else {
6583     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6584     for (k = 0; k < dof; ++k) {
6585       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6586       const PetscInt ind    = indperm ? indperm[preind] : preind;
6587 
6588       if ((cind < cdof) && (k == cdofs[cind])) {
6589         /* Insert check for returning constrained indices */
6590         indices[ind] = -(off+k+1);
6591         ++cind;
6592       } else {
6593         indices[ind] = off + k - (islocal ? 0 : cind);
6594       }
6595     }
6596   }
6597   *loff += dof;
6598   PetscFunctionReturn(0);
6599 }
6600 
6601 /*
6602  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6603 
6604  Input Parameters:
6605 + section - a section (global or local)
6606 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6607 . point - point within section
6608 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6609 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6610 . setBC - identify constrained (boundary condition) points via involution.
6611 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6612 . permsoff - offset
6613 - indperm - index permutation
6614 
6615  Output Parameter:
6616 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6617 . indices - array to hold indices (as defined by section) of each dof associated with point
6618 
6619  Notes:
6620  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6621  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6622  in the local vector.
6623 
6624  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6625  significant).  It is invalid to call with a global section and setBC=true.
6626 
6627  Developer Note:
6628  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6629  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6630  offset could be obtained from the section instead of passing it explicitly as we do now.
6631 
6632  Example:
6633  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6634  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6635  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6636  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.
6637 
6638  Level: developer
6639 */
6640 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[])
6641 {
6642   PetscInt       numFields, foff, f;
6643 
6644   PetscFunctionBegin;
6645   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6646   PetscCall(PetscSectionGetNumFields(section, &numFields));
6647   for (f = 0, foff = 0; f < numFields; ++f) {
6648     PetscInt        fdof, cfdof;
6649     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6650     PetscInt        cind = 0, b;
6651     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6652 
6653     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6654     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6655     if (!cfdof || setBC) {
6656       for (b = 0; b < fdof; ++b) {
6657         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6658         const PetscInt ind    = indperm ? indperm[preind] : preind;
6659 
6660         indices[ind] = off+foff+b;
6661       }
6662     } else {
6663       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6664       for (b = 0; b < fdof; ++b) {
6665         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6666         const PetscInt ind    = indperm ? indperm[preind] : preind;
6667 
6668         if ((cind < cfdof) && (b == fcdofs[cind])) {
6669           indices[ind] = -(off+foff+b+1);
6670           ++cind;
6671         } else {
6672           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6673         }
6674       }
6675     }
6676     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6677     foffs[f] += fdof;
6678   }
6679   PetscFunctionReturn(0);
6680 }
6681 
6682 /*
6683   This version believes the globalSection offsets for each field, rather than just the point offset
6684 
6685  . foffs - The offset into 'indices' for each field, since it is segregated by field
6686 
6687  Notes:
6688  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6689  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6690 */
6691 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6692 {
6693   PetscInt       numFields, foff, f;
6694 
6695   PetscFunctionBegin;
6696   PetscCall(PetscSectionGetNumFields(section, &numFields));
6697   for (f = 0; f < numFields; ++f) {
6698     PetscInt        fdof, cfdof;
6699     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6700     PetscInt        cind = 0, b;
6701     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6702 
6703     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6704     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6705     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6706     if (!cfdof) {
6707       for (b = 0; b < fdof; ++b) {
6708         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6709         const PetscInt ind    = indperm ? indperm[preind] : preind;
6710 
6711         indices[ind] = foff+b;
6712       }
6713     } else {
6714       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6715       for (b = 0; b < fdof; ++b) {
6716         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6717         const PetscInt ind    = indperm ? indperm[preind] : preind;
6718 
6719         if ((cind < cfdof) && (b == fcdofs[cind])) {
6720           indices[ind] = -(foff+b+1);
6721           ++cind;
6722         } else {
6723           indices[ind] = foff+b-cind;
6724         }
6725       }
6726     }
6727     foffs[f] += fdof;
6728   }
6729   PetscFunctionReturn(0);
6730 }
6731 
6732 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)
6733 {
6734   Mat             cMat;
6735   PetscSection    aSec, cSec;
6736   IS              aIS;
6737   PetscInt        aStart = -1, aEnd = -1;
6738   const PetscInt  *anchors;
6739   PetscInt        numFields, f, p, q, newP = 0;
6740   PetscInt        newNumPoints = 0, newNumIndices = 0;
6741   PetscInt        *newPoints, *indices, *newIndices;
6742   PetscInt        maxAnchor, maxDof;
6743   PetscInt        newOffsets[32];
6744   PetscInt        *pointMatOffsets[32];
6745   PetscInt        *newPointOffsets[32];
6746   PetscScalar     *pointMat[32];
6747   PetscScalar     *newValues=NULL,*tmpValues;
6748   PetscBool       anyConstrained = PETSC_FALSE;
6749 
6750   PetscFunctionBegin;
6751   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6752   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6753   PetscCall(PetscSectionGetNumFields(section, &numFields));
6754 
6755   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
6756   /* if there are point-to-point constraints */
6757   if (aSec) {
6758     PetscCall(PetscArrayzero(newOffsets, 32));
6759     PetscCall(ISGetIndices(aIS,&anchors));
6760     PetscCall(PetscSectionGetChart(aSec,&aStart,&aEnd));
6761     /* figure out how many points are going to be in the new element matrix
6762      * (we allow double counting, because it's all just going to be summed
6763      * into the global matrix anyway) */
6764     for (p = 0; p < 2*numPoints; p+=2) {
6765       PetscInt b    = points[p];
6766       PetscInt bDof = 0, bSecDof;
6767 
6768       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6769       if (!bSecDof) {
6770         continue;
6771       }
6772       if (b >= aStart && b < aEnd) {
6773         PetscCall(PetscSectionGetDof(aSec,b,&bDof));
6774       }
6775       if (bDof) {
6776         /* this point is constrained */
6777         /* it is going to be replaced by its anchors */
6778         PetscInt bOff, q;
6779 
6780         anyConstrained = PETSC_TRUE;
6781         newNumPoints  += bDof;
6782         PetscCall(PetscSectionGetOffset(aSec,b,&bOff));
6783         for (q = 0; q < bDof; q++) {
6784           PetscInt a = anchors[bOff + q];
6785           PetscInt aDof;
6786 
6787           PetscCall(PetscSectionGetDof(section,a,&aDof));
6788           newNumIndices += aDof;
6789           for (f = 0; f < numFields; ++f) {
6790             PetscInt fDof;
6791 
6792             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6793             newOffsets[f+1] += fDof;
6794           }
6795         }
6796       }
6797       else {
6798         /* this point is not constrained */
6799         newNumPoints++;
6800         newNumIndices += bSecDof;
6801         for (f = 0; f < numFields; ++f) {
6802           PetscInt fDof;
6803 
6804           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6805           newOffsets[f+1] += fDof;
6806         }
6807       }
6808     }
6809   }
6810   if (!anyConstrained) {
6811     if (outNumPoints)  *outNumPoints  = 0;
6812     if (outNumIndices) *outNumIndices = 0;
6813     if (outPoints)     *outPoints     = NULL;
6814     if (outValues)     *outValues     = NULL;
6815     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6816     PetscFunctionReturn(0);
6817   }
6818 
6819   if (outNumPoints)  *outNumPoints  = newNumPoints;
6820   if (outNumIndices) *outNumIndices = newNumIndices;
6821 
6822   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6823 
6824   if (!outPoints && !outValues) {
6825     if (offsets) {
6826       for (f = 0; f <= numFields; f++) {
6827         offsets[f] = newOffsets[f];
6828       }
6829     }
6830     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6831     PetscFunctionReturn(0);
6832   }
6833 
6834   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6835 
6836   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6837 
6838   /* workspaces */
6839   if (numFields) {
6840     for (f = 0; f < numFields; f++) {
6841       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
6842       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
6843     }
6844   }
6845   else {
6846     PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
6847     PetscCall(DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]));
6848   }
6849 
6850   /* get workspaces for the point-to-point matrices */
6851   if (numFields) {
6852     PetscInt totalOffset, totalMatOffset;
6853 
6854     for (p = 0; p < numPoints; p++) {
6855       PetscInt b    = points[2*p];
6856       PetscInt bDof = 0, bSecDof;
6857 
6858       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6859       if (!bSecDof) {
6860         for (f = 0; f < numFields; f++) {
6861           newPointOffsets[f][p + 1] = 0;
6862           pointMatOffsets[f][p + 1] = 0;
6863         }
6864         continue;
6865       }
6866       if (b >= aStart && b < aEnd) {
6867         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6868       }
6869       if (bDof) {
6870         for (f = 0; f < numFields; f++) {
6871           PetscInt fDof, q, bOff, allFDof = 0;
6872 
6873           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6874           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6875           for (q = 0; q < bDof; q++) {
6876             PetscInt a = anchors[bOff + q];
6877             PetscInt aFDof;
6878 
6879             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6880             allFDof += aFDof;
6881           }
6882           newPointOffsets[f][p+1] = allFDof;
6883           pointMatOffsets[f][p+1] = fDof * allFDof;
6884         }
6885       }
6886       else {
6887         for (f = 0; f < numFields; f++) {
6888           PetscInt fDof;
6889 
6890           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6891           newPointOffsets[f][p+1] = fDof;
6892           pointMatOffsets[f][p+1] = 0;
6893         }
6894       }
6895     }
6896     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6897       newPointOffsets[f][0] = totalOffset;
6898       pointMatOffsets[f][0] = totalMatOffset;
6899       for (p = 0; p < numPoints; p++) {
6900         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6901         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6902       }
6903       totalOffset    = newPointOffsets[f][numPoints];
6904       totalMatOffset = pointMatOffsets[f][numPoints];
6905       PetscCall(DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
6906     }
6907   }
6908   else {
6909     for (p = 0; p < numPoints; p++) {
6910       PetscInt b    = points[2*p];
6911       PetscInt bDof = 0, bSecDof;
6912 
6913       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6914       if (!bSecDof) {
6915         newPointOffsets[0][p + 1] = 0;
6916         pointMatOffsets[0][p + 1] = 0;
6917         continue;
6918       }
6919       if (b >= aStart && b < aEnd) {
6920         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6921       }
6922       if (bDof) {
6923         PetscInt bOff, q, allDof = 0;
6924 
6925         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6926         for (q = 0; q < bDof; q++) {
6927           PetscInt a = anchors[bOff + q], aDof;
6928 
6929           PetscCall(PetscSectionGetDof(section, a, &aDof));
6930           allDof += aDof;
6931         }
6932         newPointOffsets[0][p+1] = allDof;
6933         pointMatOffsets[0][p+1] = bSecDof * allDof;
6934       }
6935       else {
6936         newPointOffsets[0][p+1] = bSecDof;
6937         pointMatOffsets[0][p+1] = 0;
6938       }
6939     }
6940     newPointOffsets[0][0] = 0;
6941     pointMatOffsets[0][0] = 0;
6942     for (p = 0; p < numPoints; p++) {
6943       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6944       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6945     }
6946     PetscCall(DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
6947   }
6948 
6949   /* output arrays */
6950   PetscCall(DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
6951 
6952   /* get the point-to-point matrices; construct newPoints */
6953   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6954   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6955   PetscCall(DMGetWorkArray(dm,maxDof,MPIU_INT,&indices));
6956   PetscCall(DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
6957   if (numFields) {
6958     for (p = 0, newP = 0; p < numPoints; p++) {
6959       PetscInt b    = points[2*p];
6960       PetscInt o    = points[2*p+1];
6961       PetscInt bDof = 0, bSecDof;
6962 
6963       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6964       if (!bSecDof) {
6965         continue;
6966       }
6967       if (b >= aStart && b < aEnd) {
6968         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6969       }
6970       if (bDof) {
6971         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6972 
6973         fStart[0] = 0;
6974         fEnd[0]   = 0;
6975         for (f = 0; f < numFields; f++) {
6976           PetscInt fDof;
6977 
6978           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
6979           fStart[f+1] = fStart[f] + fDof;
6980           fEnd[f+1]   = fStart[f+1];
6981         }
6982         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
6983         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
6984 
6985         fAnchorStart[0] = 0;
6986         fAnchorEnd[0]   = 0;
6987         for (f = 0; f < numFields; f++) {
6988           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
6989 
6990           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
6991           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
6992         }
6993         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6994         for (q = 0; q < bDof; q++) {
6995           PetscInt a = anchors[bOff + q], aOff;
6996 
6997           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
6998           newPoints[2*(newP + q)]     = a;
6999           newPoints[2*(newP + q) + 1] = 0;
7000           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7001           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7002         }
7003         newP += bDof;
7004 
7005         if (outValues) {
7006           /* get the point-to-point submatrix */
7007           for (f = 0; f < numFields; f++) {
7008             PetscCall(MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]));
7009           }
7010         }
7011       }
7012       else {
7013         newPoints[2 * newP]     = b;
7014         newPoints[2 * newP + 1] = o;
7015         newP++;
7016       }
7017     }
7018   } else {
7019     for (p = 0; p < numPoints; p++) {
7020       PetscInt b    = points[2*p];
7021       PetscInt o    = points[2*p+1];
7022       PetscInt bDof = 0, bSecDof;
7023 
7024       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7025       if (!bSecDof) {
7026         continue;
7027       }
7028       if (b >= aStart && b < aEnd) {
7029         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7030       }
7031       if (bDof) {
7032         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7033 
7034         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7035         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7036 
7037         PetscCall(PetscSectionGetOffset (aSec, b, &bOff));
7038         for (q = 0; q < bDof; q++) {
7039           PetscInt a = anchors[bOff + q], aOff;
7040 
7041           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7042 
7043           newPoints[2*(newP + q)]     = a;
7044           newPoints[2*(newP + q) + 1] = 0;
7045           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7046           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7047         }
7048         newP += bDof;
7049 
7050         /* get the point-to-point submatrix */
7051         if (outValues) {
7052           PetscCall(MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]));
7053         }
7054       }
7055       else {
7056         newPoints[2 * newP]     = b;
7057         newPoints[2 * newP + 1] = o;
7058         newP++;
7059       }
7060     }
7061   }
7062 
7063   if (outValues) {
7064     PetscCall(DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7065     PetscCall(PetscArrayzero(tmpValues,newNumIndices*numIndices));
7066     /* multiply constraints on the right */
7067     if (numFields) {
7068       for (f = 0; f < numFields; f++) {
7069         PetscInt oldOff = offsets[f];
7070 
7071         for (p = 0; p < numPoints; p++) {
7072           PetscInt cStart = newPointOffsets[f][p];
7073           PetscInt b      = points[2 * p];
7074           PetscInt c, r, k;
7075           PetscInt dof;
7076 
7077           PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7078           if (!dof) {
7079             continue;
7080           }
7081           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7082             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
7083             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
7084 
7085             for (r = 0; r < numIndices; r++) {
7086               for (c = 0; c < nCols; c++) {
7087                 for (k = 0; k < dof; k++) {
7088                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7089                 }
7090               }
7091             }
7092           }
7093           else {
7094             /* copy this column as is */
7095             for (r = 0; r < numIndices; r++) {
7096               for (c = 0; c < dof; c++) {
7097                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7098               }
7099             }
7100           }
7101           oldOff += dof;
7102         }
7103       }
7104     }
7105     else {
7106       PetscInt oldOff = 0;
7107       for (p = 0; p < numPoints; p++) {
7108         PetscInt cStart = newPointOffsets[0][p];
7109         PetscInt b      = points[2 * p];
7110         PetscInt c, r, k;
7111         PetscInt dof;
7112 
7113         PetscCall(PetscSectionGetDof(section,b,&dof));
7114         if (!dof) {
7115           continue;
7116         }
7117         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7118           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
7119           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
7120 
7121           for (r = 0; r < numIndices; r++) {
7122             for (c = 0; c < nCols; c++) {
7123               for (k = 0; k < dof; k++) {
7124                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7125               }
7126             }
7127           }
7128         }
7129         else {
7130           /* copy this column as is */
7131           for (r = 0; r < numIndices; r++) {
7132             for (c = 0; c < dof; c++) {
7133               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7134             }
7135           }
7136         }
7137         oldOff += dof;
7138       }
7139     }
7140 
7141     if (multiplyLeft) {
7142       PetscCall(DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues));
7143       PetscCall(PetscArrayzero(newValues,newNumIndices*newNumIndices));
7144       /* multiply constraints transpose on the left */
7145       if (numFields) {
7146         for (f = 0; f < numFields; f++) {
7147           PetscInt oldOff = offsets[f];
7148 
7149           for (p = 0; p < numPoints; p++) {
7150             PetscInt rStart = newPointOffsets[f][p];
7151             PetscInt b      = points[2 * p];
7152             PetscInt c, r, k;
7153             PetscInt dof;
7154 
7155             PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7156             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7157               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
7158               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
7159 
7160               for (r = 0; r < nRows; r++) {
7161                 for (c = 0; c < newNumIndices; c++) {
7162                   for (k = 0; k < dof; k++) {
7163                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7164                   }
7165                 }
7166               }
7167             }
7168             else {
7169               /* copy this row as is */
7170               for (r = 0; r < dof; r++) {
7171                 for (c = 0; c < newNumIndices; c++) {
7172                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7173                 }
7174               }
7175             }
7176             oldOff += dof;
7177           }
7178         }
7179       }
7180       else {
7181         PetscInt oldOff = 0;
7182 
7183         for (p = 0; p < numPoints; p++) {
7184           PetscInt rStart = newPointOffsets[0][p];
7185           PetscInt b      = points[2 * p];
7186           PetscInt c, r, k;
7187           PetscInt dof;
7188 
7189           PetscCall(PetscSectionGetDof(section,b,&dof));
7190           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7191             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
7192             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
7193 
7194             for (r = 0; r < nRows; r++) {
7195               for (c = 0; c < newNumIndices; c++) {
7196                 for (k = 0; k < dof; k++) {
7197                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7198                 }
7199               }
7200             }
7201           }
7202           else {
7203             /* copy this row as is */
7204             for (r = 0; r < dof; r++) {
7205               for (c = 0; c < newNumIndices; c++) {
7206                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7207               }
7208             }
7209           }
7210           oldOff += dof;
7211         }
7212       }
7213 
7214       PetscCall(DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7215     }
7216     else {
7217       newValues = tmpValues;
7218     }
7219   }
7220 
7221   /* clean up */
7222   PetscCall(DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices));
7223   PetscCall(DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
7224 
7225   if (numFields) {
7226     for (f = 0; f < numFields; f++) {
7227       PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
7228       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
7229       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
7230     }
7231   }
7232   else {
7233     PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
7234     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
7235     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]));
7236   }
7237   PetscCall(ISRestoreIndices(aIS,&anchors));
7238 
7239   /* output */
7240   if (outPoints) {
7241     *outPoints = newPoints;
7242   }
7243   else {
7244     PetscCall(DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
7245   }
7246   if (outValues) {
7247     *outValues = newValues;
7248   }
7249   for (f = 0; f <= numFields; f++) {
7250     offsets[f] = newOffsets[f];
7251   }
7252   PetscFunctionReturn(0);
7253 }
7254 
7255 /*@C
7256   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7257 
7258   Not collective
7259 
7260   Input Parameters:
7261 + dm         - The DM
7262 . section    - The PetscSection describing the points (a local section)
7263 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7264 . point      - The point defining the closure
7265 - useClPerm  - Use the closure point permutation if available
7266 
7267   Output Parameters:
7268 + numIndices - The number of dof indices in the closure of point with the input sections
7269 . indices    - The dof indices
7270 . outOffsets - Array to write the field offsets into, or NULL
7271 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7272 
7273   Notes:
7274   Must call DMPlexRestoreClosureIndices() to free allocated memory
7275 
7276   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7277   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7278   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7279   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7280   indices (with the above semantics) are implied.
7281 
7282   Level: advanced
7283 
7284 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7285 @*/
7286 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7287                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7288 {
7289   /* Closure ordering */
7290   PetscSection        clSection;
7291   IS                  clPoints;
7292   const PetscInt     *clp;
7293   PetscInt           *points;
7294   const PetscInt     *clperm = NULL;
7295   /* Dof permutation and sign flips */
7296   const PetscInt    **perms[32] = {NULL};
7297   const PetscScalar **flips[32] = {NULL};
7298   PetscScalar        *valCopy   = NULL;
7299   /* Hanging node constraints */
7300   PetscInt           *pointsC = NULL;
7301   PetscScalar        *valuesC = NULL;
7302   PetscInt            NclC, NiC;
7303 
7304   PetscInt           *idx;
7305   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
7306   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7307 
7308   PetscFunctionBeginHot;
7309   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7310   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7311   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7312   if (numIndices) PetscValidIntPointer(numIndices, 6);
7313   if (indices)    PetscValidPointer(indices, 7);
7314   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7315   if (values)     PetscValidPointer(values, 9);
7316   PetscCall(PetscSectionGetNumFields(section, &Nf));
7317   PetscCheck(Nf <= 31,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7318   PetscCall(PetscArrayzero(offsets, 32));
7319   /* 1) Get points in closure */
7320   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7321   if (useClPerm) {
7322     PetscInt depth, clsize;
7323     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7324     for (clsize=0,p=0; p<Ncl; p++) {
7325       PetscInt dof;
7326       PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
7327       clsize += dof;
7328     }
7329     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
7330   }
7331   /* 2) Get number of indices on these points and field offsets from section */
7332   for (p = 0; p < Ncl*2; p += 2) {
7333     PetscInt dof, fdof;
7334 
7335     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7336     for (f = 0; f < Nf; ++f) {
7337       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7338       offsets[f+1] += fdof;
7339     }
7340     Ni += dof;
7341   }
7342   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
7343   PetscCheck(!Nf || offsets[Nf] == Ni,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7344   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7345   for (f = 0; f < PetscMax(1, Nf); ++f) {
7346     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7347     else    PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7348     /* may need to apply sign changes to the element matrix */
7349     if (values && flips[f]) {
7350       PetscInt foffset = offsets[f];
7351 
7352       for (p = 0; p < Ncl; ++p) {
7353         PetscInt           pnt  = points[2*p], fdof;
7354         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7355 
7356         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7357         else     PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7358         if (flip) {
7359           PetscInt i, j, k;
7360 
7361           if (!valCopy) {
7362             PetscCall(DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7363             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7364             *values = valCopy;
7365           }
7366           for (i = 0; i < fdof; ++i) {
7367             PetscScalar fval = flip[i];
7368 
7369             for (k = 0; k < Ni; ++k) {
7370               valCopy[Ni * (foffset + i) + k] *= fval;
7371               valCopy[Ni * k + (foffset + i)] *= fval;
7372             }
7373           }
7374         }
7375         foffset += fdof;
7376       }
7377     }
7378   }
7379   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7380   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7381   if (NclC) {
7382     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7383     for (f = 0; f < PetscMax(1, Nf); ++f) {
7384       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7385       else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7386     }
7387     for (f = 0; f < PetscMax(1, Nf); ++f) {
7388       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7389       else    PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7390     }
7391     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7392     Ncl     = NclC;
7393     Ni      = NiC;
7394     points  = pointsC;
7395     if (values) *values = valuesC;
7396   }
7397   /* 5) Calculate indices */
7398   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7399   if (Nf) {
7400     PetscInt  idxOff;
7401     PetscBool useFieldOffsets;
7402 
7403     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
7404     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7405     if (useFieldOffsets) {
7406       for (p = 0; p < Ncl; ++p) {
7407         const PetscInt pnt = points[p*2];
7408 
7409         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7410       }
7411     } else {
7412       for (p = 0; p < Ncl; ++p) {
7413         const PetscInt pnt = points[p*2];
7414 
7415         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7416         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7417          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7418          * global section. */
7419         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7420       }
7421     }
7422   } else {
7423     PetscInt off = 0, idxOff;
7424 
7425     for (p = 0; p < Ncl; ++p) {
7426       const PetscInt  pnt  = points[p*2];
7427       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7428 
7429       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7430       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7431        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7432       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7433     }
7434   }
7435   /* 6) Cleanup */
7436   for (f = 0; f < PetscMax(1, Nf); ++f) {
7437     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7438     else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7439   }
7440   if (NclC) {
7441     PetscCall(DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC));
7442   } else {
7443     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7444   }
7445 
7446   if (numIndices) *numIndices = Ni;
7447   if (indices)    *indices    = idx;
7448   PetscFunctionReturn(0);
7449 }
7450 
7451 /*@C
7452   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7453 
7454   Not collective
7455 
7456   Input Parameters:
7457 + dm         - The DM
7458 . section    - The PetscSection describing the points (a local section)
7459 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7460 . point      - The point defining the closure
7461 - useClPerm  - Use the closure point permutation if available
7462 
7463   Output Parameters:
7464 + numIndices - The number of dof indices in the closure of point with the input sections
7465 . indices    - The dof indices
7466 . outOffsets - Array to write the field offsets into, or NULL
7467 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7468 
7469   Notes:
7470   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7471 
7472   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7473   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7474   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7475   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7476   indices (with the above semantics) are implied.
7477 
7478   Level: advanced
7479 
7480 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7481 @*/
7482 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7483                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7484 {
7485   PetscFunctionBegin;
7486   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7487   PetscValidPointer(indices, 7);
7488   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7489   PetscFunctionReturn(0);
7490 }
7491 
7492 /*@C
7493   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7494 
7495   Not collective
7496 
7497   Input Parameters:
7498 + dm - The DM
7499 . section - The section describing the layout in v, or NULL to use the default section
7500 . globalSection - The section describing the layout in v, or NULL to use the default global section
7501 . A - The matrix
7502 . point - The point in the DM
7503 . values - The array of values
7504 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7505 
7506   Fortran Notes:
7507   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7508 
7509   Level: intermediate
7510 
7511 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7512 @*/
7513 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7514 {
7515   DM_Plex           *mesh = (DM_Plex*) dm->data;
7516   PetscInt          *indices;
7517   PetscInt           numIndices;
7518   const PetscScalar *valuesOrig = values;
7519   PetscErrorCode     ierr;
7520 
7521   PetscFunctionBegin;
7522   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7523   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7524   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7525   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7526   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7527   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7528 
7529   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7530 
7531   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7532   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7533   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7534   if (ierr) {
7535     PetscMPIInt    rank;
7536 
7537     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7538     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7539     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7540     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7541     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7542     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7543   }
7544   if (mesh->printFEM > 1) {
7545     PetscInt i;
7546     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7547     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7548     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7549   }
7550 
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   PetscFunctionReturn(0);
7554 }
7555 
7556 /*@C
7557   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7558 
7559   Not collective
7560 
7561   Input Parameters:
7562 + dmRow - The DM for the row fields
7563 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7564 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7565 . dmCol - The DM for the column fields
7566 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7567 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7568 . A - The matrix
7569 . point - The point in the DMs
7570 . values - The array of values
7571 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7572 
7573   Level: intermediate
7574 
7575 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7576 @*/
7577 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7578 {
7579   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7580   PetscInt          *indicesRow, *indicesCol;
7581   PetscInt           numIndicesRow, numIndicesCol;
7582   const PetscScalar *valuesOrig = values;
7583   PetscErrorCode     ierr;
7584 
7585   PetscFunctionBegin;
7586   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7587   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7588   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7589   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7590   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7591   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7592   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7593   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7594   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7595   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7596   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7597 
7598   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7599   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7600 
7601   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7602   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7603   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7604   if (ierr) {
7605     PetscMPIInt    rank;
7606 
7607     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7608     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7609     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7610     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7611     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7612     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7613   }
7614 
7615   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7616   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7617   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7618   PetscFunctionReturn(0);
7619 }
7620 
7621 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7622 {
7623   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7624   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7625   PetscInt       *cpoints = NULL;
7626   PetscInt       *findices, *cindices;
7627   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7628   PetscInt        foffsets[32], coffsets[32];
7629   DMPolytopeType  ct;
7630   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7631   PetscErrorCode  ierr;
7632 
7633   PetscFunctionBegin;
7634   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7635   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7636   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7637   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7638   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7639   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7640   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7641   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7642   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7643   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7644   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7645   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7646   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7647   PetscCall(PetscArrayzero(foffsets, 32));
7648   PetscCall(PetscArrayzero(coffsets, 32));
7649   /* Column indices */
7650   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7651   maxFPoints = numCPoints;
7652   /* Compress out points not in the section */
7653   /*   TODO: Squeeze out points with 0 dof as well */
7654   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7655   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7656     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7657       cpoints[q*2]   = cpoints[p];
7658       cpoints[q*2+1] = cpoints[p+1];
7659       ++q;
7660     }
7661   }
7662   numCPoints = q;
7663   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7664     PetscInt fdof;
7665 
7666     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7667     if (!dof) continue;
7668     for (f = 0; f < numFields; ++f) {
7669       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7670       coffsets[f+1] += fdof;
7671     }
7672     numCIndices += dof;
7673   }
7674   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7675   /* Row indices */
7676   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7677   {
7678     DMPlexTransform tr;
7679     DMPolytopeType *rct;
7680     PetscInt       *rsize, *rcone, *rornt, Nt;
7681 
7682     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7683     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7684     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7685     numSubcells = rsize[Nt-1];
7686     PetscCall(DMPlexTransformDestroy(&tr));
7687   }
7688   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7689   for (r = 0, q = 0; r < numSubcells; ++r) {
7690     /* TODO Map from coarse to fine cells */
7691     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7692     /* Compress out points not in the section */
7693     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7694     for (p = 0; p < numFPoints*2; p += 2) {
7695       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7696         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7697         if (!dof) continue;
7698         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7699         if (s < q) continue;
7700         ftotpoints[q*2]   = fpoints[p];
7701         ftotpoints[q*2+1] = fpoints[p+1];
7702         ++q;
7703       }
7704     }
7705     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7706   }
7707   numFPoints = q;
7708   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7709     PetscInt fdof;
7710 
7711     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7712     if (!dof) continue;
7713     for (f = 0; f < numFields; ++f) {
7714       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7715       foffsets[f+1] += fdof;
7716     }
7717     numFIndices += dof;
7718   }
7719   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7720 
7721   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7722   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7723   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7724   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7725   if (numFields) {
7726     const PetscInt **permsF[32] = {NULL};
7727     const PetscInt **permsC[32] = {NULL};
7728 
7729     for (f = 0; f < numFields; f++) {
7730       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7731       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7732     }
7733     for (p = 0; p < numFPoints; p++) {
7734       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7735       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7736     }
7737     for (p = 0; p < numCPoints; p++) {
7738       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7739       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7740     }
7741     for (f = 0; f < numFields; f++) {
7742       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7743       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7744     }
7745   } else {
7746     const PetscInt **permsF = NULL;
7747     const PetscInt **permsC = NULL;
7748 
7749     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7750     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7751     for (p = 0, off = 0; p < numFPoints; p++) {
7752       const PetscInt *perm = permsF ? permsF[p] : NULL;
7753 
7754       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7755       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7756     }
7757     for (p = 0, off = 0; p < numCPoints; p++) {
7758       const PetscInt *perm = permsC ? permsC[p] : NULL;
7759 
7760       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7761       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7762     }
7763     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7764     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7765   }
7766   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7767   /* TODO: flips */
7768   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7769   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7770   if (ierr) {
7771     PetscMPIInt    rank;
7772 
7773     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7774     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7775     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7776     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7777     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7778   }
7779   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7780   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7781   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7782   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7783   PetscFunctionReturn(0);
7784 }
7785 
7786 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7787 {
7788   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7789   PetscInt      *cpoints = NULL;
7790   PetscInt       foffsets[32], coffsets[32];
7791   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7792   DMPolytopeType ct;
7793   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7794 
7795   PetscFunctionBegin;
7796   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7797   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7798   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7799   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7800   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7801   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7802   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7803   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7804   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7805   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7806   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7807   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7808   PetscCall(PetscArrayzero(foffsets, 32));
7809   PetscCall(PetscArrayzero(coffsets, 32));
7810   /* Column indices */
7811   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7812   maxFPoints = numCPoints;
7813   /* Compress out points not in the section */
7814   /*   TODO: Squeeze out points with 0 dof as well */
7815   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7816   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7817     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7818       cpoints[q*2]   = cpoints[p];
7819       cpoints[q*2+1] = cpoints[p+1];
7820       ++q;
7821     }
7822   }
7823   numCPoints = q;
7824   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7825     PetscInt fdof;
7826 
7827     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7828     if (!dof) continue;
7829     for (f = 0; f < numFields; ++f) {
7830       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7831       coffsets[f+1] += fdof;
7832     }
7833     numCIndices += dof;
7834   }
7835   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7836   /* Row indices */
7837   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7838   {
7839     DMPlexTransform tr;
7840     DMPolytopeType *rct;
7841     PetscInt       *rsize, *rcone, *rornt, Nt;
7842 
7843     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7844     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7845     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7846     numSubcells = rsize[Nt-1];
7847     PetscCall(DMPlexTransformDestroy(&tr));
7848   }
7849   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7850   for (r = 0, q = 0; r < numSubcells; ++r) {
7851     /* TODO Map from coarse to fine cells */
7852     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7853     /* Compress out points not in the section */
7854     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7855     for (p = 0; p < numFPoints*2; p += 2) {
7856       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7857         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7858         if (!dof) continue;
7859         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7860         if (s < q) continue;
7861         ftotpoints[q*2]   = fpoints[p];
7862         ftotpoints[q*2+1] = fpoints[p+1];
7863         ++q;
7864       }
7865     }
7866     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7867   }
7868   numFPoints = q;
7869   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7870     PetscInt fdof;
7871 
7872     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7873     if (!dof) continue;
7874     for (f = 0; f < numFields; ++f) {
7875       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7876       foffsets[f+1] += fdof;
7877     }
7878     numFIndices += dof;
7879   }
7880   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7881 
7882   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7883   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7884   if (numFields) {
7885     const PetscInt **permsF[32] = {NULL};
7886     const PetscInt **permsC[32] = {NULL};
7887 
7888     for (f = 0; f < numFields; f++) {
7889       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7890       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7891     }
7892     for (p = 0; p < numFPoints; p++) {
7893       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7894       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7895     }
7896     for (p = 0; p < numCPoints; p++) {
7897       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7898       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7899     }
7900     for (f = 0; f < numFields; f++) {
7901       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7902       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7903     }
7904   } else {
7905     const PetscInt **permsF = NULL;
7906     const PetscInt **permsC = NULL;
7907 
7908     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7909     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7910     for (p = 0, off = 0; p < numFPoints; p++) {
7911       const PetscInt *perm = permsF ? permsF[p] : NULL;
7912 
7913       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7914       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7915     }
7916     for (p = 0, off = 0; p < numCPoints; p++) {
7917       const PetscInt *perm = permsC ? permsC[p] : NULL;
7918 
7919       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7920       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7921     }
7922     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7923     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7924   }
7925   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7926   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7927   PetscFunctionReturn(0);
7928 }
7929 
7930 /*@C
7931   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7932 
7933   Input Parameter:
7934 . dm   - The DMPlex object
7935 
7936   Output Parameter:
7937 . cellHeight - The height of a cell
7938 
7939   Level: developer
7940 
7941 .seealso `DMPlexSetVTKCellHeight()`
7942 @*/
7943 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7944 {
7945   DM_Plex *mesh = (DM_Plex*) dm->data;
7946 
7947   PetscFunctionBegin;
7948   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7949   PetscValidIntPointer(cellHeight, 2);
7950   *cellHeight = mesh->vtkCellHeight;
7951   PetscFunctionReturn(0);
7952 }
7953 
7954 /*@C
7955   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7956 
7957   Input Parameters:
7958 + dm   - The DMPlex object
7959 - cellHeight - The height of a cell
7960 
7961   Level: developer
7962 
7963 .seealso `DMPlexGetVTKCellHeight()`
7964 @*/
7965 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7966 {
7967   DM_Plex *mesh = (DM_Plex*) dm->data;
7968 
7969   PetscFunctionBegin;
7970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7971   mesh->vtkCellHeight = cellHeight;
7972   PetscFunctionReturn(0);
7973 }
7974 
7975 /*@
7976   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7977 
7978   Input Parameter:
7979 . dm - The DMPlex object
7980 
7981   Output Parameters:
7982 + gcStart - The first ghost cell, or NULL
7983 - gcEnd   - The upper bound on ghost cells, or NULL
7984 
7985   Level: advanced
7986 
7987 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
7988 @*/
7989 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
7990 {
7991   DMLabel        ctLabel;
7992 
7993   PetscFunctionBegin;
7994   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7995   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
7996   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
7997   PetscFunctionReturn(0);
7998 }
7999 
8000 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8001 {
8002   PetscSection   section, globalSection;
8003   PetscInt      *numbers, p;
8004 
8005   PetscFunctionBegin;
8006   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
8007   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8008   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8009   for (p = pStart; p < pEnd; ++p) {
8010     PetscCall(PetscSectionSetDof(section, p, 1));
8011   }
8012   PetscCall(PetscSectionSetUp(section));
8013   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8014   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8015   for (p = pStart; p < pEnd; ++p) {
8016     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8017     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8018     else                       numbers[p-pStart] += shift;
8019   }
8020   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8021   if (globalSize) {
8022     PetscLayout layout;
8023     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8024     PetscCall(PetscLayoutGetSize(layout, globalSize));
8025     PetscCall(PetscLayoutDestroy(&layout));
8026   }
8027   PetscCall(PetscSectionDestroy(&section));
8028   PetscCall(PetscSectionDestroy(&globalSection));
8029   PetscFunctionReturn(0);
8030 }
8031 
8032 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8033 {
8034   PetscInt       cellHeight, cStart, cEnd;
8035 
8036   PetscFunctionBegin;
8037   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8038   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8039   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8040   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8041   PetscFunctionReturn(0);
8042 }
8043 
8044 /*@
8045   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8046 
8047   Input Parameter:
8048 . dm   - The DMPlex object
8049 
8050   Output Parameter:
8051 . globalCellNumbers - Global cell numbers for all cells on this process
8052 
8053   Level: developer
8054 
8055 .seealso `DMPlexGetVertexNumbering()`
8056 @*/
8057 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8058 {
8059   DM_Plex       *mesh = (DM_Plex*) dm->data;
8060 
8061   PetscFunctionBegin;
8062   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8063   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8064   *globalCellNumbers = mesh->globalCellNumbers;
8065   PetscFunctionReturn(0);
8066 }
8067 
8068 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8069 {
8070   PetscInt       vStart, vEnd;
8071 
8072   PetscFunctionBegin;
8073   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8074   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8075   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8076   PetscFunctionReturn(0);
8077 }
8078 
8079 /*@
8080   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8081 
8082   Input Parameter:
8083 . dm   - The DMPlex object
8084 
8085   Output Parameter:
8086 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8087 
8088   Level: developer
8089 
8090 .seealso `DMPlexGetCellNumbering()`
8091 @*/
8092 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8093 {
8094   DM_Plex       *mesh = (DM_Plex*) dm->data;
8095 
8096   PetscFunctionBegin;
8097   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8098   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8099   *globalVertexNumbers = mesh->globalVertexNumbers;
8100   PetscFunctionReturn(0);
8101 }
8102 
8103 /*@
8104   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
8105 
8106   Input Parameter:
8107 . dm   - The DMPlex object
8108 
8109   Output Parameter:
8110 . globalPointNumbers - Global numbers for all points on this process
8111 
8112   Level: developer
8113 
8114 .seealso `DMPlexGetCellNumbering()`
8115 @*/
8116 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8117 {
8118   IS             nums[4];
8119   PetscInt       depths[4], gdepths[4], starts[4];
8120   PetscInt       depth, d, shift = 0;
8121 
8122   PetscFunctionBegin;
8123   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8124   PetscCall(DMPlexGetDepth(dm, &depth));
8125   /* For unstratified meshes use dim instead of depth */
8126   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8127   for (d = 0; d <= depth; ++d) {
8128     PetscInt end;
8129 
8130     depths[d] = depth-d;
8131     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8132     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8133   }
8134   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8135   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8136   for (d = 0; d <= depth; ++d) {
8137     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]);
8138   }
8139   for (d = 0; d <= depth; ++d) {
8140     PetscInt pStart, pEnd, gsize;
8141 
8142     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8143     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8144     shift += gsize;
8145   }
8146   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8147   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8148   PetscFunctionReturn(0);
8149 }
8150 
8151 /*@
8152   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8153 
8154   Input Parameter:
8155 . dm - The DMPlex object
8156 
8157   Output Parameter:
8158 . ranks - The rank field
8159 
8160   Options Database Keys:
8161 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8162 
8163   Level: intermediate
8164 
8165 .seealso: `DMView()`
8166 @*/
8167 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8168 {
8169   DM             rdm;
8170   PetscFE        fe;
8171   PetscScalar   *r;
8172   PetscMPIInt    rank;
8173   DMPolytopeType ct;
8174   PetscInt       dim, cStart, cEnd, c;
8175   PetscBool      simplex;
8176 
8177   PetscFunctionBeginUser;
8178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8179   PetscValidPointer(ranks, 2);
8180   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8181   PetscCall(DMClone(dm, &rdm));
8182   PetscCall(DMGetDimension(rdm, &dim));
8183   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8184   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8185   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8186   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8187   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8188   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8189   PetscCall(PetscFEDestroy(&fe));
8190   PetscCall(DMCreateDS(rdm));
8191   PetscCall(DMCreateGlobalVector(rdm, ranks));
8192   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8193   PetscCall(VecGetArray(*ranks, &r));
8194   for (c = cStart; c < cEnd; ++c) {
8195     PetscScalar *lr;
8196 
8197     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8198     if (lr) *lr = rank;
8199   }
8200   PetscCall(VecRestoreArray(*ranks, &r));
8201   PetscCall(DMDestroy(&rdm));
8202   PetscFunctionReturn(0);
8203 }
8204 
8205 /*@
8206   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8207 
8208   Input Parameters:
8209 + dm    - The DMPlex
8210 - label - The DMLabel
8211 
8212   Output Parameter:
8213 . val - The label value field
8214 
8215   Options Database Keys:
8216 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8217 
8218   Level: intermediate
8219 
8220 .seealso: `DMView()`
8221 @*/
8222 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8223 {
8224   DM             rdm;
8225   PetscFE        fe;
8226   PetscScalar   *v;
8227   PetscInt       dim, cStart, cEnd, c;
8228 
8229   PetscFunctionBeginUser;
8230   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8231   PetscValidPointer(label, 2);
8232   PetscValidPointer(val, 3);
8233   PetscCall(DMClone(dm, &rdm));
8234   PetscCall(DMGetDimension(rdm, &dim));
8235   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8236   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8237   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8238   PetscCall(PetscFEDestroy(&fe));
8239   PetscCall(DMCreateDS(rdm));
8240   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8241   PetscCall(DMCreateGlobalVector(rdm, val));
8242   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8243   PetscCall(VecGetArray(*val, &v));
8244   for (c = cStart; c < cEnd; ++c) {
8245     PetscScalar *lv;
8246     PetscInt     cval;
8247 
8248     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8249     PetscCall(DMLabelGetValue(label, c, &cval));
8250     *lv = cval;
8251   }
8252   PetscCall(VecRestoreArray(*val, &v));
8253   PetscCall(DMDestroy(&rdm));
8254   PetscFunctionReturn(0);
8255 }
8256 
8257 /*@
8258   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8259 
8260   Input Parameter:
8261 . dm - The DMPlex object
8262 
8263   Notes:
8264   This is a useful diagnostic when creating meshes programmatically.
8265 
8266   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8267 
8268   Level: developer
8269 
8270 .seealso: `DMCreate()`, `DMSetFromOptions()`
8271 @*/
8272 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8273 {
8274   PetscSection    coneSection, supportSection;
8275   const PetscInt *cone, *support;
8276   PetscInt        coneSize, c, supportSize, s;
8277   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8278   PetscBool       storagecheck = PETSC_TRUE;
8279 
8280   PetscFunctionBegin;
8281   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8282   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8283   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8284   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8285   /* Check that point p is found in the support of its cone points, and vice versa */
8286   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8287   for (p = pStart; p < pEnd; ++p) {
8288     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8289     PetscCall(DMPlexGetCone(dm, p, &cone));
8290     for (c = 0; c < coneSize; ++c) {
8291       PetscBool dup = PETSC_FALSE;
8292       PetscInt  d;
8293       for (d = c-1; d >= 0; --d) {
8294         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8295       }
8296       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8297       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8298       for (s = 0; s < supportSize; ++s) {
8299         if (support[s] == p) break;
8300       }
8301       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8302         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8303         for (s = 0; s < coneSize; ++s) {
8304           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8305         }
8306         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8307         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8308         for (s = 0; s < supportSize; ++s) {
8309           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8310         }
8311         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8312         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]);
8313         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8314       }
8315     }
8316     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8317     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8318     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8319     PetscCall(DMPlexGetSupport(dm, p, &support));
8320     for (s = 0; s < supportSize; ++s) {
8321       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8322       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8323       for (c = 0; c < coneSize; ++c) {
8324         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8325         if (cone[c] != pp) { c = 0; break; }
8326         if (cone[c] == p) break;
8327       }
8328       if (c >= coneSize) {
8329         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8330         for (c = 0; c < supportSize; ++c) {
8331           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8332         }
8333         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8334         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8335         for (c = 0; c < coneSize; ++c) {
8336           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8337         }
8338         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8339         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8340       }
8341     }
8342   }
8343   if (storagecheck) {
8344     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8345     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8346     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8347   }
8348   PetscFunctionReturn(0);
8349 }
8350 
8351 /*
8352   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.
8353 */
8354 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8355 {
8356   DMPolytopeType  cct;
8357   PetscInt        ptpoints[4];
8358   const PetscInt *cone, *ccone, *ptcone;
8359   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8360 
8361   PetscFunctionBegin;
8362   *unsplit = 0;
8363   switch (ct) {
8364     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8365       ptpoints[npt++] = c;
8366       break;
8367     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8368       PetscCall(DMPlexGetCone(dm, c, &cone));
8369       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8370       for (cp = 0; cp < coneSize; ++cp) {
8371         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8372         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8373       }
8374       break;
8375     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8376     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8377       PetscCall(DMPlexGetCone(dm, c, &cone));
8378       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8379       for (cp = 0; cp < coneSize; ++cp) {
8380         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8381         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8382         for (ccp = 0; ccp < cconeSize; ++ccp) {
8383           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8384           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8385             PetscInt p;
8386             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8387             if (p == npt) ptpoints[npt++] = ccone[ccp];
8388           }
8389         }
8390       }
8391       break;
8392     default: break;
8393   }
8394   for (pt = 0; pt < npt; ++pt) {
8395     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8396     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8397   }
8398   PetscFunctionReturn(0);
8399 }
8400 
8401 /*@
8402   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8403 
8404   Input Parameters:
8405 + dm - The DMPlex object
8406 - cellHeight - Normally 0
8407 
8408   Notes:
8409   This is a useful diagnostic when creating meshes programmatically.
8410   Currently applicable only to homogeneous simplex or tensor meshes.
8411 
8412   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8413 
8414   Level: developer
8415 
8416 .seealso: `DMCreate()`, `DMSetFromOptions()`
8417 @*/
8418 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8419 {
8420   DMPlexInterpolatedFlag interp;
8421   DMPolytopeType         ct;
8422   PetscInt               vStart, vEnd, cStart, cEnd, c;
8423 
8424   PetscFunctionBegin;
8425   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8426   PetscCall(DMPlexIsInterpolated(dm, &interp));
8427   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8428   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8429   for (c = cStart; c < cEnd; ++c) {
8430     PetscInt *closure = NULL;
8431     PetscInt  coneSize, closureSize, cl, Nv = 0;
8432 
8433     PetscCall(DMPlexGetCellType(dm, c, &ct));
8434     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8435     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8436     if (interp == DMPLEX_INTERPOLATED_FULL) {
8437       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8438       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));
8439     }
8440     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8441     for (cl = 0; cl < closureSize*2; cl += 2) {
8442       const PetscInt p = closure[cl];
8443       if ((p >= vStart) && (p < vEnd)) ++Nv;
8444     }
8445     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8446     /* Special Case: Tensor faces with identified vertices */
8447     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8448       PetscInt unsplit;
8449 
8450       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8451       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8452     }
8453     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));
8454   }
8455   PetscFunctionReturn(0);
8456 }
8457 
8458 /*@
8459   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8460 
8461   Collective
8462 
8463   Input Parameters:
8464 + dm - The DMPlex object
8465 - cellHeight - Normally 0
8466 
8467   Notes:
8468   This is a useful diagnostic when creating meshes programmatically.
8469   This routine is only relevant for meshes that are fully interpolated across all ranks.
8470   It will error out if a partially interpolated mesh is given on some rank.
8471   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8472 
8473   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8474 
8475   Level: developer
8476 
8477 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8478 @*/
8479 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8480 {
8481   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8482   DMPlexInterpolatedFlag interpEnum;
8483 
8484   PetscFunctionBegin;
8485   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8486   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8487   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8488   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8489     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8490     PetscFunctionReturn(0);
8491   }
8492 
8493   PetscCall(DMGetDimension(dm, &dim));
8494   PetscCall(DMPlexGetDepth(dm, &depth));
8495   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8496   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8497     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8498     for (c = cStart; c < cEnd; ++c) {
8499       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8500       const DMPolytopeType *faceTypes;
8501       DMPolytopeType        ct;
8502       PetscInt              numFaces, coneSize, f;
8503       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8504 
8505       PetscCall(DMPlexGetCellType(dm, c, &ct));
8506       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8507       if (unsplit) continue;
8508       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8509       PetscCall(DMPlexGetCone(dm, c, &cone));
8510       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8511       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8512       for (cl = 0; cl < closureSize*2; cl += 2) {
8513         const PetscInt p = closure[cl];
8514         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8515       }
8516       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8517       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);
8518       for (f = 0; f < numFaces; ++f) {
8519         DMPolytopeType fct;
8520         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8521 
8522         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8523         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8524         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8525           const PetscInt p = fclosure[cl];
8526           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8527         }
8528         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]);
8529         for (v = 0; v < fnumCorners; ++v) {
8530           if (fclosure[v] != faces[fOff+v]) {
8531             PetscInt v1;
8532 
8533             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8534             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8535             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8536             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff+v1]));
8537             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8538             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]);
8539           }
8540         }
8541         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8542         fOff += faceSizes[f];
8543       }
8544       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8545       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8546     }
8547   }
8548   PetscFunctionReturn(0);
8549 }
8550 
8551 /*@
8552   DMPlexCheckGeometry - Check the geometry of mesh cells
8553 
8554   Input Parameter:
8555 . dm - The DMPlex object
8556 
8557   Notes:
8558   This is a useful diagnostic when creating meshes programmatically.
8559 
8560   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8561 
8562   Level: developer
8563 
8564 .seealso: `DMCreate()`, `DMSetFromOptions()`
8565 @*/
8566 PetscErrorCode DMPlexCheckGeometry(DM dm)
8567 {
8568   Vec            coordinates;
8569   PetscReal      detJ, J[9], refVol = 1.0;
8570   PetscReal      vol;
8571   PetscBool      periodic;
8572   PetscInt       dim, depth, dE, d, cStart, cEnd, c;
8573 
8574   PetscFunctionBegin;
8575   PetscCall(DMGetDimension(dm, &dim));
8576   PetscCall(DMGetCoordinateDim(dm, &dE));
8577   if (dim != dE) PetscFunctionReturn(0);
8578   PetscCall(DMPlexGetDepth(dm, &depth));
8579   PetscCall(DMGetPeriodicity(dm, &periodic, NULL, NULL, NULL));
8580   for (d = 0; d < dim; ++d) refVol *= 2.0;
8581   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8582   /* Make sure local coordinates are created, because that step is collective */
8583   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8584   for (c = cStart; c < cEnd; ++c) {
8585     DMPolytopeType ct;
8586     PetscInt       unsplit;
8587     PetscBool      ignoreZeroVol = PETSC_FALSE;
8588 
8589     PetscCall(DMPlexGetCellType(dm, c, &ct));
8590     switch (ct) {
8591       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8592       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8593       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8594         ignoreZeroVol = PETSC_TRUE; break;
8595       default: break;
8596     }
8597     switch (ct) {
8598       case DM_POLYTOPE_TRI_PRISM:
8599       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8600       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8601       case DM_POLYTOPE_PYRAMID:
8602         continue;
8603       default: break;
8604     }
8605     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8606     if (unsplit) continue;
8607     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8608     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);
8609     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ*refVol)));
8610     if (depth > 1 && !periodic) {
8611       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8612       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);
8613       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double) vol));
8614     }
8615   }
8616   PetscFunctionReturn(0);
8617 }
8618 
8619 /*@
8620   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8621 
8622   Collective
8623 
8624   Input Parameters:
8625 + dm - The DMPlex object
8626 - pointSF - The Point SF, or NULL for Point SF attached to DM
8627 
8628   Notes:
8629   This is mainly intended for debugging/testing purposes.
8630 
8631   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8632 
8633   Level: developer
8634 
8635 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8636 @*/
8637 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF)
8638 {
8639   PetscInt        l, nleaves, nroots, overlap;
8640   const PetscInt *locals;
8641   const PetscSFNode *remotes;
8642   PetscBool       distributed;
8643   MPI_Comm        comm;
8644   PetscMPIInt     rank;
8645 
8646   PetscFunctionBegin;
8647   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8648   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8649   else         pointSF = dm->sf;
8650   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8651   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8652   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8653   {
8654     PetscMPIInt    mpiFlag;
8655 
8656     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF),&mpiFlag));
8657     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)",mpiFlag);
8658   }
8659   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8660   PetscCall(DMPlexIsDistributed(dm, &distributed));
8661   if (!distributed) {
8662     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);
8663     PetscFunctionReturn(0);
8664   }
8665   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);
8666   PetscCall(DMPlexGetOverlap(dm, &overlap));
8667 
8668   /* Check SF graph is compatible with DMPlex chart */
8669   {
8670     PetscInt pStart, pEnd, maxLeaf;
8671 
8672     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8673     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8674     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd-pStart, nroots);
8675     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8676   }
8677 
8678   /* Check Point SF has no local points referenced */
8679   for (l = 0; l < nleaves; l++) {
8680     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);
8681   }
8682 
8683   /* Check there are no cells in interface */
8684   if (!overlap) {
8685     PetscInt cellHeight, cStart, cEnd;
8686 
8687     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8688     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8689     for (l = 0; l < nleaves; ++l) {
8690       const PetscInt point = locals ? locals[l] : l;
8691 
8692       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8693     }
8694   }
8695 
8696   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8697   {
8698     const PetscInt *rootdegree;
8699 
8700     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8701     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8702     for (l = 0; l < nleaves; ++l) {
8703       const PetscInt  point = locals ? locals[l] : l;
8704       const PetscInt *cone;
8705       PetscInt        coneSize, c, idx;
8706 
8707       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8708       PetscCall(DMPlexGetCone(dm, point, &cone));
8709       for (c = 0; c < coneSize; ++c) {
8710         if (!rootdegree[cone[c]]) {
8711           if (locals) {
8712             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8713           } else {
8714             idx = (cone[c] < nleaves) ? cone[c] : -1;
8715           }
8716           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8717         }
8718       }
8719     }
8720   }
8721   PetscFunctionReturn(0);
8722 }
8723 
8724 /*@
8725   DMPlexCheck - Perform various checks of Plex sanity
8726 
8727   Input Parameter:
8728 . dm - The DMPlex object
8729 
8730   Notes:
8731   This is a useful diagnostic when creating meshes programmatically.
8732 
8733   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8734 
8735   Currently does not include DMPlexCheckCellShape().
8736 
8737   Level: developer
8738 
8739 .seealso: DMCreate(), DMSetFromOptions()
8740 @*/
8741 PetscErrorCode DMPlexCheck(DM dm)
8742 {
8743   PetscInt cellHeight;
8744 
8745   PetscFunctionBegin;
8746   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8747   PetscCall(DMPlexCheckSymmetry(dm));
8748   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8749   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8750   PetscCall(DMPlexCheckGeometry(dm));
8751   PetscCall(DMPlexCheckPointSF(dm, NULL));
8752   PetscCall(DMPlexCheckInterfaceCones(dm));
8753   PetscFunctionReturn(0);
8754 }
8755 
8756 typedef struct cell_stats
8757 {
8758   PetscReal min, max, sum, squaresum;
8759   PetscInt  count;
8760 } cell_stats_t;
8761 
8762 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8763 {
8764   PetscInt i, N = *len;
8765 
8766   for (i = 0; i < N; i++) {
8767     cell_stats_t *A = (cell_stats_t *) a;
8768     cell_stats_t *B = (cell_stats_t *) b;
8769 
8770     B->min = PetscMin(A->min,B->min);
8771     B->max = PetscMax(A->max,B->max);
8772     B->sum += A->sum;
8773     B->squaresum += A->squaresum;
8774     B->count += A->count;
8775   }
8776 }
8777 
8778 /*@
8779   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8780 
8781   Collective on dm
8782 
8783   Input Parameters:
8784 + dm        - The DMPlex object
8785 . output    - If true, statistics will be displayed on stdout
8786 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8787 
8788   Notes:
8789   This is mainly intended for debugging/testing purposes.
8790 
8791   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8792 
8793   Level: developer
8794 
8795 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8796 @*/
8797 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8798 {
8799   DM             dmCoarse;
8800   cell_stats_t   stats, globalStats;
8801   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8802   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8803   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8804   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8805   PetscMPIInt    rank,size;
8806 
8807   PetscFunctionBegin;
8808   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8809   stats.min   = PETSC_MAX_REAL;
8810   stats.max   = PETSC_MIN_REAL;
8811   stats.sum   = stats.squaresum = 0.;
8812   stats.count = 0;
8813 
8814   PetscCallMPI(MPI_Comm_size(comm, &size));
8815   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8816   PetscCall(DMGetCoordinateDim(dm,&cdim));
8817   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8818   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8819   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8820   for (c = cStart; c < cEnd; c++) {
8821     PetscInt  i;
8822     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8823 
8824     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8825     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8826     for (i = 0; i < PetscSqr(cdim); ++i) {
8827       frobJ    += J[i] * J[i];
8828       frobInvJ += invJ[i] * invJ[i];
8829     }
8830     cond2 = frobJ * frobInvJ;
8831     cond  = PetscSqrtReal(cond2);
8832 
8833     stats.min        = PetscMin(stats.min,cond);
8834     stats.max        = PetscMax(stats.max,cond);
8835     stats.sum       += cond;
8836     stats.squaresum += cond2;
8837     stats.count++;
8838     if (output && cond > limit) {
8839       PetscSection coordSection;
8840       Vec          coordsLocal;
8841       PetscScalar *coords = NULL;
8842       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8843 
8844       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8845       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8846       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8847       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double) cond));
8848       for (i = 0; i < Nv/cdim; ++i) {
8849         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8850         for (d = 0; d < cdim; ++d) {
8851           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8852           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8853         }
8854         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8855       }
8856       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8857       for (cl = 0; cl < clSize*2; cl += 2) {
8858         const PetscInt edge = closure[cl];
8859 
8860         if ((edge >= eStart) && (edge < eEnd)) {
8861           PetscReal len;
8862 
8863           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8864           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double) len));
8865         }
8866       }
8867       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8868       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8869     }
8870   }
8871   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8872 
8873   if (size > 1) {
8874     PetscMPIInt   blockLengths[2] = {4,1};
8875     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8876     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8877     MPI_Op        statReduce;
8878 
8879     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8880     PetscCallMPI(MPI_Type_commit(&statType));
8881     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8882     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8883     PetscCallMPI(MPI_Op_free(&statReduce));
8884     PetscCallMPI(MPI_Type_free(&statType));
8885   } else {
8886     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8887   }
8888   if (rank == 0) {
8889     count = globalStats.count;
8890     min   = globalStats.min;
8891     max   = globalStats.max;
8892     mean  = globalStats.sum / globalStats.count;
8893     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8894   }
8895 
8896   if (output) {
8897     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));
8898   }
8899   PetscCall(PetscFree2(J,invJ));
8900 
8901   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8902   if (dmCoarse) {
8903     PetscBool isplex;
8904 
8905     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8906     if (isplex) {
8907       PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8908     }
8909   }
8910   PetscFunctionReturn(0);
8911 }
8912 
8913 /*@
8914   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8915   orthogonal quality below given tolerance.
8916 
8917   Collective on dm
8918 
8919   Input Parameters:
8920 + dm   - The DMPlex object
8921 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8922 - atol - [0, 1] Absolute tolerance for tagging cells.
8923 
8924   Output Parameters:
8925 + OrthQual      - Vec containing orthogonal quality per cell
8926 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8927 
8928   Options Database Keys:
8929 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8930 supported.
8931 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8932 
8933   Notes:
8934   Orthogonal quality is given by the following formula:
8935 
8936   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8937 
8938   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
8939   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8940   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8941   calculating the cosine of the angle between these vectors.
8942 
8943   Orthogonal quality ranges from 1 (best) to 0 (worst).
8944 
8945   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8946   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8947 
8948   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8949 
8950   Level: intermediate
8951 
8952 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8953 @*/
8954 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8955 {
8956   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8957   PetscInt                *idx;
8958   PetscScalar             *oqVals;
8959   const PetscScalar       *cellGeomArr, *faceGeomArr;
8960   PetscReal               *ci, *fi, *Ai;
8961   MPI_Comm                comm;
8962   Vec                     cellgeom, facegeom;
8963   DM                      dmFace, dmCell;
8964   IS                      glob;
8965   ISLocalToGlobalMapping  ltog;
8966   PetscViewer             vwr;
8967 
8968   PetscFunctionBegin;
8969   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8970   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
8971   PetscValidPointer(OrthQual, 4);
8972   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
8973   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
8974   PetscCall(DMGetDimension(dm, &nc));
8975   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
8976   {
8977     DMPlexInterpolatedFlag interpFlag;
8978 
8979     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
8980     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
8981       PetscMPIInt rank;
8982 
8983       PetscCallMPI(MPI_Comm_rank(comm, &rank));
8984       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
8985     }
8986   }
8987   if (OrthQualLabel) {
8988     PetscValidPointer(OrthQualLabel, 5);
8989     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
8990     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
8991   } else {*OrthQualLabel = NULL;}
8992   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8993   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8994   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
8995   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
8996   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
8997   PetscCall(VecCreate(comm, OrthQual));
8998   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
8999   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
9000   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9001   PetscCall(VecSetUp(*OrthQual));
9002   PetscCall(ISDestroy(&glob));
9003   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9004   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9005   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9006   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9007   PetscCall(VecGetDM(cellgeom, &dmCell));
9008   PetscCall(VecGetDM(facegeom, &dmFace));
9009   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9010   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
9011     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9012     PetscInt           cellarr[2], *adj = NULL;
9013     PetscScalar        *cArr, *fArr;
9014     PetscReal          minvalc = 1.0, minvalf = 1.0;
9015     PetscFVCellGeom    *cg;
9016 
9017     idx[cellIter] = cell-cStart;
9018     cellarr[0] = cell;
9019     /* Make indexing into cellGeom easier */
9020     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9021     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9022     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9023     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9024     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
9025       PetscInt         i;
9026       const PetscInt   neigh = adj[cellneigh];
9027       PetscReal        normci = 0, normfi = 0, normai = 0;
9028       PetscFVCellGeom  *cgneigh;
9029       PetscFVFaceGeom  *fg;
9030 
9031       /* Don't count ourselves in the neighbor list */
9032       if (neigh == cell) continue;
9033       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9034       cellarr[1] = neigh;
9035       {
9036         PetscInt       numcovpts;
9037         const PetscInt *covpts;
9038 
9039         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9040         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9041         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9042       }
9043 
9044       /* Compute c_i, f_i and their norms */
9045       for (i = 0; i < nc; i++) {
9046         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9047         fi[i] = fg->centroid[i] - cg->centroid[i];
9048         Ai[i] = fg->normal[i];
9049         normci += PetscPowReal(ci[i], 2);
9050         normfi += PetscPowReal(fi[i], 2);
9051         normai += PetscPowReal(Ai[i], 2);
9052       }
9053       normci = PetscSqrtReal(normci);
9054       normfi = PetscSqrtReal(normfi);
9055       normai = PetscSqrtReal(normai);
9056 
9057       /* Normalize and compute for each face-cell-normal pair */
9058       for (i = 0; i < nc; i++) {
9059         ci[i] = ci[i]/normci;
9060         fi[i] = fi[i]/normfi;
9061         Ai[i] = Ai[i]/normai;
9062         /* PetscAbs because I don't know if normals are guaranteed to point out */
9063         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9064         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9065       }
9066       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9067         minvalc = PetscRealPart(cArr[cellneighiter]);
9068       }
9069       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9070         minvalf = PetscRealPart(fArr[cellneighiter]);
9071       }
9072     }
9073     PetscCall(PetscFree(adj));
9074     PetscCall(PetscFree2(cArr, fArr));
9075     /* Defer to cell if they're equal */
9076     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9077     if (OrthQualLabel) {
9078       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9079     }
9080   }
9081   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9082   PetscCall(VecAssemblyBegin(*OrthQual));
9083   PetscCall(VecAssemblyEnd(*OrthQual));
9084   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9085   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9086   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9087   if (OrthQualLabel) {
9088     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9089   }
9090   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9091   PetscCall(PetscViewerDestroy(&vwr));
9092   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9093   PetscFunctionReturn(0);
9094 }
9095 
9096 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9097  * interpolator construction */
9098 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9099 {
9100   PetscSection   section, newSection, gsection;
9101   PetscSF        sf;
9102   PetscBool      hasConstraints, ghasConstraints;
9103 
9104   PetscFunctionBegin;
9105   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9106   PetscValidPointer(odm,2);
9107   PetscCall(DMGetLocalSection(dm, &section));
9108   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9109   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9110   if (!ghasConstraints) {
9111     PetscCall(PetscObjectReference((PetscObject)dm));
9112     *odm = dm;
9113     PetscFunctionReturn(0);
9114   }
9115   PetscCall(DMClone(dm, odm));
9116   PetscCall(DMCopyFields(dm, *odm));
9117   PetscCall(DMGetLocalSection(*odm, &newSection));
9118   PetscCall(DMGetPointSF(*odm, &sf));
9119   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9120   PetscCall(DMSetGlobalSection(*odm, gsection));
9121   PetscCall(PetscSectionDestroy(&gsection));
9122   PetscFunctionReturn(0);
9123 }
9124 
9125 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9126 {
9127   DM             dmco, dmfo;
9128   Mat            interpo;
9129   Vec            rscale;
9130   Vec            cglobalo, clocal;
9131   Vec            fglobal, fglobalo, flocal;
9132   PetscBool      regular;
9133 
9134   PetscFunctionBegin;
9135   PetscCall(DMGetFullDM(dmc, &dmco));
9136   PetscCall(DMGetFullDM(dmf, &dmfo));
9137   PetscCall(DMSetCoarseDM(dmfo, dmco));
9138   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9139   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9140   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9141   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9142   PetscCall(DMCreateLocalVector(dmc, &clocal));
9143   PetscCall(VecSet(cglobalo, 0.));
9144   PetscCall(VecSet(clocal, 0.));
9145   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9146   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9147   PetscCall(DMCreateLocalVector(dmf, &flocal));
9148   PetscCall(VecSet(fglobal, 0.));
9149   PetscCall(VecSet(fglobalo, 0.));
9150   PetscCall(VecSet(flocal, 0.));
9151   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9152   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9153   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9154   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9155   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9156   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9157   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9158   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9159   *shift = fglobal;
9160   PetscCall(VecDestroy(&flocal));
9161   PetscCall(VecDestroy(&fglobalo));
9162   PetscCall(VecDestroy(&clocal));
9163   PetscCall(VecDestroy(&cglobalo));
9164   PetscCall(VecDestroy(&rscale));
9165   PetscCall(MatDestroy(&interpo));
9166   PetscCall(DMDestroy(&dmfo));
9167   PetscCall(DMDestroy(&dmco));
9168   PetscFunctionReturn(0);
9169 }
9170 
9171 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9172 {
9173   PetscObject    shifto;
9174   Vec            shift;
9175 
9176   PetscFunctionBegin;
9177   if (!interp) {
9178     Vec rscale;
9179 
9180     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9181     PetscCall(VecDestroy(&rscale));
9182   } else {
9183     PetscCall(PetscObjectReference((PetscObject)interp));
9184   }
9185   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9186   if (!shifto) {
9187     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9188     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9189     shifto = (PetscObject) shift;
9190     PetscCall(VecDestroy(&shift));
9191   }
9192   shift = (Vec) shifto;
9193   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9194   PetscCall(VecAXPY(fineSol, 1.0, shift));
9195   PetscCall(MatDestroy(&interp));
9196   PetscFunctionReturn(0);
9197 }
9198 
9199 /* Pointwise interpolation
9200      Just code FEM for now
9201      u^f = I u^c
9202      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9203      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9204      I_{ij} = psi^f_i phi^c_j
9205 */
9206 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9207 {
9208   PetscSection   gsc, gsf;
9209   PetscInt       m, n;
9210   void          *ctx;
9211   DM             cdm;
9212   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9213 
9214   PetscFunctionBegin;
9215   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9216   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9217   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9218   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9219 
9220   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9221   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9222   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9223   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9224   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9225 
9226   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9227   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9228   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9229   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9230   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9231   if (scaling) {
9232     /* Use naive scaling */
9233     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9234   }
9235   PetscFunctionReturn(0);
9236 }
9237 
9238 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9239 {
9240   VecScatter     ctx;
9241 
9242   PetscFunctionBegin;
9243   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9244   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9245   PetscCall(VecScatterDestroy(&ctx));
9246   PetscFunctionReturn(0);
9247 }
9248 
9249 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9250                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9251                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9252                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9253 {
9254   const PetscInt Nc = uOff[1] - uOff[0];
9255   PetscInt       c;
9256   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9257 }
9258 
9259 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9260 {
9261   DM             dmc;
9262   PetscDS        ds;
9263   Vec            ones, locmass;
9264   IS             cellIS;
9265   PetscFormKey   key;
9266   PetscInt       depth;
9267 
9268   PetscFunctionBegin;
9269   PetscCall(DMClone(dm, &dmc));
9270   PetscCall(DMCopyDisc(dm, dmc));
9271   PetscCall(DMGetDS(dmc, &ds));
9272   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9273   PetscCall(DMCreateGlobalVector(dmc, mass));
9274   PetscCall(DMGetLocalVector(dmc, &ones));
9275   PetscCall(DMGetLocalVector(dmc, &locmass));
9276   PetscCall(DMPlexGetDepth(dmc, &depth));
9277   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9278   PetscCall(VecSet(locmass, 0.0));
9279   PetscCall(VecSet(ones, 1.0));
9280   key.label = NULL;
9281   key.value = 0;
9282   key.field = 0;
9283   key.part  = 0;
9284   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9285   PetscCall(ISDestroy(&cellIS));
9286   PetscCall(VecSet(*mass, 0.0));
9287   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9288   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9289   PetscCall(DMRestoreLocalVector(dmc, &ones));
9290   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9291   PetscCall(DMDestroy(&dmc));
9292   PetscFunctionReturn(0);
9293 }
9294 
9295 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9296 {
9297   PetscSection   gsc, gsf;
9298   PetscInt       m, n;
9299   void          *ctx;
9300   DM             cdm;
9301   PetscBool      regular;
9302 
9303   PetscFunctionBegin;
9304   if (dmFine == dmCoarse) {
9305     DM            dmc;
9306     PetscDS       ds;
9307     PetscWeakForm wf;
9308     Vec           u;
9309     IS            cellIS;
9310     PetscFormKey  key;
9311     PetscInt      depth;
9312 
9313     PetscCall(DMClone(dmFine, &dmc));
9314     PetscCall(DMCopyDisc(dmFine, dmc));
9315     PetscCall(DMGetDS(dmc, &ds));
9316     PetscCall(PetscDSGetWeakForm(ds, &wf));
9317     PetscCall(PetscWeakFormClear(wf));
9318     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9319     PetscCall(DMCreateMatrix(dmc, mass));
9320     PetscCall(DMGetGlobalVector(dmc, &u));
9321     PetscCall(DMPlexGetDepth(dmc, &depth));
9322     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9323     PetscCall(MatZeroEntries(*mass));
9324     key.label = NULL;
9325     key.value = 0;
9326     key.field = 0;
9327     key.part  = 0;
9328     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9329     PetscCall(ISDestroy(&cellIS));
9330     PetscCall(DMRestoreGlobalVector(dmc, &u));
9331     PetscCall(DMDestroy(&dmc));
9332   } else {
9333     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9334     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9335     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9336     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9337 
9338     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9339     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9340     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9341     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9342 
9343     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9344     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9345     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9346     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9347   }
9348   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9349   PetscFunctionReturn(0);
9350 }
9351 
9352 /*@
9353   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9354 
9355   Input Parameter:
9356 . dm - The DMPlex object
9357 
9358   Output Parameter:
9359 . regular - The flag
9360 
9361   Level: intermediate
9362 
9363 .seealso: `DMPlexSetRegularRefinement()`
9364 @*/
9365 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9366 {
9367   PetscFunctionBegin;
9368   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9369   PetscValidBoolPointer(regular, 2);
9370   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9371   PetscFunctionReturn(0);
9372 }
9373 
9374 /*@
9375   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9376 
9377   Input Parameters:
9378 + dm - The DMPlex object
9379 - regular - The flag
9380 
9381   Level: intermediate
9382 
9383 .seealso: `DMPlexGetRegularRefinement()`
9384 @*/
9385 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9386 {
9387   PetscFunctionBegin;
9388   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9389   ((DM_Plex *) dm->data)->regularRefinement = regular;
9390   PetscFunctionReturn(0);
9391 }
9392 
9393 /* anchors */
9394 /*@
9395   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9396   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9397 
9398   not collective
9399 
9400   Input Parameter:
9401 . dm - The DMPlex object
9402 
9403   Output Parameters:
9404 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9405 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9406 
9407   Level: intermediate
9408 
9409 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9410 @*/
9411 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9412 {
9413   DM_Plex *plex = (DM_Plex *)dm->data;
9414 
9415   PetscFunctionBegin;
9416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9417   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9418   if (anchorSection) *anchorSection = plex->anchorSection;
9419   if (anchorIS) *anchorIS = plex->anchorIS;
9420   PetscFunctionReturn(0);
9421 }
9422 
9423 /*@
9424   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9425   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9426   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9427 
9428   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9429   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9430 
9431   collective on dm
9432 
9433   Input Parameters:
9434 + dm - The DMPlex object
9435 . 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).
9436 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9437 
9438   The reference counts of anchorSection and anchorIS are incremented.
9439 
9440   Level: intermediate
9441 
9442 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9443 @*/
9444 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9445 {
9446   DM_Plex        *plex = (DM_Plex *)dm->data;
9447   PetscMPIInt    result;
9448 
9449   PetscFunctionBegin;
9450   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9451   if (anchorSection) {
9452     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9453     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9454     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9455   }
9456   if (anchorIS) {
9457     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9458     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9459     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9460   }
9461 
9462   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9463   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9464   plex->anchorSection = anchorSection;
9465 
9466   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9467   PetscCall(ISDestroy(&plex->anchorIS));
9468   plex->anchorIS = anchorIS;
9469 
9470   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9471     PetscInt size, a, pStart, pEnd;
9472     const PetscInt *anchors;
9473 
9474     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9475     PetscCall(ISGetLocalSize(anchorIS,&size));
9476     PetscCall(ISGetIndices(anchorIS,&anchors));
9477     for (a = 0; a < size; a++) {
9478       PetscInt p;
9479 
9480       p = anchors[a];
9481       if (p >= pStart && p < pEnd) {
9482         PetscInt dof;
9483 
9484         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9485         if (dof) {
9486 
9487           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9488           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %" PetscInt_FMT " cannot be constrained and an anchor",p);
9489         }
9490       }
9491     }
9492     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9493   }
9494   /* reset the generic constraints */
9495   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9496   PetscFunctionReturn(0);
9497 }
9498 
9499 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9500 {
9501   PetscSection anchorSection;
9502   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9503 
9504   PetscFunctionBegin;
9505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9506   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9507   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9508   PetscCall(PetscSectionGetNumFields(section,&numFields));
9509   if (numFields) {
9510     PetscInt f;
9511     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9512 
9513     for (f = 0; f < numFields; f++) {
9514       PetscInt numComp;
9515 
9516       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9517       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9518     }
9519   }
9520   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9521   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9522   pStart = PetscMax(pStart,sStart);
9523   pEnd   = PetscMin(pEnd,sEnd);
9524   pEnd   = PetscMax(pStart,pEnd);
9525   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9526   for (p = pStart; p < pEnd; p++) {
9527     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9528     if (dof) {
9529       PetscCall(PetscSectionGetDof(section,p,&dof));
9530       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9531       for (f = 0; f < numFields; f++) {
9532         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9533         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9534       }
9535     }
9536   }
9537   PetscCall(PetscSectionSetUp(*cSec));
9538   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9539   PetscFunctionReturn(0);
9540 }
9541 
9542 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9543 {
9544   PetscSection   aSec;
9545   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9546   const PetscInt *anchors;
9547   PetscInt       numFields, f;
9548   IS             aIS;
9549   MatType        mtype;
9550   PetscBool      iscuda,iskokkos;
9551 
9552   PetscFunctionBegin;
9553   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9554   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9555   PetscCall(PetscSectionGetStorageSize(section, &n));
9556   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9557   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9558   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9559   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9560   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9561   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9562   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9563   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9564   else mtype = MATSEQAIJ;
9565   PetscCall(MatSetType(*cMat,mtype));
9566   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9567   PetscCall(ISGetIndices(aIS,&anchors));
9568   /* cSec will be a subset of aSec and section */
9569   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9570   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9571   PetscCall(PetscMalloc1(m+1,&i));
9572   i[0] = 0;
9573   PetscCall(PetscSectionGetNumFields(section,&numFields));
9574   for (p = pStart; p < pEnd; p++) {
9575     PetscInt rDof, rOff, r;
9576 
9577     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9578     if (!rDof) continue;
9579     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9580     if (numFields) {
9581       for (f = 0; f < numFields; f++) {
9582         annz = 0;
9583         for (r = 0; r < rDof; r++) {
9584           a = anchors[rOff + r];
9585           if (a < sStart || a >= sEnd) continue;
9586           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9587           annz += aDof;
9588         }
9589         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9590         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9591         for (q = 0; q < dof; q++) {
9592           i[off + q + 1] = i[off + q] + annz;
9593         }
9594       }
9595     } else {
9596       annz = 0;
9597       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9598       for (q = 0; q < dof; q++) {
9599         a = anchors[rOff + q];
9600         if (a < sStart || a >= sEnd) continue;
9601         PetscCall(PetscSectionGetDof(section,a,&aDof));
9602         annz += aDof;
9603       }
9604       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9605       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9606       for (q = 0; q < dof; q++) {
9607         i[off + q + 1] = i[off + q] + annz;
9608       }
9609     }
9610   }
9611   nnz = i[m];
9612   PetscCall(PetscMalloc1(nnz,&j));
9613   offset = 0;
9614   for (p = pStart; p < pEnd; p++) {
9615     if (numFields) {
9616       for (f = 0; f < numFields; f++) {
9617         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9618         for (q = 0; q < dof; q++) {
9619           PetscInt rDof, rOff, r;
9620           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9621           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9622           for (r = 0; r < rDof; r++) {
9623             PetscInt s;
9624 
9625             a = anchors[rOff + r];
9626             if (a < sStart || a >= sEnd) continue;
9627             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9628             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9629             for (s = 0; s < aDof; s++) {
9630               j[offset++] = aOff + s;
9631             }
9632           }
9633         }
9634       }
9635     } else {
9636       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9637       for (q = 0; q < dof; q++) {
9638         PetscInt rDof, rOff, r;
9639         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9640         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9641         for (r = 0; r < rDof; r++) {
9642           PetscInt s;
9643 
9644           a = anchors[rOff + r];
9645           if (a < sStart || a >= sEnd) continue;
9646           PetscCall(PetscSectionGetDof(section,a,&aDof));
9647           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9648           for (s = 0; s < aDof; s++) {
9649             j[offset++] = aOff + s;
9650           }
9651         }
9652       }
9653     }
9654   }
9655   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9656   PetscCall(PetscFree(i));
9657   PetscCall(PetscFree(j));
9658   PetscCall(ISRestoreIndices(aIS,&anchors));
9659   PetscFunctionReturn(0);
9660 }
9661 
9662 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9663 {
9664   DM_Plex        *plex = (DM_Plex *)dm->data;
9665   PetscSection   anchorSection, section, cSec;
9666   Mat            cMat;
9667 
9668   PetscFunctionBegin;
9669   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9670   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9671   if (anchorSection) {
9672     PetscInt Nf;
9673 
9674     PetscCall(DMGetLocalSection(dm,&section));
9675     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9676     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9677     PetscCall(DMGetNumFields(dm,&Nf));
9678     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9679     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9680     PetscCall(PetscSectionDestroy(&cSec));
9681     PetscCall(MatDestroy(&cMat));
9682   }
9683   PetscFunctionReturn(0);
9684 }
9685 
9686 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9687 {
9688   IS             subis;
9689   PetscSection   section, subsection;
9690 
9691   PetscFunctionBegin;
9692   PetscCall(DMGetLocalSection(dm, &section));
9693   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9694   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9695   /* Create subdomain */
9696   PetscCall(DMPlexFilter(dm, label, value, subdm));
9697   /* Create submodel */
9698   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9699   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9700   PetscCall(DMSetLocalSection(*subdm, subsection));
9701   PetscCall(PetscSectionDestroy(&subsection));
9702   PetscCall(DMCopyDisc(dm, *subdm));
9703   /* Create map from submodel to global model */
9704   if (is) {
9705     PetscSection    sectionGlobal, subsectionGlobal;
9706     IS              spIS;
9707     const PetscInt *spmap;
9708     PetscInt       *subIndices;
9709     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9710     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9711 
9712     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9713     PetscCall(ISGetIndices(spIS, &spmap));
9714     PetscCall(PetscSectionGetNumFields(section, &Nf));
9715     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9716     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9717     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9718     for (p = pStart; p < pEnd; ++p) {
9719       PetscInt gdof, pSubSize  = 0;
9720 
9721       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9722       if (gdof > 0) {
9723         for (f = 0; f < Nf; ++f) {
9724           PetscInt fdof, fcdof;
9725 
9726           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9727           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9728           pSubSize += fdof-fcdof;
9729         }
9730         subSize += pSubSize;
9731         if (pSubSize) {
9732           if (bs < 0) {
9733             bs = pSubSize;
9734           } else if (bs != pSubSize) {
9735             /* Layout does not admit a pointwise block size */
9736             bs = 1;
9737           }
9738         }
9739       }
9740     }
9741     /* Must have same blocksize on all procs (some might have no points) */
9742     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9743     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9744     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9745     else                            {bs = bsMinMax[0];}
9746     PetscCall(PetscMalloc1(subSize, &subIndices));
9747     for (p = pStart; p < pEnd; ++p) {
9748       PetscInt gdof, goff;
9749 
9750       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9751       if (gdof > 0) {
9752         const PetscInt point = spmap[p];
9753 
9754         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9755         for (f = 0; f < Nf; ++f) {
9756           PetscInt fdof, fcdof, fc, f2, poff = 0;
9757 
9758           /* Can get rid of this loop by storing field information in the global section */
9759           for (f2 = 0; f2 < f; ++f2) {
9760             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9761             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9762             poff += fdof-fcdof;
9763           }
9764           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9765           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9766           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9767             subIndices[subOff] = goff+poff+fc;
9768           }
9769         }
9770       }
9771     }
9772     PetscCall(ISRestoreIndices(spIS, &spmap));
9773     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9774     if (bs > 1) {
9775       /* We need to check that the block size does not come from non-contiguous fields */
9776       PetscInt i, j, set = 1;
9777       for (i = 0; i < subSize; i += bs) {
9778         for (j = 0; j < bs; ++j) {
9779           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9780         }
9781       }
9782       if (set) PetscCall(ISSetBlockSize(*is, bs));
9783     }
9784     /* Attach nullspace */
9785     for (f = 0; f < Nf; ++f) {
9786       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9787       if ((*subdm)->nullspaceConstructors[f]) break;
9788     }
9789     if (f < Nf) {
9790       MatNullSpace nullSpace;
9791       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9792 
9793       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9794       PetscCall(MatNullSpaceDestroy(&nullSpace));
9795     }
9796   }
9797   PetscFunctionReturn(0);
9798 }
9799 
9800 /*@
9801   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9802 
9803   Input Parameter:
9804 - dm - The DM
9805 
9806   Level: developer
9807 
9808   Options Database Keys:
9809 . -dm_plex_monitor_throughput - Activate the monitor
9810 
9811 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9812 @*/
9813 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9814 {
9815 #if defined(PETSC_USE_LOG)
9816   PetscStageLog      stageLog;
9817   PetscLogEvent      event;
9818   PetscLogStage      stage;
9819   PetscEventPerfInfo eventInfo;
9820   PetscReal          cellRate, flopRate;
9821   PetscInt           cStart, cEnd, Nf, N;
9822   const char        *name;
9823 #endif
9824 
9825   PetscFunctionBegin;
9826   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9827 #if defined(PETSC_USE_LOG)
9828   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9829   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9830   PetscCall(DMGetNumFields(dm, &Nf));
9831   PetscCall(PetscLogGetStageLog(&stageLog));
9832   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9833   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9834   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9835   N        = (cEnd - cStart)*Nf*eventInfo.count;
9836   flopRate = eventInfo.flops/eventInfo.time;
9837   cellRate = N/eventInfo.time;
9838   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)));
9839 #else
9840   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9841 #endif
9842   PetscFunctionReturn(0);
9843 }
9844