xref: /petsc/src/dm/impls/plex/plex.c (revision a69119a591a03a9d906b29c0a4e9802e4d7c9795)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm      - The DMPlex object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
28   If the mesh has no cells, this returns PETSC_FALSE.
29 
30   Level: intermediate
31 
32 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
33 @*/
34 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex) {
35   DMPolytopeType ct;
36   PetscInt       cStart, cEnd;
37 
38   PetscFunctionBegin;
39   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
40   if (cEnd <= cStart) {
41     *simplex = PETSC_FALSE;
42     PetscFunctionReturn(0);
43   }
44   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
45   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
46   PetscFunctionReturn(0);
47 }
48 
49 /*@
50   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
51 
52   Input Parameters:
53 + dm     - The DMPlex object
54 - height - The cell height in the Plex, 0 is the default
55 
56   Output Parameters:
57 + cStart - The first "normal" cell
58 - cEnd   - The upper bound on "normal"" cells
59 
60   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
61 
62   Level: developer
63 
64 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
65 @*/
66 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd) {
67   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
68   PetscInt       cS, cE, c;
69 
70   PetscFunctionBegin;
71   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
72   for (c = cS; c < cE; ++c) {
73     DMPolytopeType cct;
74 
75     PetscCall(DMPlexGetCellType(dm, c, &cct));
76     if ((PetscInt)cct < 0) break;
77     switch (cct) {
78     case DM_POLYTOPE_POINT:
79     case DM_POLYTOPE_SEGMENT:
80     case DM_POLYTOPE_TRIANGLE:
81     case DM_POLYTOPE_QUADRILATERAL:
82     case DM_POLYTOPE_TETRAHEDRON:
83     case DM_POLYTOPE_HEXAHEDRON: ct = cct; break;
84     default: break;
85     }
86     if (ct != DM_POLYTOPE_UNKNOWN) break;
87   }
88   if (ct != DM_POLYTOPE_UNKNOWN) {
89     DMLabel ctLabel;
90 
91     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
92     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
93     // Reset label for fast lookup
94     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
95   }
96   if (cStart) *cStart = cS;
97   if (cEnd) *cEnd = cE;
98   PetscFunctionReturn(0);
99 }
100 
101 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft) {
102   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
103   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
104 
105   PetscFunctionBegin;
106   *ft = PETSC_VTK_INVALID;
107   PetscCall(DMGetCoordinateDim(dm, &cdim));
108   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
109   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
110   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
111   if (field >= 0) {
112     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
113     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
114   } else {
115     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
116     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
117   }
118   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
119   if (globalvcdof[0]) {
120     *sStart = vStart;
121     *sEnd   = vEnd;
122     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
123     else *ft = PETSC_VTK_POINT_FIELD;
124   } else if (globalvcdof[1]) {
125     *sStart = cStart;
126     *sEnd   = cEnd;
127     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
128     else *ft = PETSC_VTK_CELL_FIELD;
129   } else {
130     if (field >= 0) {
131       const char *fieldname;
132 
133       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
134       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
135     } else {
136       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
137     }
138   }
139   PetscFunctionReturn(0);
140 }
141 
142 /*@
143   DMPlexVecView1D - Plot many 1D solutions on the same line graph
144 
145   Collective on dm
146 
147   Input Parameters:
148 + dm - The DMPlex
149 . n  - The number of vectors
150 . u  - The array of local vectors
151 - viewer - The Draw viewer
152 
153   Level: advanced
154 
155 .seealso: `VecViewFromOptions()`, `VecView()`
156 @*/
157 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer) {
158   PetscDS            ds;
159   PetscDraw          draw = NULL;
160   PetscDrawLG        lg;
161   Vec                coordinates;
162   const PetscScalar *coords, **sol;
163   PetscReal         *vals;
164   PetscInt          *Nc;
165   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
166   char             **names;
167 
168   PetscFunctionBegin;
169   PetscCall(DMGetDS(dm, &ds));
170   PetscCall(PetscDSGetNumFields(ds, &Nf));
171   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
172   PetscCall(PetscDSGetComponents(ds, &Nc));
173 
174   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
175   if (!draw) PetscFunctionReturn(0);
176   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
177 
178   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
179   for (i = 0, l = 0; i < n; ++i) {
180     const char *vname;
181 
182     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
183     for (f = 0; f < Nf; ++f) {
184       PetscObject disc;
185       const char *fname;
186       char        tmpname[PETSC_MAX_PATH_LEN];
187 
188       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
189       /* TODO Create names for components */
190       for (c = 0; c < Nc[f]; ++c, ++l) {
191         PetscCall(PetscObjectGetName(disc, &fname));
192         PetscCall(PetscStrcpy(tmpname, vname));
193         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
194         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
195         PetscCall(PetscStrallocpy(tmpname, &names[l]));
196       }
197     }
198   }
199   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
200   /* Just add P_1 support for now */
201   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
202   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
203   PetscCall(VecGetArrayRead(coordinates, &coords));
204   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
205   for (v = vStart; v < vEnd; ++v) {
206     PetscScalar *x, *svals;
207 
208     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
209     for (i = 0; i < n; ++i) {
210       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
211       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
212     }
213     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
214   }
215   PetscCall(VecRestoreArrayRead(coordinates, &coords));
216   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
217   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
218   PetscCall(PetscFree3(sol, names, vals));
219 
220   PetscCall(PetscDrawLGDraw(lg));
221   PetscCall(PetscDrawLGDestroy(&lg));
222   PetscFunctionReturn(0);
223 }
224 
225 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer) {
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   DM                 dm;
236   PetscSection       s;
237   PetscDraw          draw, popup;
238   DM                 cdm;
239   PetscSection       coordSection;
240   Vec                coordinates;
241   const PetscScalar *coords, *array;
242   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
243   PetscReal          vbound[2], time;
244   PetscBool          flg;
245   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
246   const char        *name;
247   char               title[PETSC_MAX_PATH_LEN];
248 
249   PetscFunctionBegin;
250   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
251   PetscCall(VecGetDM(v, &dm));
252   PetscCall(DMGetCoordinateDim(dm, &dim));
253   PetscCall(DMGetLocalSection(dm, &s));
254   PetscCall(PetscSectionGetNumFields(s, &Nf));
255   PetscCall(DMGetCoarsenLevel(dm, &level));
256   PetscCall(DMGetCoordinateDM(dm, &cdm));
257   PetscCall(DMGetLocalSection(cdm, &coordSection));
258   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
259   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
260   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
261 
262   PetscCall(PetscObjectGetName((PetscObject)v, &name));
263   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
264 
265   PetscCall(VecGetLocalSize(coordinates, &N));
266   PetscCall(VecGetArrayRead(coordinates, &coords));
267   for (c = 0; c < N; c += dim) {
268     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
269     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
270     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
271     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
272   }
273   PetscCall(VecRestoreArrayRead(coordinates, &coords));
274   PetscCall(PetscDrawClear(draw));
275 
276   /* Could implement something like DMDASelectFields() */
277   for (f = 0; f < Nf; ++f) {
278     DM          fdm = dm;
279     Vec         fv  = v;
280     IS          fis;
281     char        prefix[PETSC_MAX_PATH_LEN];
282     const char *fname;
283 
284     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
285     PetscCall(PetscSectionGetFieldName(s, f, &fname));
286 
287     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
288     else prefix[0] = '\0';
289     if (Nf > 1) {
290       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
291       PetscCall(VecGetSubVector(v, fis, &fv));
292       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
293       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
294     }
295     for (comp = 0; comp < Nc; ++comp, ++w) {
296       PetscInt nmax = 2;
297 
298       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
299       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
300       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
301       PetscCall(PetscDrawSetTitle(draw, title));
302 
303       /* TODO Get max and min only for this component */
304       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
305       if (!flg) {
306         PetscCall(VecMin(fv, NULL, &vbound[0]));
307         PetscCall(VecMax(fv, NULL, &vbound[1]));
308         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
309       }
310       PetscCall(PetscDrawGetPopup(draw, &popup));
311       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
312       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
313 
314       PetscCall(VecGetArrayRead(fv, &array));
315       for (c = cStart; c < cEnd; ++c) {
316         PetscScalar *coords = NULL, *a   = NULL;
317         PetscInt     numCoords, color[4] = {-1, -1, -1, -1};
318 
319         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
320         if (a) {
321           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
322           color[1] = color[2] = color[3] = color[0];
323         } else {
324           PetscScalar *vals = NULL;
325           PetscInt     numVals, va;
326 
327           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
328           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);
329           switch (numVals / Nc) {
330           case 3: /* P1 Triangle */
331           case 4: /* P1 Quadrangle */
332             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
333             break;
334           case 6: /* P2 Triangle */
335           case 8: /* P2 Quadrangle */
336             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
337             break;
338           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
339           }
340           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
341         }
342         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
343         switch (numCoords) {
344         case 6:
345         case 12: /* Localized triangle */
346           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]));
347           break;
348         case 8:
349         case 16: /* Localized quadrilateral */
350           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]));
351           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]));
352           break;
353         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
354         }
355         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
356       }
357       PetscCall(VecRestoreArrayRead(fv, &array));
358       PetscCall(PetscDrawFlush(draw));
359       PetscCall(PetscDrawPause(draw));
360       PetscCall(PetscDrawSave(draw));
361     }
362     if (Nf > 1) {
363       PetscCall(VecRestoreSubVector(v, fis, &fv));
364       PetscCall(ISDestroy(&fis));
365       PetscCall(DMDestroy(&fdm));
366     }
367   }
368   PetscFunctionReturn(0);
369 }
370 
371 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer) {
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   DM                      dm;
394   Vec                     locv;
395   const char             *name;
396   PetscSection            section;
397   PetscInt                pStart, pEnd;
398   PetscInt                numFields;
399   PetscViewerVTKFieldType ft;
400 
401   PetscFunctionBegin;
402   PetscCall(VecGetDM(v, &dm));
403   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
404   PetscCall(PetscObjectGetName((PetscObject)v, &name));
405   PetscCall(PetscObjectSetName((PetscObject)locv, name));
406   PetscCall(VecCopy(v, locv));
407   PetscCall(DMGetLocalSection(dm, &section));
408   PetscCall(PetscSectionGetNumFields(section, &numFields));
409   if (!numFields) {
410     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
411     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
412   } else {
413     PetscInt f;
414 
415     for (f = 0; f < numFields; f++) {
416       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
417       if (ft == PETSC_VTK_INVALID) continue;
418       PetscCall(PetscObjectReference((PetscObject)locv));
419       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
420     }
421     PetscCall(VecDestroy(&locv));
422   }
423   PetscFunctionReturn(0);
424 }
425 
426 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer) {
427   DM        dm;
428   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
429 
430   PetscFunctionBegin;
431   PetscCall(VecGetDM(v, &dm));
432   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
433   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
434   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
435   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
436   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
437   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
438   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
439     PetscInt    i, numFields;
440     PetscObject fe;
441     PetscBool   fem  = PETSC_FALSE;
442     Vec         locv = v;
443     const char *name;
444     PetscInt    step;
445     PetscReal   time;
446 
447     PetscCall(DMGetNumFields(dm, &numFields));
448     for (i = 0; i < numFields; i++) {
449       PetscCall(DMGetField(dm, i, NULL, &fe));
450       if (fe->classid == PETSCFE_CLASSID) {
451         fem = PETSC_TRUE;
452         break;
453       }
454     }
455     if (fem) {
456       PetscObject isZero;
457 
458       PetscCall(DMGetLocalVector(dm, &locv));
459       PetscCall(PetscObjectGetName((PetscObject)v, &name));
460       PetscCall(PetscObjectSetName((PetscObject)locv, name));
461       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
462       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
463       PetscCall(VecCopy(v, locv));
464       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
465       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
466     }
467     if (isvtk) {
468       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
469     } else if (ishdf5) {
470 #if defined(PETSC_HAVE_HDF5)
471       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
472 #else
473       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
474 #endif
475     } else if (isdraw) {
476       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
477     } else if (isglvis) {
478       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
479       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
480       PetscCall(VecView_GLVis(locv, viewer));
481     } else if (iscgns) {
482 #if defined(PETSC_HAVE_CGNS)
483       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
484 #else
485       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
486 #endif
487     }
488     if (fem) {
489       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
490       PetscCall(DMRestoreLocalVector(dm, &locv));
491     }
492   } else {
493     PetscBool isseq;
494 
495     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
496     if (isseq) PetscCall(VecView_Seq(v, viewer));
497     else PetscCall(VecView_MPI(v, viewer));
498   }
499   PetscFunctionReturn(0);
500 }
501 
502 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer) {
503   DM        dm;
504   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
505 
506   PetscFunctionBegin;
507   PetscCall(VecGetDM(v, &dm));
508   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
509   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
510   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
511   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
512   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
513   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
514   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
515   if (isvtk || isdraw || isglvis || iscgns) {
516     Vec         locv;
517     PetscObject isZero;
518     const char *name;
519 
520     PetscCall(DMGetLocalVector(dm, &locv));
521     PetscCall(PetscObjectGetName((PetscObject)v, &name));
522     PetscCall(PetscObjectSetName((PetscObject)locv, name));
523     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
524     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
525     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
526     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
527     PetscCall(VecView_Plex_Local(locv, viewer));
528     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
529     PetscCall(DMRestoreLocalVector(dm, &locv));
530   } else if (ishdf5) {
531 #if defined(PETSC_HAVE_HDF5)
532     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
533 #else
534     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
535 #endif
536   } else if (isexodusii) {
537 #if defined(PETSC_HAVE_EXODUSII)
538     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
539 #else
540     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
541 #endif
542   } else {
543     PetscBool isseq;
544 
545     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
546     if (isseq) PetscCall(VecView_Seq(v, viewer));
547     else PetscCall(VecView_MPI(v, viewer));
548   }
549   PetscFunctionReturn(0);
550 }
551 
552 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer) {
553   DM                dm;
554   MPI_Comm          comm;
555   PetscViewerFormat format;
556   Vec               v;
557   PetscBool         isvtk, ishdf5;
558 
559   PetscFunctionBegin;
560   PetscCall(VecGetDM(originalv, &dm));
561   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
562   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
563   PetscCall(PetscViewerGetFormat(viewer, &format));
564   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
565   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
566   if (format == PETSC_VIEWER_NATIVE) {
567     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
568     /* this need a better fix */
569     if (dm->useNatural) {
570       if (dm->sfNatural) {
571         const char *vecname;
572         PetscInt    n, nroots;
573 
574         PetscCall(VecGetLocalSize(originalv, &n));
575         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
576         if (n == nroots) {
577           PetscCall(DMGetGlobalVector(dm, &v));
578           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
579           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
580           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
581           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
582         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
583       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
584     } else v = originalv;
585   } else v = originalv;
586 
587   if (ishdf5) {
588 #if defined(PETSC_HAVE_HDF5)
589     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
590 #else
591     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
592 #endif
593   } else if (isvtk) {
594     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
595   } else {
596     PetscBool isseq;
597 
598     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
599     if (isseq) PetscCall(VecView_Seq(v, viewer));
600     else PetscCall(VecView_MPI(v, viewer));
601   }
602   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
603   PetscFunctionReturn(0);
604 }
605 
606 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer) {
607   DM        dm;
608   PetscBool ishdf5;
609 
610   PetscFunctionBegin;
611   PetscCall(VecGetDM(v, &dm));
612   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
613   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
614   if (ishdf5) {
615     DM          dmBC;
616     Vec         gv;
617     const char *name;
618 
619     PetscCall(DMGetOutputDM(dm, &dmBC));
620     PetscCall(DMGetGlobalVector(dmBC, &gv));
621     PetscCall(PetscObjectGetName((PetscObject)v, &name));
622     PetscCall(PetscObjectSetName((PetscObject)gv, name));
623     PetscCall(VecLoad_Default(gv, viewer));
624     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
625     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
626     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
627   } else PetscCall(VecLoad_Default(v, viewer));
628   PetscFunctionReturn(0);
629 }
630 
631 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer) {
632   DM        dm;
633   PetscBool ishdf5, isexodusii;
634 
635   PetscFunctionBegin;
636   PetscCall(VecGetDM(v, &dm));
637   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
640   if (ishdf5) {
641 #if defined(PETSC_HAVE_HDF5)
642     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
643 #else
644     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
645 #endif
646   } else if (isexodusii) {
647 #if defined(PETSC_HAVE_EXODUSII)
648     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
649 #else
650     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
651 #endif
652   } else PetscCall(VecLoad_Default(v, viewer));
653   PetscFunctionReturn(0);
654 }
655 
656 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer) {
657   DM                dm;
658   PetscViewerFormat format;
659   PetscBool         ishdf5;
660 
661   PetscFunctionBegin;
662   PetscCall(VecGetDM(originalv, &dm));
663   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
664   PetscCall(PetscViewerGetFormat(viewer, &format));
665   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
666   if (format == PETSC_VIEWER_NATIVE) {
667     if (dm->useNatural) {
668       if (dm->sfNatural) {
669         if (ishdf5) {
670 #if defined(PETSC_HAVE_HDF5)
671           Vec         v;
672           const char *vecname;
673 
674           PetscCall(DMGetGlobalVector(dm, &v));
675           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
676           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
677           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
678           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
679           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
680           PetscCall(DMRestoreGlobalVector(dm, &v));
681 #else
682           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
683 #endif
684         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
685       }
686     } else PetscCall(VecLoad_Default(originalv, viewer));
687   }
688   PetscFunctionReturn(0);
689 }
690 
691 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer) {
692   PetscSection       coordSection;
693   Vec                coordinates;
694   DMLabel            depthLabel, celltypeLabel;
695   const char        *name[4];
696   const PetscScalar *a;
697   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
698 
699   PetscFunctionBegin;
700   PetscCall(DMGetDimension(dm, &dim));
701   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
702   PetscCall(DMGetCoordinateSection(dm, &coordSection));
703   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
704   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
705   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
706   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
707   PetscCall(VecGetArrayRead(coordinates, &a));
708   name[0]       = "vertex";
709   name[1]       = "edge";
710   name[dim - 1] = "face";
711   name[dim]     = "cell";
712   for (c = cStart; c < cEnd; ++c) {
713     PetscInt *closure = NULL;
714     PetscInt  closureSize, cl, ct;
715 
716     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
717     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
718     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
719     PetscCall(PetscViewerASCIIPushTab(viewer));
720     for (cl = 0; cl < closureSize * 2; cl += 2) {
721       PetscInt point = closure[cl], depth, dof, off, d, p;
722 
723       if ((point < pStart) || (point >= pEnd)) continue;
724       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
725       if (!dof) continue;
726       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
727       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
728       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
729       for (p = 0; p < dof / dim; ++p) {
730         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
731         for (d = 0; d < dim; ++d) {
732           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
733           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
734         }
735         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
736       }
737       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
738     }
739     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
740     PetscCall(PetscViewerASCIIPopTab(viewer));
741   }
742   PetscCall(VecRestoreArrayRead(coordinates, &a));
743   PetscFunctionReturn(0);
744 }
745 
746 typedef enum {
747   CS_CARTESIAN,
748   CS_POLAR,
749   CS_CYLINDRICAL,
750   CS_SPHERICAL
751 } CoordSystem;
752 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
753 
754 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[]) {
755   PetscInt i;
756 
757   PetscFunctionBegin;
758   if (dim > 3) {
759     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
760   } else {
761     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
762 
763     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
764     switch (cs) {
765     case CS_CARTESIAN:
766       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
767       break;
768     case CS_POLAR:
769       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
770       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
771       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
772       break;
773     case CS_CYLINDRICAL:
774       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
775       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
776       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
777       trcoords[2] = coords[2];
778       break;
779     case CS_SPHERICAL:
780       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
781       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
782       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
783       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
784       break;
785     }
786     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
787   }
788   PetscFunctionReturn(0);
789 }
790 
791 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer) {
792   DM_Plex          *mesh = (DM_Plex *)dm->data;
793   DM                cdm, cdmCell;
794   PetscSection      coordSection, coordSectionCell;
795   Vec               coordinates, coordinatesCell;
796   PetscViewerFormat format;
797 
798   PetscFunctionBegin;
799   PetscCall(DMGetCoordinateDM(dm, &cdm));
800   PetscCall(DMGetCoordinateSection(dm, &coordSection));
801   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
802   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
803   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
804   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
805   PetscCall(PetscViewerGetFormat(viewer, &format));
806   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
807     const char *name;
808     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
809     PetscInt    pStart, pEnd, p, numLabels, l;
810     PetscMPIInt rank, size;
811 
812     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
813     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
814     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
815     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
816     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
817     PetscCall(DMGetDimension(dm, &dim));
818     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
819     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
820     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
821     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
822     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
823     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
824     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
825     for (p = pStart; p < pEnd; ++p) {
826       PetscInt dof, off, s;
827 
828       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
829       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
830       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
831     }
832     PetscCall(PetscViewerFlush(viewer));
833     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
834     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
835     for (p = pStart; p < pEnd; ++p) {
836       PetscInt dof, off, c;
837 
838       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
839       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
840       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
841     }
842     PetscCall(PetscViewerFlush(viewer));
843     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
844     if (coordSection && coordinates) {
845       CoordSystem        cs = CS_CARTESIAN;
846       const PetscScalar *array, *arrayCell = NULL;
847       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
848       PetscMPIInt        rank;
849       const char        *name;
850 
851       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
852       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
853       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
854       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
855       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
856       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
857       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
858       pStart = PetscMin(pvStart, pcStart);
859       pEnd   = PetscMax(pvEnd, pcEnd);
860       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
861       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
862       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
863       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
864 
865       PetscCall(VecGetArrayRead(coordinates, &array));
866       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
867       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
868       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
869       for (p = pStart; p < pEnd; ++p) {
870         PetscInt dof, off;
871 
872         if (p >= pvStart && p < pvEnd) {
873           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
874           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
875           if (dof) {
876             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
877             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
878             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
879           }
880         }
881         if (cdmCell && p >= pcStart && p < pcEnd) {
882           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
883           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
884           if (dof) {
885             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
886             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
887             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
888           }
889         }
890       }
891       PetscCall(PetscViewerFlush(viewer));
892       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
893       PetscCall(VecRestoreArrayRead(coordinates, &array));
894       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
895     }
896     PetscCall(DMGetNumLabels(dm, &numLabels));
897     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
898     for (l = 0; l < numLabels; ++l) {
899       DMLabel     label;
900       PetscBool   isdepth;
901       const char *name;
902 
903       PetscCall(DMGetLabelName(dm, l, &name));
904       PetscCall(PetscStrcmp(name, "depth", &isdepth));
905       if (isdepth) continue;
906       PetscCall(DMGetLabel(dm, name, &label));
907       PetscCall(DMLabelView(label, viewer));
908     }
909     if (size > 1) {
910       PetscSF sf;
911 
912       PetscCall(DMGetPointSF(dm, &sf));
913       PetscCall(PetscSFView(sf, viewer));
914     }
915     PetscCall(PetscViewerFlush(viewer));
916   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
917     const char  *name, *color;
918     const char  *defcolors[3]  = {"gray", "orange", "green"};
919     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
920     char         lname[PETSC_MAX_PATH_LEN];
921     PetscReal    scale      = 2.0;
922     PetscReal    tikzscale  = 1.0;
923     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
924     double       tcoords[3];
925     PetscScalar *coords;
926     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
927     PetscMPIInt  rank, size;
928     char       **names, **colors, **lcolors;
929     PetscBool    flg, lflg;
930     PetscBT      wp = NULL;
931     PetscInt     pEnd, pStart;
932 
933     PetscCall(DMGetDimension(dm, &dim));
934     PetscCall(DMPlexGetDepth(dm, &depth));
935     PetscCall(DMGetNumLabels(dm, &numLabels));
936     numLabels  = PetscMax(numLabels, 10);
937     numColors  = 10;
938     numLColors = 10;
939     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
940     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
941     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
942     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
943     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
944     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
945     n = 4;
946     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
947     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
948     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
949     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
950     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
951     if (!useLabels) numLabels = 0;
952     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
953     if (!useColors) {
954       numColors = 3;
955       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
956     }
957     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
958     if (!useColors) {
959       numLColors = 4;
960       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
961     }
962     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
963     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
964     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
965     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
966     if (depth < dim) plotEdges = PETSC_FALSE;
967     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
968 
969     /* filter points with labelvalue != labeldefaultvalue */
970     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
971     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
972     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
973     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
974     if (lflg) {
975       DMLabel lbl;
976 
977       PetscCall(DMGetLabel(dm, lname, &lbl));
978       if (lbl) {
979         PetscInt val, defval;
980 
981         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
982         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
983         for (c = pStart; c < pEnd; c++) {
984           PetscInt *closure = NULL;
985           PetscInt  closureSize;
986 
987           PetscCall(DMLabelGetValue(lbl, c, &val));
988           if (val == defval) continue;
989 
990           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
991           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
992           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
993         }
994       }
995     }
996 
997     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
998     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
999     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1000     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1001 \\documentclass[tikz]{standalone}\n\n\
1002 \\usepackage{pgflibraryshapes}\n\
1003 \\usetikzlibrary{backgrounds}\n\
1004 \\usetikzlibrary{arrows}\n\
1005 \\begin{document}\n"));
1006     if (size > 1) {
1007       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1008       for (p = 0; p < size; ++p) {
1009         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1010         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1011       }
1012       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1013     }
1014     if (drawHasse) {
1015       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1016 
1017       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1018       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1019       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1020       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1021       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1022       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1023       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1024       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1025       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1026       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1027       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1028       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1029     }
1030     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1031 
1032     /* Plot vertices */
1033     PetscCall(VecGetArray(coordinates, &coords));
1034     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1035     for (v = vStart; v < vEnd; ++v) {
1036       PetscInt  off, dof, d;
1037       PetscBool isLabeled = PETSC_FALSE;
1038 
1039       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1040       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1041       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1042       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1043       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1044       for (d = 0; d < dof; ++d) {
1045         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1046         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1047       }
1048       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1049       if (dim == 3) {
1050         PetscReal tmp = tcoords[1];
1051         tcoords[1]    = tcoords[2];
1052         tcoords[2]    = -tmp;
1053       }
1054       for (d = 0; d < dof; ++d) {
1055         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1056         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1057       }
1058       if (drawHasse) color = colors[0 % numColors];
1059       else color = colors[rank % numColors];
1060       for (l = 0; l < numLabels; ++l) {
1061         PetscInt val;
1062         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1063         if (val >= 0) {
1064           color     = lcolors[l % numLColors];
1065           isLabeled = PETSC_TRUE;
1066           break;
1067         }
1068       }
1069       if (drawNumbers[0]) {
1070         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1071       } else if (drawColors[0]) {
1072         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1073       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1074     }
1075     PetscCall(VecRestoreArray(coordinates, &coords));
1076     PetscCall(PetscViewerFlush(viewer));
1077     /* Plot edges */
1078     if (plotEdges) {
1079       PetscCall(VecGetArray(coordinates, &coords));
1080       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1081       for (e = eStart; e < eEnd; ++e) {
1082         const PetscInt *cone;
1083         PetscInt        coneSize, offA, offB, dof, d;
1084 
1085         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1086         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1087         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1088         PetscCall(DMPlexGetCone(dm, e, &cone));
1089         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1090         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1091         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1092         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1093         for (d = 0; d < dof; ++d) {
1094           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1095           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1096         }
1097         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1098         if (dim == 3) {
1099           PetscReal tmp = tcoords[1];
1100           tcoords[1]    = tcoords[2];
1101           tcoords[2]    = -tmp;
1102         }
1103         for (d = 0; d < dof; ++d) {
1104           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1105           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1106         }
1107         if (drawHasse) color = colors[1 % numColors];
1108         else color = colors[rank % numColors];
1109         for (l = 0; l < numLabels; ++l) {
1110           PetscInt val;
1111           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1112           if (val >= 0) {
1113             color = lcolors[l % numLColors];
1114             break;
1115           }
1116         }
1117         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1118       }
1119       PetscCall(VecRestoreArray(coordinates, &coords));
1120       PetscCall(PetscViewerFlush(viewer));
1121       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1122     }
1123     /* Plot cells */
1124     if (dim == 3 || !drawNumbers[1]) {
1125       for (e = eStart; e < eEnd; ++e) {
1126         const PetscInt *cone;
1127 
1128         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1129         color = colors[rank % numColors];
1130         for (l = 0; l < numLabels; ++l) {
1131           PetscInt val;
1132           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1133           if (val >= 0) {
1134             color = lcolors[l % numLColors];
1135             break;
1136           }
1137         }
1138         PetscCall(DMPlexGetCone(dm, e, &cone));
1139         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1140       }
1141     } else {
1142       DMPolytopeType ct;
1143 
1144       /* Drawing a 2D polygon */
1145       for (c = cStart; c < cEnd; ++c) {
1146         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1147         PetscCall(DMPlexGetCellType(dm, c, &ct));
1148         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1149           const PetscInt *cone;
1150           PetscInt        coneSize, e;
1151 
1152           PetscCall(DMPlexGetCone(dm, c, &cone));
1153           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1154           for (e = 0; e < coneSize; ++e) {
1155             const PetscInt *econe;
1156 
1157             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1158             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));
1159           }
1160         } else {
1161           PetscInt *closure = NULL;
1162           PetscInt  closureSize, Nv = 0, v;
1163 
1164           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1165           for (p = 0; p < closureSize * 2; p += 2) {
1166             const PetscInt point = closure[p];
1167 
1168             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1169           }
1170           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1171           for (v = 0; v <= Nv; ++v) {
1172             const PetscInt vertex = closure[v % Nv];
1173 
1174             if (v > 0) {
1175               if (plotEdges) {
1176                 const PetscInt *edge;
1177                 PetscInt        endpoints[2], ne;
1178 
1179                 endpoints[0] = closure[v - 1];
1180                 endpoints[1] = vertex;
1181                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1182                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1183                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1184                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1185               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1186             }
1187             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1188           }
1189           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1190           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1191         }
1192       }
1193     }
1194     for (c = cStart; c < cEnd; ++c) {
1195       double             ccoords[3] = {0.0, 0.0, 0.0};
1196       PetscBool          isLabeled  = PETSC_FALSE;
1197       PetscScalar       *cellCoords = NULL;
1198       const PetscScalar *array;
1199       PetscInt           numCoords, cdim, d;
1200       PetscBool          isDG;
1201 
1202       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1203       PetscCall(DMGetCoordinateDim(dm, &cdim));
1204       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1205       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1206       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1207       for (p = 0; p < numCoords / cdim; ++p) {
1208         for (d = 0; d < cdim; ++d) {
1209           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1210           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1211         }
1212         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1213         if (cdim == 3) {
1214           PetscReal tmp = tcoords[1];
1215           tcoords[1]    = tcoords[2];
1216           tcoords[2]    = -tmp;
1217         }
1218         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1219       }
1220       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1221       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1222       for (d = 0; d < cdim; ++d) {
1223         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1224         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1225       }
1226       if (drawHasse) color = colors[depth % numColors];
1227       else color = colors[rank % numColors];
1228       for (l = 0; l < numLabels; ++l) {
1229         PetscInt val;
1230         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1231         if (val >= 0) {
1232           color     = lcolors[l % numLColors];
1233           isLabeled = PETSC_TRUE;
1234           break;
1235         }
1236       }
1237       if (drawNumbers[dim]) {
1238         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1239       } else if (drawColors[dim]) {
1240         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1241       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1242     }
1243     if (drawHasse) {
1244       color = colors[depth % numColors];
1245       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1246       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1247       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1248       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1249       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1250 
1251       color = colors[1 % numColors];
1252       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1253       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1254       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1255       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1256       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1257 
1258       color = colors[0 % numColors];
1259       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1262       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1263       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1264 
1265       for (p = pStart; p < pEnd; ++p) {
1266         const PetscInt *cone;
1267         PetscInt        coneSize, cp;
1268 
1269         PetscCall(DMPlexGetCone(dm, p, &cone));
1270         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1271         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1272       }
1273     }
1274     PetscCall(PetscViewerFlush(viewer));
1275     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1276     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1277     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1278     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1279     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1280     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1281     PetscCall(PetscFree3(names, colors, lcolors));
1282     PetscCall(PetscBTDestroy(&wp));
1283   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1284     Vec                    cown, acown;
1285     VecScatter             sct;
1286     ISLocalToGlobalMapping g2l;
1287     IS                     gid, acis;
1288     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1289     MPI_Group              ggroup, ngroup;
1290     PetscScalar           *array, nid;
1291     const PetscInt        *idxs;
1292     PetscInt              *idxs2, *start, *adjacency, *work;
1293     PetscInt64             lm[3], gm[3];
1294     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1295     PetscMPIInt            d1, d2, rank;
1296 
1297     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1298     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1299 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1300     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1301 #endif
1302     if (ncomm != MPI_COMM_NULL) {
1303       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1304       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1305       d1 = 0;
1306       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1307       nid = d2;
1308       PetscCallMPI(MPI_Group_free(&ggroup));
1309       PetscCallMPI(MPI_Group_free(&ngroup));
1310       PetscCallMPI(MPI_Comm_free(&ncomm));
1311     } else nid = 0.0;
1312 
1313     /* Get connectivity */
1314     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1315     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1316 
1317     /* filter overlapped local cells */
1318     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1319     PetscCall(ISGetIndices(gid, &idxs));
1320     PetscCall(ISGetLocalSize(gid, &cum));
1321     PetscCall(PetscMalloc1(cum, &idxs2));
1322     for (c = cStart, cum = 0; c < cEnd; c++) {
1323       if (idxs[c - cStart] < 0) continue;
1324       idxs2[cum++] = idxs[c - cStart];
1325     }
1326     PetscCall(ISRestoreIndices(gid, &idxs));
1327     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1328     PetscCall(ISDestroy(&gid));
1329     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1330 
1331     /* support for node-aware cell locality */
1332     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1333     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1334     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1335     PetscCall(VecGetArray(cown, &array));
1336     for (c = 0; c < numVertices; c++) array[c] = nid;
1337     PetscCall(VecRestoreArray(cown, &array));
1338     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1339     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1340     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1341     PetscCall(ISDestroy(&acis));
1342     PetscCall(VecScatterDestroy(&sct));
1343     PetscCall(VecDestroy(&cown));
1344 
1345     /* compute edgeCut */
1346     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1347     PetscCall(PetscMalloc1(cum, &work));
1348     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1349     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1350     PetscCall(ISDestroy(&gid));
1351     PetscCall(VecGetArray(acown, &array));
1352     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1353       PetscInt totl;
1354 
1355       totl = start[c + 1] - start[c];
1356       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1357       for (i = 0; i < totl; i++) {
1358         if (work[i] < 0) {
1359           ect += 1;
1360           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1361         }
1362       }
1363     }
1364     PetscCall(PetscFree(work));
1365     PetscCall(VecRestoreArray(acown, &array));
1366     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1367     lm[1] = -numVertices;
1368     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1369     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1370     lm[0] = ect;                     /* edgeCut */
1371     lm[1] = ectn;                    /* node-aware edgeCut */
1372     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1373     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1374     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1375 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1376     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1377 #else
1378     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1379 #endif
1380     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1381     PetscCall(PetscFree(start));
1382     PetscCall(PetscFree(adjacency));
1383     PetscCall(VecDestroy(&acown));
1384   } else {
1385     const char    *name;
1386     PetscInt      *sizes, *hybsizes, *ghostsizes;
1387     PetscInt       locDepth, depth, cellHeight, dim, d;
1388     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1389     PetscInt       numLabels, l, maxSize = 17;
1390     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1391     MPI_Comm       comm;
1392     PetscMPIInt    size, rank;
1393 
1394     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1395     PetscCallMPI(MPI_Comm_size(comm, &size));
1396     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1397     PetscCall(DMGetDimension(dm, &dim));
1398     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1399     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1400     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1401     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1402     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1403     PetscCall(DMPlexGetDepth(dm, &locDepth));
1404     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1405     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1406     gcNum = gcEnd - gcStart;
1407     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1408     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1409     for (d = 0; d <= depth; d++) {
1410       PetscInt Nc[2] = {0, 0}, ict;
1411 
1412       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1413       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1414       ict = ct0;
1415       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1416       ct0 = (DMPolytopeType)ict;
1417       for (p = pStart; p < pEnd; ++p) {
1418         DMPolytopeType ct;
1419 
1420         PetscCall(DMPlexGetCellType(dm, p, &ct));
1421         if (ct == ct0) ++Nc[0];
1422         else ++Nc[1];
1423       }
1424       if (size < maxSize) {
1425         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1426         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1427         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1428         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1429         for (p = 0; p < size; ++p) {
1430           if (rank == 0) {
1431             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1432             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1433             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1434           }
1435         }
1436       } else {
1437         PetscInt locMinMax[2];
1438 
1439         locMinMax[0] = Nc[0] + Nc[1];
1440         locMinMax[1] = Nc[0] + Nc[1];
1441         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1442         locMinMax[0] = Nc[1];
1443         locMinMax[1] = Nc[1];
1444         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1445         if (d == depth) {
1446           locMinMax[0] = gcNum;
1447           locMinMax[1] = gcNum;
1448           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1449         }
1450         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1451         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1452         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1453         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1454       }
1455       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1456     }
1457     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1458     {
1459       const PetscReal *maxCell;
1460       const PetscReal *L;
1461       PetscBool        localized;
1462 
1463       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1464       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1465       if (L || localized) {
1466         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1467         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1468         if (L) {
1469           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1470           for (d = 0; d < dim; ++d) {
1471             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1472             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1473           }
1474           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1475         }
1476         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1477         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1478       }
1479     }
1480     PetscCall(DMGetNumLabels(dm, &numLabels));
1481     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1482     for (l = 0; l < numLabels; ++l) {
1483       DMLabel         label;
1484       const char     *name;
1485       IS              valueIS;
1486       const PetscInt *values;
1487       PetscInt        numValues, v;
1488 
1489       PetscCall(DMGetLabelName(dm, l, &name));
1490       PetscCall(DMGetLabel(dm, name, &label));
1491       PetscCall(DMLabelGetNumValues(label, &numValues));
1492       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1493       PetscCall(DMLabelGetValueIS(label, &valueIS));
1494       PetscCall(ISGetIndices(valueIS, &values));
1495       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1496       for (v = 0; v < numValues; ++v) {
1497         PetscInt size;
1498 
1499         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1500         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1501         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1502       }
1503       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1504       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1505       PetscCall(ISRestoreIndices(valueIS, &values));
1506       PetscCall(ISDestroy(&valueIS));
1507     }
1508     {
1509       char    **labelNames;
1510       PetscInt  Nl = numLabels;
1511       PetscBool flg;
1512 
1513       PetscCall(PetscMalloc1(Nl, &labelNames));
1514       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1515       for (l = 0; l < Nl; ++l) {
1516         DMLabel label;
1517 
1518         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1519         if (flg) {
1520           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1521           PetscCall(DMLabelView(label, viewer));
1522         }
1523         PetscCall(PetscFree(labelNames[l]));
1524       }
1525       PetscCall(PetscFree(labelNames));
1526     }
1527     /* If no fields are specified, people do not want to see adjacency */
1528     if (dm->Nf) {
1529       PetscInt f;
1530 
1531       for (f = 0; f < dm->Nf; ++f) {
1532         const char *name;
1533 
1534         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1535         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1536         PetscCall(PetscViewerASCIIPushTab(viewer));
1537         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1538         if (dm->fields[f].adjacency[0]) {
1539           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1540           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1541         } else {
1542           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1543           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1544         }
1545         PetscCall(PetscViewerASCIIPopTab(viewer));
1546       }
1547     }
1548     PetscCall(DMGetCoarseDM(dm, &cdm));
1549     if (cdm) {
1550       PetscCall(PetscViewerASCIIPushTab(viewer));
1551       PetscCall(DMPlexView_Ascii(cdm, viewer));
1552       PetscCall(PetscViewerASCIIPopTab(viewer));
1553     }
1554   }
1555   PetscFunctionReturn(0);
1556 }
1557 
1558 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[]) {
1559   DMPolytopeType ct;
1560   PetscMPIInt    rank;
1561   PetscInt       cdim;
1562 
1563   PetscFunctionBegin;
1564   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1565   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1566   PetscCall(DMGetCoordinateDim(dm, &cdim));
1567   switch (ct) {
1568   case DM_POLYTOPE_SEGMENT:
1569   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1570     switch (cdim) {
1571     case 1: {
1572       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1573       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1574 
1575       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1576       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1577       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1578     } break;
1579     case 2: {
1580       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1581       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1582       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1583 
1584       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1585       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));
1586       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));
1587     } break;
1588     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1589     }
1590     break;
1591   case DM_POLYTOPE_TRIANGLE:
1592     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1593     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1594     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1595     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1596     break;
1597   case DM_POLYTOPE_QUADRILATERAL:
1598     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1599     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1600     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1601     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1602     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1603     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1604     break;
1605   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1606   }
1607   PetscFunctionReturn(0);
1608 }
1609 
1610 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[]) {
1611   DMPolytopeType ct;
1612   PetscReal      centroid[2] = {0., 0.};
1613   PetscMPIInt    rank;
1614   PetscInt       fillColor, v, e, d;
1615 
1616   PetscFunctionBegin;
1617   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1618   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1619   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1620   switch (ct) {
1621   case DM_POLYTOPE_TRIANGLE: {
1622     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1623 
1624     for (v = 0; v < 3; ++v) {
1625       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1626       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1627     }
1628     for (e = 0; e < 3; ++e) {
1629       refCoords[0] = refVertices[e * 2 + 0];
1630       refCoords[1] = refVertices[e * 2 + 1];
1631       for (d = 1; d <= edgeDiv; ++d) {
1632         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1633         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1634       }
1635       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1636       for (d = 0; d < edgeDiv; ++d) {
1637         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));
1638         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1639       }
1640     }
1641   } break;
1642   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1643   }
1644   PetscFunctionReturn(0);
1645 }
1646 
1647 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer) {
1648   PetscDraw          draw;
1649   DM                 cdm;
1650   PetscSection       coordSection;
1651   Vec                coordinates;
1652   const PetscScalar *coords;
1653   PetscReal          xyl[2], xyr[2], bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1654   PetscReal         *refCoords, *edgeCoords;
1655   PetscBool          isnull, drawAffine = PETSC_TRUE;
1656   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1657 
1658   PetscFunctionBegin;
1659   PetscCall(DMGetCoordinateDim(dm, &dim));
1660   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1661   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1662   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1663   PetscCall(DMGetCoordinateDM(dm, &cdm));
1664   PetscCall(DMGetLocalSection(cdm, &coordSection));
1665   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1666   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1667   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1668 
1669   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1670   PetscCall(PetscDrawIsNull(draw, &isnull));
1671   if (isnull) PetscFunctionReturn(0);
1672   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1673 
1674   PetscCall(VecGetLocalSize(coordinates, &N));
1675   PetscCall(VecGetArrayRead(coordinates, &coords));
1676   for (c = 0; c < N; c += dim) {
1677     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
1678     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1679     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
1680     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
1681   }
1682   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1683   PetscCall(MPIU_Allreduce(&bound[0], xyl, 2, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
1684   PetscCall(MPIU_Allreduce(&bound[2], xyr, 2, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)dm)));
1685   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1686   PetscCall(PetscDrawClear(draw));
1687 
1688   for (c = cStart; c < cEnd; ++c) {
1689     PetscScalar *coords = NULL;
1690     PetscInt     numCoords;
1691 
1692     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1693     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1694     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1695     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1696   }
1697   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1698   PetscCall(PetscDrawFlush(draw));
1699   PetscCall(PetscDrawPause(draw));
1700   PetscCall(PetscDrawSave(draw));
1701   PetscFunctionReturn(0);
1702 }
1703 
1704 #if defined(PETSC_HAVE_EXODUSII)
1705 #include <exodusII.h>
1706 #include <petscviewerexodusii.h>
1707 #endif
1708 
1709 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer) {
1710   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1711   char      name[PETSC_MAX_PATH_LEN];
1712 
1713   PetscFunctionBegin;
1714   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1715   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1716   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1717   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1718   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1719   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1720   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1721   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1722   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1723   if (iascii) {
1724     PetscViewerFormat format;
1725     PetscCall(PetscViewerGetFormat(viewer, &format));
1726     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1727     else PetscCall(DMPlexView_Ascii(dm, viewer));
1728   } else if (ishdf5) {
1729 #if defined(PETSC_HAVE_HDF5)
1730     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1731 #else
1732     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1733 #endif
1734   } else if (isvtk) {
1735     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1736   } else if (isdraw) {
1737     PetscCall(DMPlexView_Draw(dm, viewer));
1738   } else if (isglvis) {
1739     PetscCall(DMPlexView_GLVis(dm, viewer));
1740 #if defined(PETSC_HAVE_EXODUSII)
1741   } else if (isexodus) {
1742     /*
1743       exodusII requires that all sets be part of exactly one cell set.
1744       If the dm does not have a "Cell Sets" label defined, we create one
1745       with ID 1, containig all cells.
1746       Note that if the Cell Sets label is defined but does not cover all cells,
1747       we may still have a problem. This should probably be checked here or in the viewer;
1748     */
1749     PetscInt numCS;
1750     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1751     if (!numCS) {
1752       PetscInt cStart, cEnd, c;
1753       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1754       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1755       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1756     }
1757     PetscCall(DMView_PlexExodusII(dm, viewer));
1758 #endif
1759 #if defined(PETSC_HAVE_CGNS)
1760   } else if (iscgns) {
1761     PetscCall(DMView_PlexCGNS(dm, viewer));
1762 #endif
1763   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1764   /* Optionally view the partition */
1765   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1766   if (flg) {
1767     Vec ranks;
1768     PetscCall(DMPlexCreateRankField(dm, &ranks));
1769     PetscCall(VecView(ranks, viewer));
1770     PetscCall(VecDestroy(&ranks));
1771   }
1772   /* Optionally view a label */
1773   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1774   if (flg) {
1775     DMLabel label;
1776     Vec     val;
1777 
1778     PetscCall(DMGetLabel(dm, name, &label));
1779     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1780     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1781     PetscCall(VecView(val, viewer));
1782     PetscCall(VecDestroy(&val));
1783   }
1784   PetscFunctionReturn(0);
1785 }
1786 
1787 /*@
1788   DMPlexTopologyView - Saves a DMPlex topology into a file
1789 
1790   Collective on DM
1791 
1792   Input Parameters:
1793 + dm                - The DM whose topology is to be saved
1794 - viewer            - The PetscViewer for saving
1795 
1796   Level: advanced
1797 
1798 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1799 @*/
1800 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer) {
1801   PetscBool ishdf5;
1802 
1803   PetscFunctionBegin;
1804   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1805   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1806   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1807   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1808   if (ishdf5) {
1809 #if defined(PETSC_HAVE_HDF5)
1810     PetscViewerFormat format;
1811     PetscCall(PetscViewerGetFormat(viewer, &format));
1812     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1813       IS globalPointNumbering;
1814 
1815       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1816       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1817       PetscCall(ISDestroy(&globalPointNumbering));
1818     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1819 #else
1820     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1821 #endif
1822   }
1823   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1824   PetscFunctionReturn(0);
1825 }
1826 
1827 /*@
1828   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1829 
1830   Collective on DM
1831 
1832   Input Parameters:
1833 + dm     - The DM whose coordinates are to be saved
1834 - viewer - The PetscViewer for saving
1835 
1836   Level: advanced
1837 
1838 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1839 @*/
1840 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer) {
1841   PetscBool ishdf5;
1842 
1843   PetscFunctionBegin;
1844   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1845   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1846   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1847   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1848   if (ishdf5) {
1849 #if defined(PETSC_HAVE_HDF5)
1850     PetscViewerFormat format;
1851     PetscCall(PetscViewerGetFormat(viewer, &format));
1852     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1853       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1854     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1855 #else
1856     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1857 #endif
1858   }
1859   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1860   PetscFunctionReturn(0);
1861 }
1862 
1863 /*@
1864   DMPlexLabelsView - Saves DMPlex labels into a file
1865 
1866   Collective on DM
1867 
1868   Input Parameters:
1869 + dm     - The DM whose labels are to be saved
1870 - viewer - The PetscViewer for saving
1871 
1872   Level: advanced
1873 
1874 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1875 @*/
1876 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer) {
1877   PetscBool ishdf5;
1878 
1879   PetscFunctionBegin;
1880   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1881   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1882   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1883   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1884   if (ishdf5) {
1885 #if defined(PETSC_HAVE_HDF5)
1886     IS                globalPointNumbering;
1887     PetscViewerFormat format;
1888 
1889     PetscCall(PetscViewerGetFormat(viewer, &format));
1890     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1891       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1892       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1893       PetscCall(ISDestroy(&globalPointNumbering));
1894     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1895 #else
1896     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1897 #endif
1898   }
1899   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1900   PetscFunctionReturn(0);
1901 }
1902 
1903 /*@
1904   DMPlexSectionView - Saves a section associated with a DMPlex
1905 
1906   Collective on DM
1907 
1908   Input Parameters:
1909 + dm         - The DM that contains the topology on which the section to be saved is defined
1910 . viewer     - The PetscViewer for saving
1911 - sectiondm  - The DM that contains the section to be saved
1912 
1913   Level: advanced
1914 
1915   Notes:
1916   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.
1917 
1918   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.
1919 
1920 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1921 @*/
1922 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm) {
1923   PetscBool ishdf5;
1924 
1925   PetscFunctionBegin;
1926   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1927   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1928   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1929   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1930   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1931   if (ishdf5) {
1932 #if defined(PETSC_HAVE_HDF5)
1933     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1934 #else
1935     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1936 #endif
1937   }
1938   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1939   PetscFunctionReturn(0);
1940 }
1941 
1942 /*@
1943   DMPlexGlobalVectorView - Saves a global vector
1944 
1945   Collective on DM
1946 
1947   Input Parameters:
1948 + dm        - The DM that represents the topology
1949 . viewer    - The PetscViewer to save data with
1950 . sectiondm - The DM that contains the global section on which vec is defined
1951 - vec       - The global vector to be saved
1952 
1953   Level: advanced
1954 
1955   Notes:
1956   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.
1957 
1958   Typical calling sequence
1959 $       DMCreate(PETSC_COMM_WORLD, &dm);
1960 $       DMSetType(dm, DMPLEX);
1961 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1962 $       DMClone(dm, &sectiondm);
1963 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1964 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1965 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1966 $       PetscSectionSetChart(section, pStart, pEnd);
1967 $       PetscSectionSetUp(section);
1968 $       DMSetLocalSection(sectiondm, section);
1969 $       PetscSectionDestroy(&section);
1970 $       DMGetGlobalVector(sectiondm, &vec);
1971 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1972 $       DMPlexTopologyView(dm, viewer);
1973 $       DMPlexSectionView(dm, viewer, sectiondm);
1974 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1975 $       DMRestoreGlobalVector(sectiondm, &vec);
1976 $       DMDestroy(&sectiondm);
1977 $       DMDestroy(&dm);
1978 
1979 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1980 @*/
1981 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec) {
1982   PetscBool ishdf5;
1983 
1984   PetscFunctionBegin;
1985   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1986   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1987   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1988   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1989   /* Check consistency */
1990   {
1991     PetscSection section;
1992     PetscBool    includesConstraints;
1993     PetscInt     m, m1;
1994 
1995     PetscCall(VecGetLocalSize(vec, &m1));
1996     PetscCall(DMGetGlobalSection(sectiondm, &section));
1997     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1998     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
1999     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2000     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2001   }
2002   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2003   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2004   if (ishdf5) {
2005 #if defined(PETSC_HAVE_HDF5)
2006     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2007 #else
2008     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2009 #endif
2010   }
2011   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2012   PetscFunctionReturn(0);
2013 }
2014 
2015 /*@
2016   DMPlexLocalVectorView - Saves a local vector
2017 
2018   Collective on DM
2019 
2020   Input Parameters:
2021 + dm        - The DM that represents the topology
2022 . viewer    - The PetscViewer to save data with
2023 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2024 - vec       - The local vector to be saved
2025 
2026   Level: advanced
2027 
2028   Notes:
2029   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.
2030 
2031   Typical calling sequence
2032 $       DMCreate(PETSC_COMM_WORLD, &dm);
2033 $       DMSetType(dm, DMPLEX);
2034 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2035 $       DMClone(dm, &sectiondm);
2036 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2037 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2038 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2039 $       PetscSectionSetChart(section, pStart, pEnd);
2040 $       PetscSectionSetUp(section);
2041 $       DMSetLocalSection(sectiondm, section);
2042 $       DMGetLocalVector(sectiondm, &vec);
2043 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2044 $       DMPlexTopologyView(dm, viewer);
2045 $       DMPlexSectionView(dm, viewer, sectiondm);
2046 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2047 $       DMRestoreLocalVector(sectiondm, &vec);
2048 $       DMDestroy(&sectiondm);
2049 $       DMDestroy(&dm);
2050 
2051 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2052 @*/
2053 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec) {
2054   PetscBool ishdf5;
2055 
2056   PetscFunctionBegin;
2057   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2058   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2059   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2060   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2061   /* Check consistency */
2062   {
2063     PetscSection section;
2064     PetscBool    includesConstraints;
2065     PetscInt     m, m1;
2066 
2067     PetscCall(VecGetLocalSize(vec, &m1));
2068     PetscCall(DMGetLocalSection(sectiondm, &section));
2069     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2070     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2071     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2072     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2073   }
2074   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2075   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2076   if (ishdf5) {
2077 #if defined(PETSC_HAVE_HDF5)
2078     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2079 #else
2080     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2081 #endif
2082   }
2083   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2084   PetscFunctionReturn(0);
2085 }
2086 
2087 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer) {
2088   PetscBool ishdf5;
2089 
2090   PetscFunctionBegin;
2091   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2092   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2093   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2094   if (ishdf5) {
2095 #if defined(PETSC_HAVE_HDF5)
2096     PetscViewerFormat format;
2097     PetscCall(PetscViewerGetFormat(viewer, &format));
2098     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2099       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2100     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2101       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2102     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2103     PetscFunctionReturn(0);
2104 #else
2105     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2106 #endif
2107   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2108 }
2109 
2110 /*@
2111   DMPlexTopologyLoad - Loads a topology into a DMPlex
2112 
2113   Collective on DM
2114 
2115   Input Parameters:
2116 + dm                - The DM into which the topology is loaded
2117 - viewer            - The PetscViewer for the saved topology
2118 
2119   Output Parameters:
2120 . 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
2121 
2122   Level: advanced
2123 
2124 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2125 @*/
2126 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF) {
2127   PetscBool ishdf5;
2128 
2129   PetscFunctionBegin;
2130   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2131   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2132   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2133   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2134   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2135   if (ishdf5) {
2136 #if defined(PETSC_HAVE_HDF5)
2137     PetscViewerFormat format;
2138     PetscCall(PetscViewerGetFormat(viewer, &format));
2139     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2140       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2141     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2142 #else
2143     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2144 #endif
2145   }
2146   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2147   PetscFunctionReturn(0);
2148 }
2149 
2150 /*@
2151   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2152 
2153   Collective on DM
2154 
2155   Input Parameters:
2156 + dm     - The DM into which the coordinates are loaded
2157 . viewer - The PetscViewer for the saved coordinates
2158 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2159 
2160   Level: advanced
2161 
2162 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2163 @*/
2164 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF) {
2165   PetscBool ishdf5;
2166 
2167   PetscFunctionBegin;
2168   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2169   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2170   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2171   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2172   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2173   if (ishdf5) {
2174 #if defined(PETSC_HAVE_HDF5)
2175     PetscViewerFormat format;
2176     PetscCall(PetscViewerGetFormat(viewer, &format));
2177     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2178       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2179     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2180 #else
2181     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2182 #endif
2183   }
2184   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2185   PetscFunctionReturn(0);
2186 }
2187 
2188 /*@
2189   DMPlexLabelsLoad - Loads labels into a DMPlex
2190 
2191   Collective on DM
2192 
2193   Input Parameters:
2194 + dm     - The DM into which the labels are loaded
2195 . viewer - The PetscViewer for the saved labels
2196 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2197 
2198   Level: advanced
2199 
2200   Notes:
2201   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2202 
2203 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2204 @*/
2205 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF) {
2206   PetscBool ishdf5;
2207 
2208   PetscFunctionBegin;
2209   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2210   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2211   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2212   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2213   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2214   if (ishdf5) {
2215 #if defined(PETSC_HAVE_HDF5)
2216     PetscViewerFormat format;
2217 
2218     PetscCall(PetscViewerGetFormat(viewer, &format));
2219     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2220       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2221     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2222 #else
2223     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2224 #endif
2225   }
2226   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2227   PetscFunctionReturn(0);
2228 }
2229 
2230 /*@
2231   DMPlexSectionLoad - Loads section into a DMPlex
2232 
2233   Collective on DM
2234 
2235   Input Parameters:
2236 + dm          - The DM that represents the topology
2237 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2238 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2239 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2240 
2241   Output Parameters
2242 + 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)
2243 - 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)
2244 
2245   Level: advanced
2246 
2247   Notes:
2248   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.
2249 
2250   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.
2251 
2252   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.
2253 
2254   Example using 2 processes:
2255 $  NX (number of points on dm): 4
2256 $  sectionA                   : the on-disk section
2257 $  vecA                       : a vector associated with sectionA
2258 $  sectionB                   : sectiondm's local section constructed in this function
2259 $  vecB (local)               : a vector associated with sectiondm's local section
2260 $  vecB (global)              : a vector associated with sectiondm's global section
2261 $
2262 $                                     rank 0    rank 1
2263 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2264 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2265 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2266 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2267 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2268 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2269 $  sectionB->atlasDof             :     1 0 1 | 1 3
2270 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2271 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2272 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2273 $
2274 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2275 
2276 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2277 @*/
2278 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF) {
2279   PetscBool ishdf5;
2280 
2281   PetscFunctionBegin;
2282   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2283   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2284   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2285   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2286   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2287   if (localDofSF) PetscValidPointer(localDofSF, 6);
2288   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2289   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2290   if (ishdf5) {
2291 #if defined(PETSC_HAVE_HDF5)
2292     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2293 #else
2294     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2295 #endif
2296   }
2297   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2298   PetscFunctionReturn(0);
2299 }
2300 
2301 /*@
2302   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2303 
2304   Collective on DM
2305 
2306   Input Parameters:
2307 + dm        - The DM that represents the topology
2308 . viewer    - The PetscViewer that represents the on-disk vector data
2309 . sectiondm - The DM that contains the global section on which vec is defined
2310 . sf        - The SF that migrates the on-disk vector data into vec
2311 - vec       - The global vector to set values of
2312 
2313   Level: advanced
2314 
2315   Notes:
2316   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.
2317 
2318   Typical calling sequence
2319 $       DMCreate(PETSC_COMM_WORLD, &dm);
2320 $       DMSetType(dm, DMPLEX);
2321 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2322 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2323 $       DMClone(dm, &sectiondm);
2324 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2325 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2326 $       DMGetGlobalVector(sectiondm, &vec);
2327 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2328 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2329 $       DMRestoreGlobalVector(sectiondm, &vec);
2330 $       PetscSFDestroy(&gsf);
2331 $       PetscSFDestroy(&sfX);
2332 $       DMDestroy(&sectiondm);
2333 $       DMDestroy(&dm);
2334 
2335 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2336 @*/
2337 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec) {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2344   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2345   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2346   /* Check consistency */
2347   {
2348     PetscSection section;
2349     PetscBool    includesConstraints;
2350     PetscInt     m, m1;
2351 
2352     PetscCall(VecGetLocalSize(vec, &m1));
2353     PetscCall(DMGetGlobalSection(sectiondm, &section));
2354     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2355     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2356     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2357     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2358   }
2359   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2360   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2361   if (ishdf5) {
2362 #if defined(PETSC_HAVE_HDF5)
2363     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2364 #else
2365     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2366 #endif
2367   }
2368   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2369   PetscFunctionReturn(0);
2370 }
2371 
2372 /*@
2373   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2374 
2375   Collective on DM
2376 
2377   Input Parameters:
2378 + dm        - The DM that represents the topology
2379 . viewer    - The PetscViewer that represents the on-disk vector data
2380 . sectiondm - The DM that contains the local section on which vec is defined
2381 . sf        - The SF that migrates the on-disk vector data into vec
2382 - vec       - The local vector to set values of
2383 
2384   Level: advanced
2385 
2386   Notes:
2387   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.
2388 
2389   Typical calling sequence
2390 $       DMCreate(PETSC_COMM_WORLD, &dm);
2391 $       DMSetType(dm, DMPLEX);
2392 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2393 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2394 $       DMClone(dm, &sectiondm);
2395 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2396 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2397 $       DMGetLocalVector(sectiondm, &vec);
2398 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2399 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2400 $       DMRestoreLocalVector(sectiondm, &vec);
2401 $       PetscSFDestroy(&lsf);
2402 $       PetscSFDestroy(&sfX);
2403 $       DMDestroy(&sectiondm);
2404 $       DMDestroy(&dm);
2405 
2406 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2407 @*/
2408 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec) {
2409   PetscBool ishdf5;
2410 
2411   PetscFunctionBegin;
2412   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2413   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2414   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2415   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2416   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2417   /* Check consistency */
2418   {
2419     PetscSection section;
2420     PetscBool    includesConstraints;
2421     PetscInt     m, m1;
2422 
2423     PetscCall(VecGetLocalSize(vec, &m1));
2424     PetscCall(DMGetLocalSection(sectiondm, &section));
2425     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2426     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2427     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2428     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2429   }
2430   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2431   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2432   if (ishdf5) {
2433 #if defined(PETSC_HAVE_HDF5)
2434     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2435 #else
2436     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2437 #endif
2438   }
2439   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2440   PetscFunctionReturn(0);
2441 }
2442 
2443 PetscErrorCode DMDestroy_Plex(DM dm) {
2444   DM_Plex *mesh = (DM_Plex *)dm->data;
2445 
2446   PetscFunctionBegin;
2447   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2448   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2449   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2450   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2451   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2452   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2453   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2454   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2455   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2456   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2457   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2458   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2459   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2460   if (--mesh->refct > 0) PetscFunctionReturn(0);
2461   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2462   PetscCall(PetscFree(mesh->cones));
2463   PetscCall(PetscFree(mesh->coneOrientations));
2464   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2465   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2466   PetscCall(PetscFree(mesh->supports));
2467   PetscCall(PetscFree(mesh->facesTmp));
2468   PetscCall(PetscFree(mesh->tetgenOpts));
2469   PetscCall(PetscFree(mesh->triangleOpts));
2470   PetscCall(PetscFree(mesh->transformType));
2471   PetscCall(PetscFree(mesh->distributionName));
2472   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2473   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2474   PetscCall(ISDestroy(&mesh->subpointIS));
2475   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2476   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2477   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2478   PetscCall(ISDestroy(&mesh->anchorIS));
2479   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2480   PetscCall(PetscFree(mesh->parents));
2481   PetscCall(PetscFree(mesh->childIDs));
2482   PetscCall(PetscSectionDestroy(&mesh->childSection));
2483   PetscCall(PetscFree(mesh->children));
2484   PetscCall(DMDestroy(&mesh->referenceTree));
2485   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2486   PetscCall(PetscFree(mesh->neighbors));
2487   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2488   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2489   PetscCall(PetscFree(mesh));
2490   PetscFunctionReturn(0);
2491 }
2492 
2493 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J) {
2494   PetscSection           sectionGlobal;
2495   PetscInt               bs = -1, mbs;
2496   PetscInt               localSize, localStart = 0;
2497   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2498   MatType                mtype;
2499   ISLocalToGlobalMapping ltog;
2500 
2501   PetscFunctionBegin;
2502   PetscCall(MatInitializePackage());
2503   mtype = dm->mattype;
2504   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2505   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2506   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2507   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2508   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2509   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2510   PetscCall(MatSetType(*J, mtype));
2511   PetscCall(MatSetFromOptions(*J));
2512   PetscCall(MatGetBlockSize(*J, &mbs));
2513   if (mbs > 1) bs = mbs;
2514   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2515   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2516   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2517   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2518   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2519   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2520   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2521   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2522   if (!isShell) {
2523     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2524     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2525     PetscInt  pStart, pEnd, p, dof, cdof;
2526 
2527     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2528 
2529     PetscCall(PetscCalloc1(localSize, &pblocks));
2530     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2531     for (p = pStart; p < pEnd; ++p) {
2532       PetscInt bdof, offset;
2533 
2534       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2535       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2536       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2537       for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2538       dof  = dof < 0 ? -(dof + 1) : dof;
2539       bdof = cdof && (dof - cdof) ? 1 : dof;
2540       if (dof) {
2541         if (bs < 0) {
2542           bs = bdof;
2543         } else if (bs != bdof) {
2544           bs = 1;
2545         }
2546       }
2547     }
2548     /* Must have same blocksize on all procs (some might have no points) */
2549     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2550     bsLocal[1] = bs;
2551     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2552     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2553     else bs = bsMinMax[0];
2554     bs = PetscMax(1, bs);
2555     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2556     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2557       PetscCall(MatSetBlockSize(*J, bs));
2558       PetscCall(MatSetUp(*J));
2559     } else {
2560       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2561       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2562       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2563     }
2564     { // Consolidate blocks
2565       PetscInt nblocks = 0;
2566       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2567         if (pblocks[i] == 0) continue;
2568         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2569         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2570       }
2571       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2572     }
2573     PetscCall(PetscFree(pblocks));
2574   }
2575   PetscCall(MatSetDM(*J, dm));
2576   PetscFunctionReturn(0);
2577 }
2578 
2579 /*@
2580   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2581 
2582   Not collective
2583 
2584   Input Parameter:
2585 . mesh - The DMPlex
2586 
2587   Output Parameters:
2588 . subsection - The subdomain section
2589 
2590   Level: developer
2591 
2592 .seealso:
2593 @*/
2594 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection) {
2595   DM_Plex *mesh = (DM_Plex *)dm->data;
2596 
2597   PetscFunctionBegin;
2598   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2599   if (!mesh->subdomainSection) {
2600     PetscSection section;
2601     PetscSF      sf;
2602 
2603     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2604     PetscCall(DMGetLocalSection(dm, &section));
2605     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2606     PetscCall(PetscSFDestroy(&sf));
2607   }
2608   *subsection = mesh->subdomainSection;
2609   PetscFunctionReturn(0);
2610 }
2611 
2612 /*@
2613   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2614 
2615   Not collective
2616 
2617   Input Parameter:
2618 . mesh - The DMPlex
2619 
2620   Output Parameters:
2621 + pStart - The first mesh point
2622 - pEnd   - The upper bound for mesh points
2623 
2624   Level: beginner
2625 
2626 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2627 @*/
2628 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd) {
2629   DM_Plex *mesh = (DM_Plex *)dm->data;
2630 
2631   PetscFunctionBegin;
2632   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2633   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2634   PetscFunctionReturn(0);
2635 }
2636 
2637 /*@
2638   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2639 
2640   Not collective
2641 
2642   Input Parameters:
2643 + mesh - The DMPlex
2644 . pStart - The first mesh point
2645 - pEnd   - The upper bound for mesh points
2646 
2647   Output Parameters:
2648 
2649   Level: beginner
2650 
2651 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2652 @*/
2653 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd) {
2654   DM_Plex *mesh = (DM_Plex *)dm->data;
2655 
2656   PetscFunctionBegin;
2657   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2658   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2659   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2660   PetscFunctionReturn(0);
2661 }
2662 
2663 /*@
2664   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2665 
2666   Not collective
2667 
2668   Input Parameters:
2669 + mesh - The DMPlex
2670 - p - The point, which must lie in the chart set with DMPlexSetChart()
2671 
2672   Output Parameter:
2673 . size - The cone size for point p
2674 
2675   Level: beginner
2676 
2677 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2678 @*/
2679 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size) {
2680   DM_Plex *mesh = (DM_Plex *)dm->data;
2681 
2682   PetscFunctionBegin;
2683   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2684   PetscValidIntPointer(size, 3);
2685   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2686   PetscFunctionReturn(0);
2687 }
2688 
2689 /*@
2690   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2691 
2692   Not collective
2693 
2694   Input Parameters:
2695 + mesh - The DMPlex
2696 . p - The point, which must lie in the chart set with DMPlexSetChart()
2697 - size - The cone size for point p
2698 
2699   Output Parameter:
2700 
2701   Note:
2702   This should be called after DMPlexSetChart().
2703 
2704   Level: beginner
2705 
2706 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2707 @*/
2708 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size) {
2709   DM_Plex *mesh = (DM_Plex *)dm->data;
2710 
2711   PetscFunctionBegin;
2712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2713   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2714   PetscFunctionReturn(0);
2715 }
2716 
2717 /*@
2718   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2719 
2720   Not collective
2721 
2722   Input Parameters:
2723 + mesh - The DMPlex
2724 . p - The point, which must lie in the chart set with DMPlexSetChart()
2725 - size - The additional cone size for point p
2726 
2727   Output Parameter:
2728 
2729   Note:
2730   This should be called after DMPlexSetChart().
2731 
2732   Level: beginner
2733 
2734 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2735 @*/
2736 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size) {
2737   DM_Plex *mesh = (DM_Plex *)dm->data;
2738   PetscFunctionBegin;
2739   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2740   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2741   PetscFunctionReturn(0);
2742 }
2743 
2744 /*@C
2745   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2746 
2747   Not collective
2748 
2749   Input Parameters:
2750 + dm - The DMPlex
2751 - p - The point, which must lie in the chart set with DMPlexSetChart()
2752 
2753   Output Parameter:
2754 . cone - An array of points which are on the in-edges for point p
2755 
2756   Level: beginner
2757 
2758   Fortran Notes:
2759   Since it returns an array, this routine is only available in Fortran 90, and you must
2760   include petsc.h90 in your code.
2761   You must also call DMPlexRestoreCone() after you finish using the returned array.
2762   DMPlexRestoreCone() is not needed/available in C.
2763 
2764 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2765 @*/
2766 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[]) {
2767   DM_Plex *mesh = (DM_Plex *)dm->data;
2768   PetscInt off;
2769 
2770   PetscFunctionBegin;
2771   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2772   PetscValidPointer(cone, 3);
2773   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2774   *cone = &mesh->cones[off];
2775   PetscFunctionReturn(0);
2776 }
2777 
2778 /*@C
2779   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2780 
2781   Not collective
2782 
2783   Input Parameters:
2784 + dm - The DMPlex
2785 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2786 
2787   Output Parameters:
2788 + pConesSection - PetscSection describing the layout of pCones
2789 - pCones - An array of points which are on the in-edges for the point set p
2790 
2791   Level: intermediate
2792 
2793 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2794 @*/
2795 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones) {
2796   PetscSection cs, newcs;
2797   PetscInt    *cones;
2798   PetscInt    *newarr = NULL;
2799   PetscInt     n;
2800 
2801   PetscFunctionBegin;
2802   PetscCall(DMPlexGetCones(dm, &cones));
2803   PetscCall(DMPlexGetConeSection(dm, &cs));
2804   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2805   if (pConesSection) *pConesSection = newcs;
2806   if (pCones) {
2807     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2808     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2809   }
2810   PetscFunctionReturn(0);
2811 }
2812 
2813 /*@
2814   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2815 
2816   Not collective
2817 
2818   Input Parameters:
2819 + dm - The DMPlex
2820 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2821 
2822   Output Parameter:
2823 . expandedPoints - An array of vertices recursively expanded from input points
2824 
2825   Level: advanced
2826 
2827   Notes:
2828   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2829   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2830 
2831 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2832 @*/
2833 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints) {
2834   IS      *expandedPointsAll;
2835   PetscInt depth;
2836 
2837   PetscFunctionBegin;
2838   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2839   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2840   PetscValidPointer(expandedPoints, 3);
2841   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2842   *expandedPoints = expandedPointsAll[0];
2843   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2844   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2845   PetscFunctionReturn(0);
2846 }
2847 
2848 /*@
2849   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).
2850 
2851   Not collective
2852 
2853   Input Parameters:
2854 + dm - The DMPlex
2855 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2856 
2857   Output Parameters:
2858 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2859 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2860 - sections - (optional) An array of sections which describe mappings from points to their cone points
2861 
2862   Level: advanced
2863 
2864   Notes:
2865   Like DMPlexGetConeTuple() but recursive.
2866 
2867   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.
2868   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2869 
2870   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:
2871   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2872   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2873 
2874 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2875 @*/
2876 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[]) {
2877   const PetscInt *arr0 = NULL, *cone = NULL;
2878   PetscInt       *arr = NULL, *newarr = NULL;
2879   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2880   IS             *expandedPoints_;
2881   PetscSection   *sections_;
2882 
2883   PetscFunctionBegin;
2884   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2885   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2886   if (depth) PetscValidIntPointer(depth, 3);
2887   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2888   if (sections) PetscValidPointer(sections, 5);
2889   PetscCall(ISGetLocalSize(points, &n));
2890   PetscCall(ISGetIndices(points, &arr0));
2891   PetscCall(DMPlexGetDepth(dm, &depth_));
2892   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2893   PetscCall(PetscCalloc1(depth_, &sections_));
2894   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2895   for (d = depth_ - 1; d >= 0; d--) {
2896     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2897     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2898     for (i = 0; i < n; i++) {
2899       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2900       if (arr[i] >= start && arr[i] < end) {
2901         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2902         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2903       } else {
2904         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2905       }
2906     }
2907     PetscCall(PetscSectionSetUp(sections_[d]));
2908     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2909     PetscCall(PetscMalloc1(newn, &newarr));
2910     for (i = 0; i < n; i++) {
2911       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2912       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2913       if (cn > 1) {
2914         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2915         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2916       } else {
2917         newarr[co] = arr[i];
2918       }
2919     }
2920     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2921     arr = newarr;
2922     n   = newn;
2923   }
2924   PetscCall(ISRestoreIndices(points, &arr0));
2925   *depth = depth_;
2926   if (expandedPoints) *expandedPoints = expandedPoints_;
2927   else {
2928     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2929     PetscCall(PetscFree(expandedPoints_));
2930   }
2931   if (sections) *sections = sections_;
2932   else {
2933     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2934     PetscCall(PetscFree(sections_));
2935   }
2936   PetscFunctionReturn(0);
2937 }
2938 
2939 /*@
2940   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2941 
2942   Not collective
2943 
2944   Input Parameters:
2945 + dm - The DMPlex
2946 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2947 
2948   Output Parameters:
2949 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2950 . expandedPoints - (optional) An array of recursively expanded cones
2951 - sections - (optional) An array of sections which describe mappings from points to their cone points
2952 
2953   Level: advanced
2954 
2955   Notes:
2956   See DMPlexGetConeRecursive() for details.
2957 
2958 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2959 @*/
2960 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[]) {
2961   PetscInt d, depth_;
2962 
2963   PetscFunctionBegin;
2964   PetscCall(DMPlexGetDepth(dm, &depth_));
2965   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2966   if (depth) *depth = 0;
2967   if (expandedPoints) {
2968     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2969     PetscCall(PetscFree(*expandedPoints));
2970   }
2971   if (sections) {
2972     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2973     PetscCall(PetscFree(*sections));
2974   }
2975   PetscFunctionReturn(0);
2976 }
2977 
2978 /*@
2979   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
2980 
2981   Not collective
2982 
2983   Input Parameters:
2984 + mesh - The DMPlex
2985 . p - The point, which must lie in the chart set with DMPlexSetChart()
2986 - cone - An array of points which are on the in-edges for point p
2987 
2988   Output Parameter:
2989 
2990   Note:
2991   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
2992 
2993   Level: beginner
2994 
2995 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
2996 @*/
2997 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[]) {
2998   DM_Plex *mesh = (DM_Plex *)dm->data;
2999   PetscInt pStart, pEnd;
3000   PetscInt dof, off, c;
3001 
3002   PetscFunctionBegin;
3003   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3004   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3005   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3006   if (dof) PetscValidIntPointer(cone, 3);
3007   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3008   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);
3009   for (c = 0; c < dof; ++c) {
3010     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);
3011     mesh->cones[off + c] = cone[c];
3012   }
3013   PetscFunctionReturn(0);
3014 }
3015 
3016 /*@C
3017   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3018 
3019   Not collective
3020 
3021   Input Parameters:
3022 + mesh - The DMPlex
3023 - p - The point, which must lie in the chart set with DMPlexSetChart()
3024 
3025   Output Parameter:
3026 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3027                     integer giving the prescription for cone traversal.
3028 
3029   Level: beginner
3030 
3031   Notes:
3032   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3033   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3034   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3035   with the identity.
3036 
3037   Fortran Notes:
3038   Since it returns an array, this routine is only available in Fortran 90, and you must
3039   include petsc.h90 in your code.
3040   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3041   DMPlexRestoreConeOrientation() is not needed/available in C.
3042 
3043 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3044 @*/
3045 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[]) {
3046   DM_Plex *mesh = (DM_Plex *)dm->data;
3047   PetscInt off;
3048 
3049   PetscFunctionBegin;
3050   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3051   if (PetscDefined(USE_DEBUG)) {
3052     PetscInt dof;
3053     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3054     if (dof) PetscValidPointer(coneOrientation, 3);
3055   }
3056   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3057 
3058   *coneOrientation = &mesh->coneOrientations[off];
3059   PetscFunctionReturn(0);
3060 }
3061 
3062 /*@
3063   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3064 
3065   Not collective
3066 
3067   Input Parameters:
3068 + mesh - The DMPlex
3069 . p - The point, which must lie in the chart set with DMPlexSetChart()
3070 - coneOrientation - An array of orientations
3071   Output Parameter:
3072 
3073   Notes:
3074   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3075 
3076   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3077 
3078   Level: beginner
3079 
3080 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3081 @*/
3082 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[]) {
3083   DM_Plex *mesh = (DM_Plex *)dm->data;
3084   PetscInt pStart, pEnd;
3085   PetscInt dof, off, c;
3086 
3087   PetscFunctionBegin;
3088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3089   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3090   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3091   if (dof) PetscValidIntPointer(coneOrientation, 3);
3092   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3093   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);
3094   for (c = 0; c < dof; ++c) {
3095     PetscInt cdof, o = coneOrientation[c];
3096 
3097     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3098     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);
3099     mesh->coneOrientations[off + c] = o;
3100   }
3101   PetscFunctionReturn(0);
3102 }
3103 
3104 /*@
3105   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3106 
3107   Not collective
3108 
3109   Input Parameters:
3110 + mesh - The DMPlex
3111 . p - The point, which must lie in the chart set with DMPlexSetChart()
3112 . conePos - The local index in the cone where the point should be put
3113 - conePoint - The mesh point to insert
3114 
3115   Level: beginner
3116 
3117 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3118 @*/
3119 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint) {
3120   DM_Plex *mesh = (DM_Plex *)dm->data;
3121   PetscInt pStart, pEnd;
3122   PetscInt dof, off;
3123 
3124   PetscFunctionBegin;
3125   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3126   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3127   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);
3128   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);
3129   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3130   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3131   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);
3132   mesh->cones[off + conePos] = conePoint;
3133   PetscFunctionReturn(0);
3134 }
3135 
3136 /*@
3137   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3138 
3139   Not collective
3140 
3141   Input Parameters:
3142 + mesh - The DMPlex
3143 . p - The point, which must lie in the chart set with DMPlexSetChart()
3144 . conePos - The local index in the cone where the point should be put
3145 - coneOrientation - The point orientation to insert
3146 
3147   Level: beginner
3148 
3149   Notes:
3150   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3151 
3152 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3153 @*/
3154 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation) {
3155   DM_Plex *mesh = (DM_Plex *)dm->data;
3156   PetscInt pStart, pEnd;
3157   PetscInt dof, off;
3158 
3159   PetscFunctionBegin;
3160   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3161   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3162   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);
3163   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3164   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3165   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);
3166   mesh->coneOrientations[off + conePos] = coneOrientation;
3167   PetscFunctionReturn(0);
3168 }
3169 
3170 /*@
3171   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3172 
3173   Not collective
3174 
3175   Input Parameters:
3176 + mesh - The DMPlex
3177 - p - The point, which must lie in the chart set with DMPlexSetChart()
3178 
3179   Output Parameter:
3180 . size - The support size for point p
3181 
3182   Level: beginner
3183 
3184 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3185 @*/
3186 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size) {
3187   DM_Plex *mesh = (DM_Plex *)dm->data;
3188 
3189   PetscFunctionBegin;
3190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3191   PetscValidIntPointer(size, 3);
3192   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3193   PetscFunctionReturn(0);
3194 }
3195 
3196 /*@
3197   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3198 
3199   Not collective
3200 
3201   Input Parameters:
3202 + mesh - The DMPlex
3203 . p - The point, which must lie in the chart set with DMPlexSetChart()
3204 - size - The support size for point p
3205 
3206   Output Parameter:
3207 
3208   Note:
3209   This should be called after DMPlexSetChart().
3210 
3211   Level: beginner
3212 
3213 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3214 @*/
3215 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size) {
3216   DM_Plex *mesh = (DM_Plex *)dm->data;
3217 
3218   PetscFunctionBegin;
3219   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3220   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3221   PetscFunctionReturn(0);
3222 }
3223 
3224 /*@C
3225   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3226 
3227   Not collective
3228 
3229   Input Parameters:
3230 + mesh - The DMPlex
3231 - p - The point, which must lie in the chart set with DMPlexSetChart()
3232 
3233   Output Parameter:
3234 . support - An array of points which are on the out-edges for point p
3235 
3236   Level: beginner
3237 
3238   Fortran Notes:
3239   Since it returns an array, this routine is only available in Fortran 90, and you must
3240   include petsc.h90 in your code.
3241   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3242   DMPlexRestoreSupport() is not needed/available in C.
3243 
3244 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3245 @*/
3246 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[]) {
3247   DM_Plex *mesh = (DM_Plex *)dm->data;
3248   PetscInt off;
3249 
3250   PetscFunctionBegin;
3251   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3252   PetscValidPointer(support, 3);
3253   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3254   *support = &mesh->supports[off];
3255   PetscFunctionReturn(0);
3256 }
3257 
3258 /*@
3259   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3260 
3261   Not collective
3262 
3263   Input Parameters:
3264 + mesh - The DMPlex
3265 . p - The point, which must lie in the chart set with DMPlexSetChart()
3266 - support - An array of points which are on the out-edges for point p
3267 
3268   Output Parameter:
3269 
3270   Note:
3271   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3272 
3273   Level: beginner
3274 
3275 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3276 @*/
3277 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[]) {
3278   DM_Plex *mesh = (DM_Plex *)dm->data;
3279   PetscInt pStart, pEnd;
3280   PetscInt dof, off, c;
3281 
3282   PetscFunctionBegin;
3283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3284   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3285   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3286   if (dof) PetscValidIntPointer(support, 3);
3287   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3288   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);
3289   for (c = 0; c < dof; ++c) {
3290     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);
3291     mesh->supports[off + c] = support[c];
3292   }
3293   PetscFunctionReturn(0);
3294 }
3295 
3296 /*@
3297   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3298 
3299   Not collective
3300 
3301   Input Parameters:
3302 + mesh - The DMPlex
3303 . p - The point, which must lie in the chart set with DMPlexSetChart()
3304 . supportPos - The local index in the cone where the point should be put
3305 - supportPoint - The mesh point to insert
3306 
3307   Level: beginner
3308 
3309 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3310 @*/
3311 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint) {
3312   DM_Plex *mesh = (DM_Plex *)dm->data;
3313   PetscInt pStart, pEnd;
3314   PetscInt dof, off;
3315 
3316   PetscFunctionBegin;
3317   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3318   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3319   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3320   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3321   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);
3322   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);
3323   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);
3324   mesh->supports[off + supportPos] = supportPoint;
3325   PetscFunctionReturn(0);
3326 }
3327 
3328 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3329 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o) {
3330   switch (ct) {
3331   case DM_POLYTOPE_SEGMENT:
3332     if (o == -1) return -2;
3333     break;
3334   case DM_POLYTOPE_TRIANGLE:
3335     if (o == -3) return -1;
3336     if (o == -2) return -3;
3337     if (o == -1) return -2;
3338     break;
3339   case DM_POLYTOPE_QUADRILATERAL:
3340     if (o == -4) return -2;
3341     if (o == -3) return -1;
3342     if (o == -2) return -4;
3343     if (o == -1) return -3;
3344     break;
3345   default: return o;
3346   }
3347   return o;
3348 }
3349 
3350 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3351 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o) {
3352   switch (ct) {
3353   case DM_POLYTOPE_SEGMENT:
3354     if ((o == -2) || (o == 1)) return -1;
3355     if (o == -1) return 0;
3356     break;
3357   case DM_POLYTOPE_TRIANGLE:
3358     if (o == -3) return -2;
3359     if (o == -2) return -1;
3360     if (o == -1) return -3;
3361     break;
3362   case DM_POLYTOPE_QUADRILATERAL:
3363     if (o == -4) return -2;
3364     if (o == -3) return -1;
3365     if (o == -2) return -4;
3366     if (o == -1) return -3;
3367     break;
3368   default: return o;
3369   }
3370   return o;
3371 }
3372 
3373 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3374 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm) {
3375   PetscInt pStart, pEnd, p;
3376 
3377   PetscFunctionBegin;
3378   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3379   for (p = pStart; p < pEnd; ++p) {
3380     const PetscInt *cone, *ornt;
3381     PetscInt        coneSize, c;
3382 
3383     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3384     PetscCall(DMPlexGetCone(dm, p, &cone));
3385     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3386     for (c = 0; c < coneSize; ++c) {
3387       DMPolytopeType ct;
3388       const PetscInt o = ornt[c];
3389 
3390       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3391       switch (ct) {
3392       case DM_POLYTOPE_SEGMENT:
3393         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3394         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3395         break;
3396       case DM_POLYTOPE_TRIANGLE:
3397         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3398         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3399         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3400         break;
3401       case DM_POLYTOPE_QUADRILATERAL:
3402         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3403         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3404         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3405         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3406         break;
3407       default: break;
3408       }
3409     }
3410   }
3411   PetscFunctionReturn(0);
3412 }
3413 
3414 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3415   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3416   PetscInt       *closure;
3417   const PetscInt *tmp = NULL, *tmpO = NULL;
3418   PetscInt        off = 0, tmpSize, t;
3419 
3420   PetscFunctionBeginHot;
3421   if (ornt) {
3422     PetscCall(DMPlexGetCellType(dm, p, &ct));
3423     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3424   }
3425   if (*points) {
3426     closure = *points;
3427   } else {
3428     PetscInt maxConeSize, maxSupportSize;
3429     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3430     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3431   }
3432   if (useCone) {
3433     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3434     PetscCall(DMPlexGetCone(dm, p, &tmp));
3435     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3436   } else {
3437     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3438     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3439   }
3440   if (ct == DM_POLYTOPE_UNKNOWN) {
3441     closure[off++] = p;
3442     closure[off++] = 0;
3443     for (t = 0; t < tmpSize; ++t) {
3444       closure[off++] = tmp[t];
3445       closure[off++] = tmpO ? tmpO[t] : 0;
3446     }
3447   } else {
3448     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3449 
3450     /* We assume that cells with a valid type have faces with a valid type */
3451     closure[off++] = p;
3452     closure[off++] = ornt;
3453     for (t = 0; t < tmpSize; ++t) {
3454       DMPolytopeType ft;
3455 
3456       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3457       closure[off++] = tmp[arr[t]];
3458       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3459     }
3460   }
3461   if (numPoints) *numPoints = tmpSize + 1;
3462   if (points) *points = closure;
3463   PetscFunctionReturn(0);
3464 }
3465 
3466 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3467 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points) {
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) {
3485     pts = *points;
3486   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3487   c        = 0;
3488   pts[c++] = point;
3489   pts[c++] = o;
3490   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3491   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3492   for (cl = 0; cl < clSize * 2; cl += 2) {
3493     pts[c++] = closure[cl];
3494     pts[c++] = closure[cl + 1];
3495   }
3496   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3497   for (cl = 0; cl < clSize * 2; cl += 2) {
3498     pts[c++] = closure[cl];
3499     pts[c++] = closure[cl + 1];
3500   }
3501   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3502   for (d = 2; d < coneSize; ++d) {
3503     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3504     pts[c++] = cone[arr[d * 2 + 0]];
3505     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3506   }
3507   if (dim >= 3) {
3508     for (d = 2; d < coneSize; ++d) {
3509       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3510       const PetscInt *fcone, *fornt;
3511       PetscInt        fconeSize, fc, i;
3512 
3513       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3514       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3515       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3516       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3517       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3518       for (fc = 0; fc < fconeSize; ++fc) {
3519         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3520         const PetscInt co = farr[fc * 2 + 1];
3521 
3522         for (i = 0; i < c; i += 2)
3523           if (pts[i] == cp) break;
3524         if (i == c) {
3525           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3526           pts[c++] = cp;
3527           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3528         }
3529       }
3530     }
3531   }
3532   *numPoints = c / 2;
3533   *points    = pts;
3534   PetscFunctionReturn(0);
3535 }
3536 
3537 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3538   DMPolytopeType ct;
3539   PetscInt      *closure, *fifo;
3540   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3541   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3542   PetscInt       depth, maxSize;
3543 
3544   PetscFunctionBeginHot;
3545   PetscCall(DMPlexGetDepth(dm, &depth));
3546   if (depth == 1) {
3547     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3548     PetscFunctionReturn(0);
3549   }
3550   PetscCall(DMPlexGetCellType(dm, p, &ct));
3551   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3552   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3553     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3554     PetscFunctionReturn(0);
3555   }
3556   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3557   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3558   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3559   maxSize       = PetscMax(coneSeries, supportSeries);
3560   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3561   if (*points) {
3562     closure = *points;
3563   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3564   closure[closureSize++] = p;
3565   closure[closureSize++] = ornt;
3566   fifo[fifoSize++]       = p;
3567   fifo[fifoSize++]       = ornt;
3568   fifo[fifoSize++]       = ct;
3569   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3570   while (fifoSize - fifoStart) {
3571     const PetscInt       q    = fifo[fifoStart++];
3572     const PetscInt       o    = fifo[fifoStart++];
3573     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3574     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3575     const PetscInt      *tmp, *tmpO;
3576     PetscInt             tmpSize, t;
3577 
3578     if (PetscDefined(USE_DEBUG)) {
3579       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3580       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);
3581     }
3582     if (useCone) {
3583       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3584       PetscCall(DMPlexGetCone(dm, q, &tmp));
3585       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3586     } else {
3587       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3588       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3589       tmpO = NULL;
3590     }
3591     for (t = 0; t < tmpSize; ++t) {
3592       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3593       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3594       const PetscInt cp = tmp[ip];
3595       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3596       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3597       PetscInt       c;
3598 
3599       /* Check for duplicate */
3600       for (c = 0; c < closureSize; c += 2) {
3601         if (closure[c] == cp) break;
3602       }
3603       if (c == closureSize) {
3604         closure[closureSize++] = cp;
3605         closure[closureSize++] = co;
3606         fifo[fifoSize++]       = cp;
3607         fifo[fifoSize++]       = co;
3608         fifo[fifoSize++]       = ct;
3609       }
3610     }
3611   }
3612   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3613   if (numPoints) *numPoints = closureSize / 2;
3614   if (points) *points = closure;
3615   PetscFunctionReturn(0);
3616 }
3617 
3618 /*@C
3619   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3620 
3621   Not collective
3622 
3623   Input Parameters:
3624 + dm      - The DMPlex
3625 . p       - The mesh point
3626 - useCone - PETSC_TRUE for the closure, otherwise return the star
3627 
3628   Input/Output Parameter:
3629 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3630            if NULL on input, internal storage will be returned, otherwise the provided array is used
3631 
3632   Output Parameter:
3633 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3634 
3635   Note:
3636   If using internal storage (points is NULL on input), each call overwrites the last output.
3637 
3638   Fortran Note:
3639   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3640 
3641   Level: beginner
3642 
3643 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3644 @*/
3645 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3646   PetscFunctionBeginHot;
3647   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3648   if (numPoints) PetscValidIntPointer(numPoints, 4);
3649   if (points) PetscValidPointer(points, 5);
3650   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3651   PetscFunctionReturn(0);
3652 }
3653 
3654 /*@C
3655   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3656 
3657   Not collective
3658 
3659   Input Parameters:
3660 + dm        - The DMPlex
3661 . p         - The mesh point
3662 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3663 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3664 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3665 
3666   Note:
3667   If not using internal storage (points is not NULL on input), this call is unnecessary
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   PetscFunctionBeginHot;
3675   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3676   if (numPoints) *numPoints = 0;
3677   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3678   PetscFunctionReturn(0);
3679 }
3680 
3681 /*@
3682   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3683 
3684   Not collective
3685 
3686   Input Parameter:
3687 . mesh - The DMPlex
3688 
3689   Output Parameters:
3690 + maxConeSize - The maximum number of in-edges
3691 - maxSupportSize - The maximum number of out-edges
3692 
3693   Level: beginner
3694 
3695 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3696 @*/
3697 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize) {
3698   DM_Plex *mesh = (DM_Plex *)dm->data;
3699 
3700   PetscFunctionBegin;
3701   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3702   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3703   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3704   PetscFunctionReturn(0);
3705 }
3706 
3707 PetscErrorCode DMSetUp_Plex(DM dm) {
3708   DM_Plex *mesh = (DM_Plex *)dm->data;
3709   PetscInt size, maxSupportSize;
3710 
3711   PetscFunctionBegin;
3712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3713   PetscCall(PetscSectionSetUp(mesh->coneSection));
3714   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3715   PetscCall(PetscMalloc1(size, &mesh->cones));
3716   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3717   PetscCall(PetscLogObjectMemory((PetscObject)dm, size * 2 * sizeof(PetscInt)));
3718   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3719   if (maxSupportSize) {
3720     PetscCall(PetscSectionSetUp(mesh->supportSection));
3721     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3722     PetscCall(PetscMalloc1(size, &mesh->supports));
3723     PetscCall(PetscLogObjectMemory((PetscObject)dm, size * sizeof(PetscInt)));
3724   }
3725   PetscFunctionReturn(0);
3726 }
3727 
3728 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm) {
3729   PetscFunctionBegin;
3730   if (subdm) PetscCall(DMClone(dm, subdm));
3731   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3732   if (subdm) (*subdm)->useNatural = dm->useNatural;
3733   if (dm->useNatural && dm->sfMigration) {
3734     PetscSF sfNatural;
3735 
3736     (*subdm)->sfMigration = dm->sfMigration;
3737     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3738     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3739     (*subdm)->sfNatural = sfNatural;
3740   }
3741   PetscFunctionReturn(0);
3742 }
3743 
3744 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm) {
3745   PetscInt i = 0;
3746 
3747   PetscFunctionBegin;
3748   PetscCall(DMClone(dms[0], superdm));
3749   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3750   (*superdm)->useNatural = PETSC_FALSE;
3751   for (i = 0; i < len; i++) {
3752     if (dms[i]->useNatural && dms[i]->sfMigration) {
3753       PetscSF sfNatural;
3754 
3755       (*superdm)->sfMigration = dms[i]->sfMigration;
3756       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3757       (*superdm)->useNatural = PETSC_TRUE;
3758       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3759       (*superdm)->sfNatural = sfNatural;
3760       break;
3761     }
3762   }
3763   PetscFunctionReturn(0);
3764 }
3765 
3766 /*@
3767   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3768 
3769   Not collective
3770 
3771   Input Parameter:
3772 . mesh - The DMPlex
3773 
3774   Output Parameter:
3775 
3776   Note:
3777   This should be called after all calls to DMPlexSetCone()
3778 
3779   Level: beginner
3780 
3781 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3782 @*/
3783 PetscErrorCode DMPlexSymmetrize(DM dm) {
3784   DM_Plex  *mesh = (DM_Plex *)dm->data;
3785   PetscInt *offsets;
3786   PetscInt  supportSize;
3787   PetscInt  pStart, pEnd, p;
3788 
3789   PetscFunctionBegin;
3790   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3791   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3792   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3793   /* Calculate support sizes */
3794   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3795   for (p = pStart; p < pEnd; ++p) {
3796     PetscInt dof, off, c;
3797 
3798     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3799     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3800     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3801   }
3802   PetscCall(PetscSectionSetUp(mesh->supportSection));
3803   /* Calculate supports */
3804   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3805   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3806   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3807   for (p = pStart; p < pEnd; ++p) {
3808     PetscInt dof, off, c;
3809 
3810     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3811     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3812     for (c = off; c < off + dof; ++c) {
3813       const PetscInt q = mesh->cones[c];
3814       PetscInt       offS;
3815 
3816       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3817 
3818       mesh->supports[offS + offsets[q]] = p;
3819       ++offsets[q];
3820     }
3821   }
3822   PetscCall(PetscFree(offsets));
3823   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
3824   PetscFunctionReturn(0);
3825 }
3826 
3827 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd) {
3828   IS stratumIS;
3829 
3830   PetscFunctionBegin;
3831   if (pStart >= pEnd) PetscFunctionReturn(0);
3832   if (PetscDefined(USE_DEBUG)) {
3833     PetscInt  qStart, qEnd, numLevels, level;
3834     PetscBool overlap = PETSC_FALSE;
3835     PetscCall(DMLabelGetNumValues(label, &numLevels));
3836     for (level = 0; level < numLevels; level++) {
3837       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3838       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
3839         overlap = PETSC_TRUE;
3840         break;
3841       }
3842     }
3843     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);
3844   }
3845   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
3846   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3847   PetscCall(ISDestroy(&stratumIS));
3848   PetscFunctionReturn(0);
3849 }
3850 
3851 /*@
3852   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3853   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3854   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3855   the DAG.
3856 
3857   Collective on dm
3858 
3859   Input Parameter:
3860 . mesh - The DMPlex
3861 
3862   Output Parameter:
3863 
3864   Notes:
3865   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3866   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3867   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3868   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3869   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3870 
3871   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3872   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3873   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
3874   to interpolate only that one (e0), so that
3875 $  cone(c0) = {e0, v2}
3876 $  cone(e0) = {v0, v1}
3877   If DMPlexStratify() is run on this mesh, it will give depths
3878 $  depth 0 = {v0, v1, v2}
3879 $  depth 1 = {e0, c0}
3880   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3881 
3882   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3883 
3884   Level: beginner
3885 
3886 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3887 @*/
3888 PetscErrorCode DMPlexStratify(DM dm) {
3889   DM_Plex *mesh = (DM_Plex *)dm->data;
3890   DMLabel  label;
3891   PetscInt pStart, pEnd, p;
3892   PetscInt numRoots = 0, numLeaves = 0;
3893 
3894   PetscFunctionBegin;
3895   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3896   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
3897 
3898   /* Create depth label */
3899   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3900   PetscCall(DMCreateLabel(dm, "depth"));
3901   PetscCall(DMPlexGetDepthLabel(dm, &label));
3902 
3903   {
3904     /* Initialize roots and count leaves */
3905     PetscInt sMin = PETSC_MAX_INT;
3906     PetscInt sMax = PETSC_MIN_INT;
3907     PetscInt coneSize, supportSize;
3908 
3909     for (p = pStart; p < pEnd; ++p) {
3910       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3911       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3912       if (!coneSize && supportSize) {
3913         sMin = PetscMin(p, sMin);
3914         sMax = PetscMax(p, sMax);
3915         ++numRoots;
3916       } else if (!supportSize && coneSize) {
3917         ++numLeaves;
3918       } else if (!supportSize && !coneSize) {
3919         /* Isolated points */
3920         sMin = PetscMin(p, sMin);
3921         sMax = PetscMax(p, sMax);
3922       }
3923     }
3924     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
3925   }
3926 
3927   if (numRoots + numLeaves == (pEnd - pStart)) {
3928     PetscInt sMin = PETSC_MAX_INT;
3929     PetscInt sMax = PETSC_MIN_INT;
3930     PetscInt coneSize, supportSize;
3931 
3932     for (p = pStart; p < pEnd; ++p) {
3933       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3934       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3935       if (!supportSize && coneSize) {
3936         sMin = PetscMin(p, sMin);
3937         sMax = PetscMax(p, sMax);
3938       }
3939     }
3940     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
3941   } else {
3942     PetscInt level = 0;
3943     PetscInt qStart, qEnd, q;
3944 
3945     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3946     while (qEnd > qStart) {
3947       PetscInt sMin = PETSC_MAX_INT;
3948       PetscInt sMax = PETSC_MIN_INT;
3949 
3950       for (q = qStart; q < qEnd; ++q) {
3951         const PetscInt *support;
3952         PetscInt        supportSize, s;
3953 
3954         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
3955         PetscCall(DMPlexGetSupport(dm, q, &support));
3956         for (s = 0; s < supportSize; ++s) {
3957           sMin = PetscMin(support[s], sMin);
3958           sMax = PetscMax(support[s], sMax);
3959         }
3960       }
3961       PetscCall(DMLabelGetNumValues(label, &level));
3962       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
3963       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3964     }
3965   }
3966   { /* just in case there is an empty process */
3967     PetscInt numValues, maxValues = 0, v;
3968 
3969     PetscCall(DMLabelGetNumValues(label, &numValues));
3970     PetscCallMPI(MPI_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
3971     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
3972   }
3973   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
3974   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
3975   PetscFunctionReturn(0);
3976 }
3977 
3978 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt) {
3979   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
3980   PetscInt       dim, depth, pheight, coneSize;
3981 
3982   PetscFunctionBeginHot;
3983   PetscCall(DMGetDimension(dm, &dim));
3984   PetscCall(DMPlexGetDepth(dm, &depth));
3985   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3986   pheight = depth - pdepth;
3987   if (depth <= 1) {
3988     switch (pdepth) {
3989     case 0: ct = DM_POLYTOPE_POINT; break;
3990     case 1:
3991       switch (coneSize) {
3992       case 2: ct = DM_POLYTOPE_SEGMENT; break;
3993       case 3: ct = DM_POLYTOPE_TRIANGLE; break;
3994       case 4:
3995         switch (dim) {
3996         case 2: ct = DM_POLYTOPE_QUADRILATERAL; break;
3997         case 3: ct = DM_POLYTOPE_TETRAHEDRON; break;
3998         default: break;
3999         }
4000         break;
4001       case 5: ct = DM_POLYTOPE_PYRAMID; break;
4002       case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR; break;
4003       case 8: ct = DM_POLYTOPE_HEXAHEDRON; break;
4004       default: break;
4005       }
4006     }
4007   } else {
4008     if (pdepth == 0) {
4009       ct = DM_POLYTOPE_POINT;
4010     } else if (pheight == 0) {
4011       switch (dim) {
4012       case 1:
4013         switch (coneSize) {
4014         case 2: ct = DM_POLYTOPE_SEGMENT; break;
4015         default: break;
4016         }
4017         break;
4018       case 2:
4019         switch (coneSize) {
4020         case 3: ct = DM_POLYTOPE_TRIANGLE; break;
4021         case 4: ct = DM_POLYTOPE_QUADRILATERAL; break;
4022         default: break;
4023         }
4024         break;
4025       case 3:
4026         switch (coneSize) {
4027         case 4: ct = DM_POLYTOPE_TETRAHEDRON; break;
4028         case 5: {
4029           const PetscInt *cone;
4030           PetscInt        faceConeSize;
4031 
4032           PetscCall(DMPlexGetCone(dm, p, &cone));
4033           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4034           switch (faceConeSize) {
4035           case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR; break;
4036           case 4: ct = DM_POLYTOPE_PYRAMID; break;
4037           }
4038         } break;
4039         case 6: ct = DM_POLYTOPE_HEXAHEDRON; break;
4040         default: break;
4041         }
4042         break;
4043       default: break;
4044       }
4045     } else if (pheight > 0) {
4046       switch (coneSize) {
4047       case 2: ct = DM_POLYTOPE_SEGMENT; break;
4048       case 3: ct = DM_POLYTOPE_TRIANGLE; break;
4049       case 4: ct = DM_POLYTOPE_QUADRILATERAL; break;
4050       default: break;
4051       }
4052     }
4053   }
4054   *pt = ct;
4055   PetscFunctionReturn(0);
4056 }
4057 
4058 /*@
4059   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4060 
4061   Collective on dm
4062 
4063   Input Parameter:
4064 . mesh - The DMPlex
4065 
4066   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4067 
4068   Level: developer
4069 
4070   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4071   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4072   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4073 
4074 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4075 @*/
4076 PetscErrorCode DMPlexComputeCellTypes(DM dm) {
4077   DM_Plex *mesh;
4078   DMLabel  ctLabel;
4079   PetscInt pStart, pEnd, p;
4080 
4081   PetscFunctionBegin;
4082   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4083   mesh = (DM_Plex *)dm->data;
4084   PetscCall(DMCreateLabel(dm, "celltype"));
4085   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4086   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4087   for (p = pStart; p < pEnd; ++p) {
4088     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4089     PetscInt       pdepth;
4090 
4091     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4092     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4093     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4094     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4095   }
4096   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4097   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4098   PetscFunctionReturn(0);
4099 }
4100 
4101 /*@C
4102   DMPlexGetJoin - Get an array for the join of the set of points
4103 
4104   Not Collective
4105 
4106   Input Parameters:
4107 + dm - The DMPlex object
4108 . numPoints - The number of input points for the join
4109 - points - The input points
4110 
4111   Output Parameters:
4112 + numCoveredPoints - The number of points in the join
4113 - coveredPoints - The points in the join
4114 
4115   Level: intermediate
4116 
4117   Note: Currently, this is restricted to a single level join
4118 
4119   Fortran Notes:
4120   Since it returns an array, this routine is only available in Fortran 90, and you must
4121   include petsc.h90 in your code.
4122 
4123   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4124 
4125 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4126 @*/
4127 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4128   DM_Plex  *mesh = (DM_Plex *)dm->data;
4129   PetscInt *join[2];
4130   PetscInt  joinSize, i = 0;
4131   PetscInt  dof, off, p, c, m;
4132   PetscInt  maxSupportSize;
4133 
4134   PetscFunctionBegin;
4135   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4136   PetscValidIntPointer(points, 3);
4137   PetscValidIntPointer(numCoveredPoints, 4);
4138   PetscValidPointer(coveredPoints, 5);
4139   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4140   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4141   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4142   /* Copy in support of first point */
4143   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4144   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4145   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4146   /* Check each successive support */
4147   for (p = 1; p < numPoints; ++p) {
4148     PetscInt newJoinSize = 0;
4149 
4150     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4151     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4152     for (c = 0; c < dof; ++c) {
4153       const PetscInt point = mesh->supports[off + c];
4154 
4155       for (m = 0; m < joinSize; ++m) {
4156         if (point == join[i][m]) {
4157           join[1 - i][newJoinSize++] = point;
4158           break;
4159         }
4160       }
4161     }
4162     joinSize = newJoinSize;
4163     i        = 1 - i;
4164   }
4165   *numCoveredPoints = joinSize;
4166   *coveredPoints    = join[i];
4167   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4168   PetscFunctionReturn(0);
4169 }
4170 
4171 /*@C
4172   DMPlexRestoreJoin - Restore an array for the join of the set of points
4173 
4174   Not Collective
4175 
4176   Input Parameters:
4177 + dm - The DMPlex object
4178 . numPoints - The number of input points for the join
4179 - points - The input points
4180 
4181   Output Parameters:
4182 + numCoveredPoints - The number of points in the join
4183 - coveredPoints - The points in the join
4184 
4185   Fortran Notes:
4186   Since it returns an array, this routine is only available in Fortran 90, and you must
4187   include petsc.h90 in your code.
4188 
4189   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4190 
4191   Level: intermediate
4192 
4193 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4194 @*/
4195 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4196   PetscFunctionBegin;
4197   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4198   if (points) PetscValidIntPointer(points, 3);
4199   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4200   PetscValidPointer(coveredPoints, 5);
4201   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4202   if (numCoveredPoints) *numCoveredPoints = 0;
4203   PetscFunctionReturn(0);
4204 }
4205 
4206 /*@C
4207   DMPlexGetFullJoin - Get an array for the join of the set of points
4208 
4209   Not Collective
4210 
4211   Input Parameters:
4212 + dm - The DMPlex object
4213 . numPoints - The number of input points for the join
4214 - points - The input points
4215 
4216   Output Parameters:
4217 + numCoveredPoints - The number of points in the join
4218 - coveredPoints - The points in the join
4219 
4220   Fortran Notes:
4221   Since it returns an array, this routine is only available in Fortran 90, and you must
4222   include petsc.h90 in your code.
4223 
4224   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4225 
4226   Level: intermediate
4227 
4228 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4229 @*/
4230 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4231   PetscInt *offsets, **closures;
4232   PetscInt *join[2];
4233   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4234   PetscInt  p, d, c, m, ms;
4235 
4236   PetscFunctionBegin;
4237   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4238   PetscValidIntPointer(points, 3);
4239   PetscValidIntPointer(numCoveredPoints, 4);
4240   PetscValidPointer(coveredPoints, 5);
4241 
4242   PetscCall(DMPlexGetDepth(dm, &depth));
4243   PetscCall(PetscCalloc1(numPoints, &closures));
4244   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4245   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4246   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4247   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4248   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4249 
4250   for (p = 0; p < numPoints; ++p) {
4251     PetscInt closureSize;
4252 
4253     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4254 
4255     offsets[p * (depth + 2) + 0] = 0;
4256     for (d = 0; d < depth + 1; ++d) {
4257       PetscInt pStart, pEnd, i;
4258 
4259       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4260       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4261         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4262           offsets[p * (depth + 2) + d + 1] = i;
4263           break;
4264         }
4265       }
4266       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4267     }
4268     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);
4269   }
4270   for (d = 0; d < depth + 1; ++d) {
4271     PetscInt dof;
4272 
4273     /* Copy in support of first point */
4274     dof = offsets[d + 1] - offsets[d];
4275     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4276     /* Check each successive cone */
4277     for (p = 1; p < numPoints && joinSize; ++p) {
4278       PetscInt newJoinSize = 0;
4279 
4280       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4281       for (c = 0; c < dof; ++c) {
4282         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4283 
4284         for (m = 0; m < joinSize; ++m) {
4285           if (point == join[i][m]) {
4286             join[1 - i][newJoinSize++] = point;
4287             break;
4288           }
4289         }
4290       }
4291       joinSize = newJoinSize;
4292       i        = 1 - i;
4293     }
4294     if (joinSize) break;
4295   }
4296   *numCoveredPoints = joinSize;
4297   *coveredPoints    = join[i];
4298   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4299   PetscCall(PetscFree(closures));
4300   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4301   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4302   PetscFunctionReturn(0);
4303 }
4304 
4305 /*@C
4306   DMPlexGetMeet - Get an array for the meet of the set of points
4307 
4308   Not Collective
4309 
4310   Input Parameters:
4311 + dm - The DMPlex object
4312 . numPoints - The number of input points for the meet
4313 - points - The input points
4314 
4315   Output Parameters:
4316 + numCoveredPoints - The number of points in the meet
4317 - coveredPoints - The points in the meet
4318 
4319   Level: intermediate
4320 
4321   Note: Currently, this is restricted to a single level meet
4322 
4323   Fortran Notes:
4324   Since it returns an array, this routine is only available in Fortran 90, and you must
4325   include petsc.h90 in your code.
4326 
4327   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4328 
4329 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4330 @*/
4331 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints) {
4332   DM_Plex  *mesh = (DM_Plex *)dm->data;
4333   PetscInt *meet[2];
4334   PetscInt  meetSize, i = 0;
4335   PetscInt  dof, off, p, c, m;
4336   PetscInt  maxConeSize;
4337 
4338   PetscFunctionBegin;
4339   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4340   PetscValidIntPointer(points, 3);
4341   PetscValidIntPointer(numCoveringPoints, 4);
4342   PetscValidPointer(coveringPoints, 5);
4343   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4344   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4345   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4346   /* Copy in cone of first point */
4347   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4348   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4349   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4350   /* Check each successive cone */
4351   for (p = 1; p < numPoints; ++p) {
4352     PetscInt newMeetSize = 0;
4353 
4354     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4355     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4356     for (c = 0; c < dof; ++c) {
4357       const PetscInt point = mesh->cones[off + c];
4358 
4359       for (m = 0; m < meetSize; ++m) {
4360         if (point == meet[i][m]) {
4361           meet[1 - i][newMeetSize++] = point;
4362           break;
4363         }
4364       }
4365     }
4366     meetSize = newMeetSize;
4367     i        = 1 - i;
4368   }
4369   *numCoveringPoints = meetSize;
4370   *coveringPoints    = meet[i];
4371   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4372   PetscFunctionReturn(0);
4373 }
4374 
4375 /*@C
4376   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4377 
4378   Not Collective
4379 
4380   Input Parameters:
4381 + dm - The DMPlex object
4382 . numPoints - The number of input points for the meet
4383 - points - The input points
4384 
4385   Output Parameters:
4386 + numCoveredPoints - The number of points in the meet
4387 - coveredPoints - The points in the meet
4388 
4389   Level: intermediate
4390 
4391   Fortran Notes:
4392   Since it returns an array, this routine is only available in Fortran 90, and you must
4393   include petsc.h90 in your code.
4394 
4395   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4396 
4397 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4398 @*/
4399 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4400   PetscFunctionBegin;
4401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4402   if (points) PetscValidIntPointer(points, 3);
4403   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4404   PetscValidPointer(coveredPoints, 5);
4405   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4406   if (numCoveredPoints) *numCoveredPoints = 0;
4407   PetscFunctionReturn(0);
4408 }
4409 
4410 /*@C
4411   DMPlexGetFullMeet - Get an array for the meet of the set of points
4412 
4413   Not Collective
4414 
4415   Input Parameters:
4416 + dm - The DMPlex object
4417 . numPoints - The number of input points for the meet
4418 - points - The input points
4419 
4420   Output Parameters:
4421 + numCoveredPoints - The number of points in the meet
4422 - coveredPoints - The points in the meet
4423 
4424   Level: intermediate
4425 
4426   Fortran Notes:
4427   Since it returns an array, this routine is only available in Fortran 90, and you must
4428   include petsc.h90 in your code.
4429 
4430   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4431 
4432 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4433 @*/
4434 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4435   PetscInt *offsets, **closures;
4436   PetscInt *meet[2];
4437   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4438   PetscInt  p, h, c, m, mc;
4439 
4440   PetscFunctionBegin;
4441   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4442   PetscValidIntPointer(points, 3);
4443   PetscValidIntPointer(numCoveredPoints, 4);
4444   PetscValidPointer(coveredPoints, 5);
4445 
4446   PetscCall(DMPlexGetDepth(dm, &height));
4447   PetscCall(PetscMalloc1(numPoints, &closures));
4448   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4449   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4450   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4451   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4452   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4453 
4454   for (p = 0; p < numPoints; ++p) {
4455     PetscInt closureSize;
4456 
4457     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4458 
4459     offsets[p * (height + 2) + 0] = 0;
4460     for (h = 0; h < height + 1; ++h) {
4461       PetscInt pStart, pEnd, i;
4462 
4463       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4464       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4465         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4466           offsets[p * (height + 2) + h + 1] = i;
4467           break;
4468         }
4469       }
4470       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4471     }
4472     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);
4473   }
4474   for (h = 0; h < height + 1; ++h) {
4475     PetscInt dof;
4476 
4477     /* Copy in cone of first point */
4478     dof = offsets[h + 1] - offsets[h];
4479     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4480     /* Check each successive cone */
4481     for (p = 1; p < numPoints && meetSize; ++p) {
4482       PetscInt newMeetSize = 0;
4483 
4484       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4485       for (c = 0; c < dof; ++c) {
4486         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4487 
4488         for (m = 0; m < meetSize; ++m) {
4489           if (point == meet[i][m]) {
4490             meet[1 - i][newMeetSize++] = point;
4491             break;
4492           }
4493         }
4494       }
4495       meetSize = newMeetSize;
4496       i        = 1 - i;
4497     }
4498     if (meetSize) break;
4499   }
4500   *numCoveredPoints = meetSize;
4501   *coveredPoints    = meet[i];
4502   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4503   PetscCall(PetscFree(closures));
4504   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4505   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4506   PetscFunctionReturn(0);
4507 }
4508 
4509 /*@C
4510   DMPlexEqual - Determine if two DMs have the same topology
4511 
4512   Not Collective
4513 
4514   Input Parameters:
4515 + dmA - A DMPlex object
4516 - dmB - A DMPlex object
4517 
4518   Output Parameters:
4519 . equal - PETSC_TRUE if the topologies are identical
4520 
4521   Level: intermediate
4522 
4523   Notes:
4524   We are not solving graph isomorphism, so we do not permutation.
4525 
4526 .seealso: `DMPlexGetCone()`
4527 @*/
4528 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal) {
4529   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4530 
4531   PetscFunctionBegin;
4532   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4533   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4534   PetscValidBoolPointer(equal, 3);
4535 
4536   *equal = PETSC_FALSE;
4537   PetscCall(DMPlexGetDepth(dmA, &depth));
4538   PetscCall(DMPlexGetDepth(dmB, &depthB));
4539   if (depth != depthB) PetscFunctionReturn(0);
4540   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4541   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4542   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4543   for (p = pStart; p < pEnd; ++p) {
4544     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4545     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4546 
4547     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4548     PetscCall(DMPlexGetCone(dmA, p, &cone));
4549     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4550     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4551     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4552     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4553     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4554     for (c = 0; c < coneSize; ++c) {
4555       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4556       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4557     }
4558     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4559     PetscCall(DMPlexGetSupport(dmA, p, &support));
4560     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4561     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4562     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4563     for (s = 0; s < supportSize; ++s) {
4564       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4565     }
4566   }
4567   *equal = PETSC_TRUE;
4568   PetscFunctionReturn(0);
4569 }
4570 
4571 /*@C
4572   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4573 
4574   Not Collective
4575 
4576   Input Parameters:
4577 + dm         - The DMPlex
4578 . cellDim    - The cell dimension
4579 - numCorners - The number of vertices on a cell
4580 
4581   Output Parameters:
4582 . numFaceVertices - The number of vertices on a face
4583 
4584   Level: developer
4585 
4586   Notes:
4587   Of course this can only work for a restricted set of symmetric shapes
4588 
4589 .seealso: `DMPlexGetCone()`
4590 @*/
4591 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices) {
4592   MPI_Comm comm;
4593 
4594   PetscFunctionBegin;
4595   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4596   PetscValidIntPointer(numFaceVertices, 4);
4597   switch (cellDim) {
4598   case 0: *numFaceVertices = 0; break;
4599   case 1: *numFaceVertices = 1; break;
4600   case 2:
4601     switch (numCorners) {
4602     case 3:                 /* triangle */
4603       *numFaceVertices = 2; /* Edge has 2 vertices */
4604       break;
4605     case 4:                 /* quadrilateral */
4606       *numFaceVertices = 2; /* Edge has 2 vertices */
4607       break;
4608     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4609       *numFaceVertices = 3; /* Edge has 3 vertices */
4610       break;
4611     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4612       *numFaceVertices = 3; /* Edge has 3 vertices */
4613       break;
4614     default: SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4615     }
4616     break;
4617   case 3:
4618     switch (numCorners) {
4619     case 4:                 /* tetradehdron */
4620       *numFaceVertices = 3; /* Face has 3 vertices */
4621       break;
4622     case 6:                 /* tet cohesive cells */
4623       *numFaceVertices = 4; /* Face has 4 vertices */
4624       break;
4625     case 8:                 /* hexahedron */
4626       *numFaceVertices = 4; /* Face has 4 vertices */
4627       break;
4628     case 9:                 /* tet cohesive Lagrange cells */
4629       *numFaceVertices = 6; /* Face has 6 vertices */
4630       break;
4631     case 10:                /* quadratic tetrahedron */
4632       *numFaceVertices = 6; /* Face has 6 vertices */
4633       break;
4634     case 12:                /* hex cohesive Lagrange cells */
4635       *numFaceVertices = 6; /* Face has 6 vertices */
4636       break;
4637     case 18:                /* quadratic tet cohesive Lagrange cells */
4638       *numFaceVertices = 6; /* Face has 6 vertices */
4639       break;
4640     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4641       *numFaceVertices = 9; /* Face has 9 vertices */
4642       break;
4643     default: SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4644     }
4645     break;
4646   default: SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4647   }
4648   PetscFunctionReturn(0);
4649 }
4650 
4651 /*@
4652   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4653 
4654   Not Collective
4655 
4656   Input Parameter:
4657 . dm    - The DMPlex object
4658 
4659   Output Parameter:
4660 . depthLabel - The DMLabel recording point depth
4661 
4662   Level: developer
4663 
4664 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4665 @*/
4666 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel) {
4667   PetscFunctionBegin;
4668   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4669   PetscValidPointer(depthLabel, 2);
4670   *depthLabel = dm->depthLabel;
4671   PetscFunctionReturn(0);
4672 }
4673 
4674 /*@
4675   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4676 
4677   Not Collective
4678 
4679   Input Parameter:
4680 . dm    - The DMPlex object
4681 
4682   Output Parameter:
4683 . depth - The number of strata (breadth first levels) in the DAG
4684 
4685   Level: developer
4686 
4687   Notes:
4688   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4689   The point depth is described more in detail in DMPlexGetDepthStratum().
4690   An empty mesh gives -1.
4691 
4692 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4693 @*/
4694 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth) {
4695   DMLabel  label;
4696   PetscInt d = 0;
4697 
4698   PetscFunctionBegin;
4699   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4700   PetscValidIntPointer(depth, 2);
4701   PetscCall(DMPlexGetDepthLabel(dm, &label));
4702   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4703   *depth = d - 1;
4704   PetscFunctionReturn(0);
4705 }
4706 
4707 /*@
4708   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4709 
4710   Not Collective
4711 
4712   Input Parameters:
4713 + dm    - The DMPlex object
4714 - depth - The requested depth
4715 
4716   Output Parameters:
4717 + start - The first point at this depth
4718 - end   - One beyond the last point at this depth
4719 
4720   Notes:
4721   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4722   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4723   higher dimension, e.g., "edges".
4724 
4725   Level: developer
4726 
4727 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4728 @*/
4729 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end) {
4730   DMLabel  label;
4731   PetscInt pStart, pEnd;
4732 
4733   PetscFunctionBegin;
4734   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4735   if (start) {
4736     PetscValidIntPointer(start, 3);
4737     *start = 0;
4738   }
4739   if (end) {
4740     PetscValidIntPointer(end, 4);
4741     *end = 0;
4742   }
4743   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4744   if (pStart == pEnd) PetscFunctionReturn(0);
4745   if (depth < 0) {
4746     if (start) *start = pStart;
4747     if (end) *end = pEnd;
4748     PetscFunctionReturn(0);
4749   }
4750   PetscCall(DMPlexGetDepthLabel(dm, &label));
4751   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4752   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4753   PetscFunctionReturn(0);
4754 }
4755 
4756 /*@
4757   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4758 
4759   Not Collective
4760 
4761   Input Parameters:
4762 + dm     - The DMPlex object
4763 - height - The requested height
4764 
4765   Output Parameters:
4766 + start - The first point at this height
4767 - end   - One beyond the last point at this height
4768 
4769   Notes:
4770   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4771   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4772   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4773 
4774   Level: developer
4775 
4776 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4777 @*/
4778 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end) {
4779   DMLabel  label;
4780   PetscInt depth, pStart, pEnd;
4781 
4782   PetscFunctionBegin;
4783   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4784   if (start) {
4785     PetscValidIntPointer(start, 3);
4786     *start = 0;
4787   }
4788   if (end) {
4789     PetscValidIntPointer(end, 4);
4790     *end = 0;
4791   }
4792   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4793   if (pStart == pEnd) PetscFunctionReturn(0);
4794   if (height < 0) {
4795     if (start) *start = pStart;
4796     if (end) *end = pEnd;
4797     PetscFunctionReturn(0);
4798   }
4799   PetscCall(DMPlexGetDepthLabel(dm, &label));
4800   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4801   PetscCall(DMLabelGetNumValues(label, &depth));
4802   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
4803   PetscFunctionReturn(0);
4804 }
4805 
4806 /*@
4807   DMPlexGetPointDepth - Get the depth of a given point
4808 
4809   Not Collective
4810 
4811   Input Parameters:
4812 + dm    - The DMPlex object
4813 - point - The point
4814 
4815   Output Parameter:
4816 . depth - The depth of the point
4817 
4818   Level: intermediate
4819 
4820 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4821 @*/
4822 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth) {
4823   PetscFunctionBegin;
4824   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4825   PetscValidIntPointer(depth, 3);
4826   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4827   PetscFunctionReturn(0);
4828 }
4829 
4830 /*@
4831   DMPlexGetPointHeight - Get the height of a given point
4832 
4833   Not Collective
4834 
4835   Input Parameters:
4836 + dm    - The DMPlex object
4837 - point - The point
4838 
4839   Output Parameter:
4840 . height - The height of the point
4841 
4842   Level: intermediate
4843 
4844 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4845 @*/
4846 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height) {
4847   PetscInt n, pDepth;
4848 
4849   PetscFunctionBegin;
4850   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4851   PetscValidIntPointer(height, 3);
4852   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4853   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4854   *height = n - 1 - pDepth; /* DAG depth is n-1 */
4855   PetscFunctionReturn(0);
4856 }
4857 
4858 /*@
4859   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4860 
4861   Not Collective
4862 
4863   Input Parameter:
4864 . dm - The DMPlex object
4865 
4866   Output Parameter:
4867 . celltypeLabel - The DMLabel recording cell polytope type
4868 
4869   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4870   DMCreateLabel(dm, "celltype") beforehand.
4871 
4872   Level: developer
4873 
4874 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4875 @*/
4876 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel) {
4877   PetscFunctionBegin;
4878   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4879   PetscValidPointer(celltypeLabel, 2);
4880   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4881   *celltypeLabel = dm->celltypeLabel;
4882   PetscFunctionReturn(0);
4883 }
4884 
4885 /*@
4886   DMPlexGetCellType - Get the polytope type of a given cell
4887 
4888   Not Collective
4889 
4890   Input Parameters:
4891 + dm   - The DMPlex object
4892 - cell - The cell
4893 
4894   Output Parameter:
4895 . celltype - The polytope type of the cell
4896 
4897   Level: intermediate
4898 
4899 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4900 @*/
4901 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype) {
4902   DMLabel  label;
4903   PetscInt ct;
4904 
4905   PetscFunctionBegin;
4906   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4907   PetscValidPointer(celltype, 3);
4908   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4909   PetscCall(DMLabelGetValue(label, cell, &ct));
4910   PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4911   *celltype = (DMPolytopeType)ct;
4912   PetscFunctionReturn(0);
4913 }
4914 
4915 /*@
4916   DMPlexSetCellType - Set the polytope type of a given cell
4917 
4918   Not Collective
4919 
4920   Input Parameters:
4921 + dm   - The DMPlex object
4922 . cell - The cell
4923 - celltype - The polytope type of the cell
4924 
4925   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
4926   is executed. This function will override the computed type. However, if automatic classification will not succeed
4927   and a user wants to manually specify all types, the classification must be disabled by calling
4928   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
4929 
4930   Level: advanced
4931 
4932 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
4933 @*/
4934 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype) {
4935   DMLabel label;
4936 
4937   PetscFunctionBegin;
4938   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4939   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4940   PetscCall(DMLabelSetValue(label, cell, celltype));
4941   PetscFunctionReturn(0);
4942 }
4943 
4944 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm) {
4945   PetscSection section, s;
4946   Mat          m;
4947   PetscInt     maxHeight;
4948 
4949   PetscFunctionBegin;
4950   PetscCall(DMClone(dm, cdm));
4951   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
4952   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
4953   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
4954   PetscCall(DMSetLocalSection(*cdm, section));
4955   PetscCall(PetscSectionDestroy(&section));
4956   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
4957   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
4958   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
4959   PetscCall(PetscSectionDestroy(&s));
4960   PetscCall(MatDestroy(&m));
4961 
4962   PetscCall(DMSetNumFields(*cdm, 1));
4963   PetscCall(DMCreateDS(*cdm));
4964   PetscFunctionReturn(0);
4965 }
4966 
4967 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field) {
4968   Vec coordsLocal, cellCoordsLocal;
4969   DM  coordsDM, cellCoordsDM;
4970 
4971   PetscFunctionBegin;
4972   *field = NULL;
4973   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
4974   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
4975   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
4976   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
4977   if (coordsLocal && coordsDM) {
4978     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
4979     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
4980   }
4981   PetscFunctionReturn(0);
4982 }
4983 
4984 /*@C
4985   DMPlexGetConeSection - Return a section which describes the layout of cone data
4986 
4987   Not Collective
4988 
4989   Input Parameters:
4990 . dm        - The DMPlex object
4991 
4992   Output Parameter:
4993 . section - The PetscSection object
4994 
4995   Level: developer
4996 
4997 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
4998 @*/
4999 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section) {
5000   DM_Plex *mesh = (DM_Plex *)dm->data;
5001 
5002   PetscFunctionBegin;
5003   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5004   if (section) *section = mesh->coneSection;
5005   PetscFunctionReturn(0);
5006 }
5007 
5008 /*@C
5009   DMPlexGetSupportSection - Return a section which describes the layout of support data
5010 
5011   Not Collective
5012 
5013   Input Parameters:
5014 . dm        - The DMPlex object
5015 
5016   Output Parameter:
5017 . section - The PetscSection object
5018 
5019   Level: developer
5020 
5021 .seealso: `DMPlexGetConeSection()`
5022 @*/
5023 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section) {
5024   DM_Plex *mesh = (DM_Plex *)dm->data;
5025 
5026   PetscFunctionBegin;
5027   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5028   if (section) *section = mesh->supportSection;
5029   PetscFunctionReturn(0);
5030 }
5031 
5032 /*@C
5033   DMPlexGetCones - Return cone data
5034 
5035   Not Collective
5036 
5037   Input Parameters:
5038 . dm        - The DMPlex object
5039 
5040   Output Parameter:
5041 . cones - The cone for each point
5042 
5043   Level: developer
5044 
5045 .seealso: `DMPlexGetConeSection()`
5046 @*/
5047 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[]) {
5048   DM_Plex *mesh = (DM_Plex *)dm->data;
5049 
5050   PetscFunctionBegin;
5051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5052   if (cones) *cones = mesh->cones;
5053   PetscFunctionReturn(0);
5054 }
5055 
5056 /*@C
5057   DMPlexGetConeOrientations - Return cone orientation data
5058 
5059   Not Collective
5060 
5061   Input Parameters:
5062 . dm        - The DMPlex object
5063 
5064   Output Parameter:
5065 . coneOrientations - The array of cone orientations for all points
5066 
5067   Level: developer
5068 
5069   Notes:
5070   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5071 
5072   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5073 
5074 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5075 @*/
5076 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[]) {
5077   DM_Plex *mesh = (DM_Plex *)dm->data;
5078 
5079   PetscFunctionBegin;
5080   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5081   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5082   PetscFunctionReturn(0);
5083 }
5084 
5085 /******************************** FEM Support **********************************/
5086 
5087 /*
5088  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5089  representing a line in the section.
5090 */
5091 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k) {
5092   PetscFunctionBeginHot;
5093   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5094   if (line < 0) {
5095     *k  = 0;
5096     *Nc = 0;
5097   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5098     *k = 1;
5099   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5100     /* An order k SEM disc has k-1 dofs on an edge */
5101     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5102     *k = *k / *Nc + 1;
5103   }
5104   PetscFunctionReturn(0);
5105 }
5106 
5107 /*@
5108 
5109   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5110   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5111   section provided (or the section of the DM).
5112 
5113   Input Parameters:
5114 + dm      - The DM
5115 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5116 - section - The PetscSection to reorder, or NULL for the default section
5117 
5118   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5119   degree of the basis.
5120 
5121   Example:
5122   A typical interpolated single-quad mesh might order points as
5123 .vb
5124   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5125 
5126   v4 -- e6 -- v3
5127   |           |
5128   e7    c0    e8
5129   |           |
5130   v1 -- e5 -- v2
5131 .ve
5132 
5133   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5134   dofs in the order of points, e.g.,
5135 .vb
5136     c0 -> [0,1,2,3]
5137     v1 -> [4]
5138     ...
5139     e5 -> [8, 9]
5140 .ve
5141 
5142   which corresponds to the dofs
5143 .vb
5144     6   10  11  7
5145     13  2   3   15
5146     12  0   1   14
5147     4   8   9   5
5148 .ve
5149 
5150   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5151 .vb
5152   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5153 .ve
5154 
5155   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5156 .vb
5157    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5158 .ve
5159 
5160   Level: developer
5161 
5162 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5163 @*/
5164 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section) {
5165   DMLabel   label;
5166   PetscInt  dim, depth = -1, eStart = -1, Nf;
5167   PetscBool vertexchart;
5168 
5169   PetscFunctionBegin;
5170   PetscCall(DMGetDimension(dm, &dim));
5171   if (dim < 1) PetscFunctionReturn(0);
5172   if (point < 0) {
5173     PetscInt sStart, sEnd;
5174 
5175     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5176     point = sEnd - sStart ? sStart : point;
5177   }
5178   PetscCall(DMPlexGetDepthLabel(dm, &label));
5179   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5180   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5181   if (depth == 1) {
5182     eStart = point;
5183   } else if (depth == dim) {
5184     const PetscInt *cone;
5185 
5186     PetscCall(DMPlexGetCone(dm, point, &cone));
5187     if (dim == 2) eStart = cone[0];
5188     else if (dim == 3) {
5189       const PetscInt *cone2;
5190       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5191       eStart = cone2[0];
5192     } 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);
5193   } 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);
5194   { /* Determine whether the chart covers all points or just vertices. */
5195     PetscInt pStart, pEnd, cStart, cEnd;
5196     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5197     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5198     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5199     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5200     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5201   }
5202   PetscCall(PetscSectionGetNumFields(section, &Nf));
5203   for (PetscInt d = 1; d <= dim; d++) {
5204     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5205     PetscInt *perm;
5206 
5207     for (f = 0; f < Nf; ++f) {
5208       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5209       size += PetscPowInt(k + 1, d) * Nc;
5210     }
5211     PetscCall(PetscMalloc1(size, &perm));
5212     for (f = 0; f < Nf; ++f) {
5213       switch (d) {
5214       case 1:
5215         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5216         /*
5217          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5218          We want              [ vtx0; edge of length k-1; vtx1 ]
5219          */
5220         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5221         for (i = 0; i < k - 1; i++)
5222           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5223         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5224         foffset = offset;
5225         break;
5226       case 2:
5227         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5228         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5229         /* The SEM order is
5230 
5231          v_lb, {e_b}, v_rb,
5232          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5233          v_lt, reverse {e_t}, v_rt
5234          */
5235         {
5236           const PetscInt of   = 0;
5237           const PetscInt oeb  = of + PetscSqr(k - 1);
5238           const PetscInt oer  = oeb + (k - 1);
5239           const PetscInt oet  = oer + (k - 1);
5240           const PetscInt oel  = oet + (k - 1);
5241           const PetscInt ovlb = oel + (k - 1);
5242           const PetscInt ovrb = ovlb + 1;
5243           const PetscInt ovrt = ovrb + 1;
5244           const PetscInt ovlt = ovrt + 1;
5245           PetscInt       o;
5246 
5247           /* bottom */
5248           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5249           for (o = oeb; o < oer; ++o)
5250             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5251           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5252           /* middle */
5253           for (i = 0; i < k - 1; ++i) {
5254             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5255             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5256               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5257             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5258           }
5259           /* top */
5260           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5261           for (o = oel - 1; o >= oet; --o)
5262             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5263           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5264           foffset = offset;
5265         }
5266         break;
5267       case 3:
5268         /* The original hex closure is
5269 
5270          {c,
5271          f_b, f_t, f_f, f_b, f_r, f_l,
5272          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5273          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5274          */
5275         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5276         /* The SEM order is
5277          Bottom Slice
5278          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5279          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5280          v_blb, {e_bb}, v_brb,
5281 
5282          Middle Slice (j)
5283          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5284          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5285          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5286 
5287          Top Slice
5288          v_tlf, {e_tf}, v_trf,
5289          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5290          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5291          */
5292         {
5293           const PetscInt oc    = 0;
5294           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5295           const PetscInt oft   = ofb + PetscSqr(k - 1);
5296           const PetscInt off   = oft + PetscSqr(k - 1);
5297           const PetscInt ofk   = off + PetscSqr(k - 1);
5298           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5299           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5300           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5301           const PetscInt oebb  = oebl + (k - 1);
5302           const PetscInt oebr  = oebb + (k - 1);
5303           const PetscInt oebf  = oebr + (k - 1);
5304           const PetscInt oetf  = oebf + (k - 1);
5305           const PetscInt oetr  = oetf + (k - 1);
5306           const PetscInt oetb  = oetr + (k - 1);
5307           const PetscInt oetl  = oetb + (k - 1);
5308           const PetscInt oerf  = oetl + (k - 1);
5309           const PetscInt oelf  = oerf + (k - 1);
5310           const PetscInt oelb  = oelf + (k - 1);
5311           const PetscInt oerb  = oelb + (k - 1);
5312           const PetscInt ovblf = oerb + (k - 1);
5313           const PetscInt ovblb = ovblf + 1;
5314           const PetscInt ovbrb = ovblb + 1;
5315           const PetscInt ovbrf = ovbrb + 1;
5316           const PetscInt ovtlf = ovbrf + 1;
5317           const PetscInt ovtrf = ovtlf + 1;
5318           const PetscInt ovtrb = ovtrf + 1;
5319           const PetscInt ovtlb = ovtrb + 1;
5320           PetscInt       o, n;
5321 
5322           /* Bottom Slice */
5323           /*   bottom */
5324           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5325           for (o = oetf - 1; o >= oebf; --o)
5326             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5327           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5328           /*   middle */
5329           for (i = 0; i < k - 1; ++i) {
5330             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5331             for (n = 0; n < k - 1; ++n) {
5332               o = ofb + n * (k - 1) + i;
5333               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5334             }
5335             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5336           }
5337           /*   top */
5338           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5339           for (o = oebb; o < oebr; ++o)
5340             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5341           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5342 
5343           /* Middle Slice */
5344           for (j = 0; j < k - 1; ++j) {
5345             /*   bottom */
5346             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5347             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5348               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5349             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5350             /*   middle */
5351             for (i = 0; i < k - 1; ++i) {
5352               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5353               for (n = 0; n < k - 1; ++n)
5354                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5355               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5356             }
5357             /*   top */
5358             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5359             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5360               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5361             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5362           }
5363 
5364           /* Top Slice */
5365           /*   bottom */
5366           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5367           for (o = oetf; o < oetr; ++o)
5368             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5369           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5370           /*   middle */
5371           for (i = 0; i < k - 1; ++i) {
5372             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5373             for (n = 0; n < k - 1; ++n)
5374               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5375             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5376           }
5377           /*   top */
5378           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5379           for (o = oetl - 1; o >= oetb; --o)
5380             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5381           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5382 
5383           foffset = offset;
5384         }
5385         break;
5386       default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5387       }
5388     }
5389     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5390     /* Check permutation */
5391     {
5392       PetscInt *check;
5393 
5394       PetscCall(PetscMalloc1(size, &check));
5395       for (i = 0; i < size; ++i) {
5396         check[i] = -1;
5397         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5398       }
5399       for (i = 0; i < size; ++i) check[perm[i]] = i;
5400       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5401       PetscCall(PetscFree(check));
5402     }
5403     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5404     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5405       PetscInt *loc_perm;
5406       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5407       for (PetscInt i = 0; i < size; i++) {
5408         loc_perm[i]        = perm[i];
5409         loc_perm[size + i] = size + perm[i];
5410       }
5411       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5412     }
5413   }
5414   PetscFunctionReturn(0);
5415 }
5416 
5417 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace) {
5418   PetscDS  prob;
5419   PetscInt depth, Nf, h;
5420   DMLabel  label;
5421 
5422   PetscFunctionBeginHot;
5423   PetscCall(DMGetDS(dm, &prob));
5424   Nf      = prob->Nf;
5425   label   = dm->depthLabel;
5426   *dspace = NULL;
5427   if (field < Nf) {
5428     PetscObject disc = prob->disc[field];
5429 
5430     if (disc->classid == PETSCFE_CLASSID) {
5431       PetscDualSpace dsp;
5432 
5433       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5434       PetscCall(DMLabelGetNumValues(label, &depth));
5435       PetscCall(DMLabelGetValue(label, point, &h));
5436       h = depth - 1 - h;
5437       if (h) {
5438         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5439       } else {
5440         *dspace = dsp;
5441       }
5442     }
5443   }
5444   PetscFunctionReturn(0);
5445 }
5446 
5447 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[]) {
5448   PetscScalar       *array;
5449   const PetscScalar *vArray;
5450   const PetscInt    *cone, *coneO;
5451   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5452 
5453   PetscFunctionBeginHot;
5454   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5455   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5456   PetscCall(DMPlexGetCone(dm, point, &cone));
5457   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5458   if (!values || !*values) {
5459     if ((point >= pStart) && (point < pEnd)) {
5460       PetscInt dof;
5461 
5462       PetscCall(PetscSectionGetDof(section, point, &dof));
5463       size += dof;
5464     }
5465     for (p = 0; p < numPoints; ++p) {
5466       const PetscInt cp = cone[p];
5467       PetscInt       dof;
5468 
5469       if ((cp < pStart) || (cp >= pEnd)) continue;
5470       PetscCall(PetscSectionGetDof(section, cp, &dof));
5471       size += dof;
5472     }
5473     if (!values) {
5474       if (csize) *csize = size;
5475       PetscFunctionReturn(0);
5476     }
5477     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5478   } else {
5479     array = *values;
5480   }
5481   size = 0;
5482   PetscCall(VecGetArrayRead(v, &vArray));
5483   if ((point >= pStart) && (point < pEnd)) {
5484     PetscInt           dof, off, d;
5485     const PetscScalar *varr;
5486 
5487     PetscCall(PetscSectionGetDof(section, point, &dof));
5488     PetscCall(PetscSectionGetOffset(section, point, &off));
5489     varr = &vArray[off];
5490     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5491     size += dof;
5492   }
5493   for (p = 0; p < numPoints; ++p) {
5494     const PetscInt     cp = cone[p];
5495     PetscInt           o  = coneO[p];
5496     PetscInt           dof, off, d;
5497     const PetscScalar *varr;
5498 
5499     if ((cp < pStart) || (cp >= pEnd)) continue;
5500     PetscCall(PetscSectionGetDof(section, cp, &dof));
5501     PetscCall(PetscSectionGetOffset(section, cp, &off));
5502     varr = &vArray[off];
5503     if (o >= 0) {
5504       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5505     } else {
5506       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5507     }
5508     size += dof;
5509   }
5510   PetscCall(VecRestoreArrayRead(v, &vArray));
5511   if (!*values) {
5512     if (csize) *csize = size;
5513     *values = array;
5514   } else {
5515     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5516     *csize = size;
5517   }
5518   PetscFunctionReturn(0);
5519 }
5520 
5521 /* Compress out points not in the section */
5522 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[]) {
5523   const PetscInt np = *numPoints;
5524   PetscInt       pStart, pEnd, p, q;
5525 
5526   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5527   for (p = 0, q = 0; p < np; ++p) {
5528     const PetscInt r = points[p * 2];
5529     if ((r >= pStart) && (r < pEnd)) {
5530       points[q * 2]     = r;
5531       points[q * 2 + 1] = points[p * 2 + 1];
5532       ++q;
5533     }
5534   }
5535   *numPoints = q;
5536   return 0;
5537 }
5538 
5539 /* Compressed closure does not apply closure permutation */
5540 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp) {
5541   const PetscInt *cla = NULL;
5542   PetscInt        np, *pts = NULL;
5543 
5544   PetscFunctionBeginHot;
5545   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5546   if (*clPoints) {
5547     PetscInt dof, off;
5548 
5549     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5550     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5551     PetscCall(ISGetIndices(*clPoints, &cla));
5552     np  = dof / 2;
5553     pts = (PetscInt *)&cla[off];
5554   } else {
5555     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5556     PetscCall(CompressPoints_Private(section, &np, pts));
5557   }
5558   *numPoints = np;
5559   *points    = pts;
5560   *clp       = cla;
5561   PetscFunctionReturn(0);
5562 }
5563 
5564 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp) {
5565   PetscFunctionBeginHot;
5566   if (!*clPoints) {
5567     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5568   } else {
5569     PetscCall(ISRestoreIndices(*clPoints, clp));
5570   }
5571   *numPoints = 0;
5572   *points    = NULL;
5573   *clSec     = NULL;
5574   *clPoints  = NULL;
5575   *clp       = NULL;
5576   PetscFunctionReturn(0);
5577 }
5578 
5579 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[]) {
5580   PetscInt            offset = 0, p;
5581   const PetscInt    **perms  = NULL;
5582   const PetscScalar **flips  = NULL;
5583 
5584   PetscFunctionBeginHot;
5585   *size = 0;
5586   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5587   for (p = 0; p < numPoints; p++) {
5588     const PetscInt     point = points[2 * p];
5589     const PetscInt    *perm  = perms ? perms[p] : NULL;
5590     const PetscScalar *flip  = flips ? flips[p] : NULL;
5591     PetscInt           dof, off, d;
5592     const PetscScalar *varr;
5593 
5594     PetscCall(PetscSectionGetDof(section, point, &dof));
5595     PetscCall(PetscSectionGetOffset(section, point, &off));
5596     varr = &vArray[off];
5597     if (clperm) {
5598       if (perm) {
5599         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5600       } else {
5601         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5602       }
5603       if (flip) {
5604         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5605       }
5606     } else {
5607       if (perm) {
5608         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5609       } else {
5610         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5611       }
5612       if (flip) {
5613         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5614       }
5615     }
5616     offset += dof;
5617   }
5618   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5619   *size = offset;
5620   PetscFunctionReturn(0);
5621 }
5622 
5623 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[]) {
5624   PetscInt offset = 0, f;
5625 
5626   PetscFunctionBeginHot;
5627   *size = 0;
5628   for (f = 0; f < numFields; ++f) {
5629     PetscInt            p;
5630     const PetscInt    **perms = NULL;
5631     const PetscScalar **flips = NULL;
5632 
5633     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5634     for (p = 0; p < numPoints; p++) {
5635       const PetscInt     point = points[2 * p];
5636       PetscInt           fdof, foff, b;
5637       const PetscScalar *varr;
5638       const PetscInt    *perm = perms ? perms[p] : NULL;
5639       const PetscScalar *flip = flips ? flips[p] : NULL;
5640 
5641       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5642       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5643       varr = &vArray[foff];
5644       if (clperm) {
5645         if (perm) {
5646           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5647         } else {
5648           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5649         }
5650         if (flip) {
5651           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5652         }
5653       } else {
5654         if (perm) {
5655           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5656         } else {
5657           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5658         }
5659         if (flip) {
5660           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5661         }
5662       }
5663       offset += fdof;
5664     }
5665     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5666   }
5667   *size = offset;
5668   PetscFunctionReturn(0);
5669 }
5670 
5671 /*@C
5672   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5673 
5674   Not collective
5675 
5676   Input Parameters:
5677 + dm - The DM
5678 . section - The section describing the layout in v, or NULL to use the default section
5679 . v - The local vector
5680 - point - The point in the DM
5681 
5682   Input/Output Parameters:
5683 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5684 - values - An array to use for the values, or NULL to have it allocated automatically;
5685            if the user provided NULL, it is a borrowed array and should not be freed
5686 
5687 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5688 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5689 $ assembly function, and a user may already have allocated storage for this operation.
5690 $
5691 $ A typical use could be
5692 $
5693 $  values = NULL;
5694 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5695 $  for (cl = 0; cl < clSize; ++cl) {
5696 $    <Compute on closure>
5697 $  }
5698 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5699 $
5700 $ or
5701 $
5702 $  PetscMalloc1(clMaxSize, &values);
5703 $  for (p = pStart; p < pEnd; ++p) {
5704 $    clSize = clMaxSize;
5705 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5706 $    for (cl = 0; cl < clSize; ++cl) {
5707 $      <Compute on closure>
5708 $    }
5709 $  }
5710 $  PetscFree(values);
5711 
5712   Fortran Notes:
5713   Since it returns an array, this routine is only available in Fortran 90, and you must
5714   include petsc.h90 in your code.
5715 
5716   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5717 
5718   Level: intermediate
5719 
5720 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5721 @*/
5722 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[]) {
5723   PetscSection    clSection;
5724   IS              clPoints;
5725   PetscInt       *points = NULL;
5726   const PetscInt *clp, *perm;
5727   PetscInt        depth, numFields, numPoints, asize;
5728 
5729   PetscFunctionBeginHot;
5730   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5731   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5732   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5733   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5734   PetscCall(DMPlexGetDepth(dm, &depth));
5735   PetscCall(PetscSectionGetNumFields(section, &numFields));
5736   if (depth == 1 && numFields < 2) {
5737     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5738     PetscFunctionReturn(0);
5739   }
5740   /* Get points */
5741   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5742   /* Get sizes */
5743   asize = 0;
5744   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
5745     PetscInt dof;
5746     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5747     asize += dof;
5748   }
5749   if (values) {
5750     const PetscScalar *vArray;
5751     PetscInt           size;
5752 
5753     if (*values) {
5754       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);
5755     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5756     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
5757     PetscCall(VecGetArrayRead(v, &vArray));
5758     /* Get values */
5759     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5760     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5761     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5762     /* Cleanup array */
5763     PetscCall(VecRestoreArrayRead(v, &vArray));
5764   }
5765   if (csize) *csize = asize;
5766   /* Cleanup points */
5767   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5768   PetscFunctionReturn(0);
5769 }
5770 
5771 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[]) {
5772   DMLabel            depthLabel;
5773   PetscSection       clSection;
5774   IS                 clPoints;
5775   PetscScalar       *array;
5776   const PetscScalar *vArray;
5777   PetscInt          *points = NULL;
5778   const PetscInt    *clp, *perm = NULL;
5779   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5780 
5781   PetscFunctionBeginHot;
5782   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5783   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5784   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5785   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5786   PetscCall(DMPlexGetDepth(dm, &mdepth));
5787   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5788   PetscCall(PetscSectionGetNumFields(section, &numFields));
5789   if (mdepth == 1 && numFields < 2) {
5790     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5791     PetscFunctionReturn(0);
5792   }
5793   /* Get points */
5794   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5795   for (clsize = 0, p = 0; p < Np; p++) {
5796     PetscInt dof;
5797     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
5798     clsize += dof;
5799   }
5800   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
5801   /* Filter points */
5802   for (p = 0; p < numPoints * 2; p += 2) {
5803     PetscInt dep;
5804 
5805     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5806     if (dep != depth) continue;
5807     points[Np * 2 + 0] = points[p];
5808     points[Np * 2 + 1] = points[p + 1];
5809     ++Np;
5810   }
5811   /* Get array */
5812   if (!values || !*values) {
5813     PetscInt asize = 0, dof;
5814 
5815     for (p = 0; p < Np * 2; p += 2) {
5816       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5817       asize += dof;
5818     }
5819     if (!values) {
5820       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5821       if (csize) *csize = asize;
5822       PetscFunctionReturn(0);
5823     }
5824     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5825   } else {
5826     array = *values;
5827   }
5828   PetscCall(VecGetArrayRead(v, &vArray));
5829   /* Get values */
5830   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5831   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5832   /* Cleanup points */
5833   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5834   /* Cleanup array */
5835   PetscCall(VecRestoreArrayRead(v, &vArray));
5836   if (!*values) {
5837     if (csize) *csize = size;
5838     *values = array;
5839   } else {
5840     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5841     *csize = size;
5842   }
5843   PetscFunctionReturn(0);
5844 }
5845 
5846 /*@C
5847   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5848 
5849   Not collective
5850 
5851   Input Parameters:
5852 + dm - The DM
5853 . section - The section describing the layout in v, or NULL to use the default section
5854 . v - The local vector
5855 . point - The point in the DM
5856 . csize - The number of values in the closure, or NULL
5857 - values - The array of values, which is a borrowed array and should not be freed
5858 
5859   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5860 
5861   Fortran Notes:
5862   Since it returns an array, this routine is only available in Fortran 90, and you must
5863   include petsc.h90 in your code.
5864 
5865   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5866 
5867   Level: intermediate
5868 
5869 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5870 @*/
5871 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[]) {
5872   PetscInt size = 0;
5873 
5874   PetscFunctionBegin;
5875   /* Should work without recalculating size */
5876   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
5877   *values = NULL;
5878   PetscFunctionReturn(0);
5879 }
5880 
5881 static inline void add(PetscScalar *x, PetscScalar y) {
5882   *x += y;
5883 }
5884 static inline void insert(PetscScalar *x, PetscScalar y) {
5885   *x = y;
5886 }
5887 
5888 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[]) {
5889   PetscInt        cdof;  /* The number of constraints on this point */
5890   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5891   PetscScalar    *a;
5892   PetscInt        off, cind = 0, k;
5893 
5894   PetscFunctionBegin;
5895   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5896   PetscCall(PetscSectionGetOffset(section, point, &off));
5897   a = &array[off];
5898   if (!cdof || setBC) {
5899     if (clperm) {
5900       if (perm) {
5901         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
5902       } else {
5903         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
5904       }
5905     } else {
5906       if (perm) {
5907         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
5908       } else {
5909         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
5910       }
5911     }
5912   } else {
5913     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5914     if (clperm) {
5915       if (perm) {
5916         for (k = 0; k < dof; ++k) {
5917           if ((cind < cdof) && (k == cdofs[cind])) {
5918             ++cind;
5919             continue;
5920           }
5921           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
5922         }
5923       } else {
5924         for (k = 0; k < dof; ++k) {
5925           if ((cind < cdof) && (k == cdofs[cind])) {
5926             ++cind;
5927             continue;
5928           }
5929           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
5930         }
5931       }
5932     } else {
5933       if (perm) {
5934         for (k = 0; k < dof; ++k) {
5935           if ((cind < cdof) && (k == cdofs[cind])) {
5936             ++cind;
5937             continue;
5938           }
5939           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
5940         }
5941       } else {
5942         for (k = 0; k < dof; ++k) {
5943           if ((cind < cdof) && (k == cdofs[cind])) {
5944             ++cind;
5945             continue;
5946           }
5947           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
5948         }
5949       }
5950     }
5951   }
5952   PetscFunctionReturn(0);
5953 }
5954 
5955 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[]) {
5956   PetscInt        cdof;  /* The number of constraints on this point */
5957   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5958   PetscScalar    *a;
5959   PetscInt        off, cind = 0, k;
5960 
5961   PetscFunctionBegin;
5962   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5963   PetscCall(PetscSectionGetOffset(section, point, &off));
5964   a = &array[off];
5965   if (cdof) {
5966     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5967     if (clperm) {
5968       if (perm) {
5969         for (k = 0; k < dof; ++k) {
5970           if ((cind < cdof) && (k == cdofs[cind])) {
5971             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
5972             cind++;
5973           }
5974         }
5975       } else {
5976         for (k = 0; k < dof; ++k) {
5977           if ((cind < cdof) && (k == cdofs[cind])) {
5978             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
5979             cind++;
5980           }
5981         }
5982       }
5983     } else {
5984       if (perm) {
5985         for (k = 0; k < dof; ++k) {
5986           if ((cind < cdof) && (k == cdofs[cind])) {
5987             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
5988             cind++;
5989           }
5990         }
5991       } else {
5992         for (k = 0; k < dof; ++k) {
5993           if ((cind < cdof) && (k == cdofs[cind])) {
5994             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
5995             cind++;
5996           }
5997         }
5998       }
5999     }
6000   }
6001   PetscFunctionReturn(0);
6002 }
6003 
6004 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[]) {
6005   PetscScalar    *a;
6006   PetscInt        fdof, foff, fcdof, foffset = *offset;
6007   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6008   PetscInt        cind = 0, b;
6009 
6010   PetscFunctionBegin;
6011   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6012   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6013   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6014   a = &array[foff];
6015   if (!fcdof || setBC) {
6016     if (clperm) {
6017       if (perm) {
6018         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6019       } else {
6020         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6021       }
6022     } else {
6023       if (perm) {
6024         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6025       } else {
6026         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6027       }
6028     }
6029   } else {
6030     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6031     if (clperm) {
6032       if (perm) {
6033         for (b = 0; b < fdof; b++) {
6034           if ((cind < fcdof) && (b == fcdofs[cind])) {
6035             ++cind;
6036             continue;
6037           }
6038           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6039         }
6040       } else {
6041         for (b = 0; b < fdof; b++) {
6042           if ((cind < fcdof) && (b == fcdofs[cind])) {
6043             ++cind;
6044             continue;
6045           }
6046           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6047         }
6048       }
6049     } else {
6050       if (perm) {
6051         for (b = 0; b < fdof; b++) {
6052           if ((cind < fcdof) && (b == fcdofs[cind])) {
6053             ++cind;
6054             continue;
6055           }
6056           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6057         }
6058       } else {
6059         for (b = 0; b < fdof; b++) {
6060           if ((cind < fcdof) && (b == fcdofs[cind])) {
6061             ++cind;
6062             continue;
6063           }
6064           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6065         }
6066       }
6067     }
6068   }
6069   *offset += fdof;
6070   PetscFunctionReturn(0);
6071 }
6072 
6073 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[]) {
6074   PetscScalar    *a;
6075   PetscInt        fdof, foff, fcdof, foffset = *offset;
6076   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6077   PetscInt        Nc, cind = 0, ncind = 0, b;
6078   PetscBool       ncSet, fcSet;
6079 
6080   PetscFunctionBegin;
6081   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6082   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6083   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6084   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6085   a = &array[foff];
6086   if (fcdof) {
6087     /* We just override fcdof and fcdofs with Ncc and comps */
6088     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6089     if (clperm) {
6090       if (perm) {
6091         if (comps) {
6092           for (b = 0; b < fdof; b++) {
6093             ncSet = fcSet = PETSC_FALSE;
6094             if (b % Nc == comps[ncind]) {
6095               ncind = (ncind + 1) % Ncc;
6096               ncSet = PETSC_TRUE;
6097             }
6098             if ((cind < fcdof) && (b == fcdofs[cind])) {
6099               ++cind;
6100               fcSet = PETSC_TRUE;
6101             }
6102             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6103           }
6104         } else {
6105           for (b = 0; b < fdof; b++) {
6106             if ((cind < fcdof) && (b == fcdofs[cind])) {
6107               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6108               ++cind;
6109             }
6110           }
6111         }
6112       } else {
6113         if (comps) {
6114           for (b = 0; b < fdof; b++) {
6115             ncSet = fcSet = PETSC_FALSE;
6116             if (b % Nc == comps[ncind]) {
6117               ncind = (ncind + 1) % Ncc;
6118               ncSet = PETSC_TRUE;
6119             }
6120             if ((cind < fcdof) && (b == fcdofs[cind])) {
6121               ++cind;
6122               fcSet = PETSC_TRUE;
6123             }
6124             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6125           }
6126         } else {
6127           for (b = 0; b < fdof; b++) {
6128             if ((cind < fcdof) && (b == fcdofs[cind])) {
6129               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6130               ++cind;
6131             }
6132           }
6133         }
6134       }
6135     } else {
6136       if (perm) {
6137         if (comps) {
6138           for (b = 0; b < fdof; b++) {
6139             ncSet = fcSet = PETSC_FALSE;
6140             if (b % Nc == comps[ncind]) {
6141               ncind = (ncind + 1) % Ncc;
6142               ncSet = PETSC_TRUE;
6143             }
6144             if ((cind < fcdof) && (b == fcdofs[cind])) {
6145               ++cind;
6146               fcSet = PETSC_TRUE;
6147             }
6148             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6149           }
6150         } else {
6151           for (b = 0; b < fdof; b++) {
6152             if ((cind < fcdof) && (b == fcdofs[cind])) {
6153               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6154               ++cind;
6155             }
6156           }
6157         }
6158       } else {
6159         if (comps) {
6160           for (b = 0; b < fdof; b++) {
6161             ncSet = fcSet = PETSC_FALSE;
6162             if (b % Nc == comps[ncind]) {
6163               ncind = (ncind + 1) % Ncc;
6164               ncSet = PETSC_TRUE;
6165             }
6166             if ((cind < fcdof) && (b == fcdofs[cind])) {
6167               ++cind;
6168               fcSet = PETSC_TRUE;
6169             }
6170             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6171           }
6172         } else {
6173           for (b = 0; b < fdof; b++) {
6174             if ((cind < fcdof) && (b == fcdofs[cind])) {
6175               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6176               ++cind;
6177             }
6178           }
6179         }
6180       }
6181     }
6182   }
6183   *offset += fdof;
6184   PetscFunctionReturn(0);
6185 }
6186 
6187 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode) {
6188   PetscScalar    *array;
6189   const PetscInt *cone, *coneO;
6190   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6191 
6192   PetscFunctionBeginHot;
6193   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6194   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6195   PetscCall(DMPlexGetCone(dm, point, &cone));
6196   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6197   PetscCall(VecGetArray(v, &array));
6198   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6199     const PetscInt cp = !p ? point : cone[p - 1];
6200     const PetscInt o  = !p ? 0 : coneO[p - 1];
6201 
6202     if ((cp < pStart) || (cp >= pEnd)) {
6203       dof = 0;
6204       continue;
6205     }
6206     PetscCall(PetscSectionGetDof(section, cp, &dof));
6207     /* ADD_VALUES */
6208     {
6209       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6210       PetscScalar    *a;
6211       PetscInt        cdof, coff, cind = 0, k;
6212 
6213       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6214       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6215       a = &array[coff];
6216       if (!cdof) {
6217         if (o >= 0) {
6218           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6219         } else {
6220           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6221         }
6222       } else {
6223         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6224         if (o >= 0) {
6225           for (k = 0; k < dof; ++k) {
6226             if ((cind < cdof) && (k == cdofs[cind])) {
6227               ++cind;
6228               continue;
6229             }
6230             a[k] += values[off + k];
6231           }
6232         } else {
6233           for (k = 0; k < dof; ++k) {
6234             if ((cind < cdof) && (k == cdofs[cind])) {
6235               ++cind;
6236               continue;
6237             }
6238             a[k] += values[off + dof - k - 1];
6239           }
6240         }
6241       }
6242     }
6243   }
6244   PetscCall(VecRestoreArray(v, &array));
6245   PetscFunctionReturn(0);
6246 }
6247 
6248 /*@C
6249   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6250 
6251   Not collective
6252 
6253   Input Parameters:
6254 + dm - The DM
6255 . section - The section describing the layout in v, or NULL to use the default section
6256 . v - The local vector
6257 . point - The point in the DM
6258 . values - The array of values
6259 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6260          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6261 
6262   Fortran Notes:
6263   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6264 
6265   Level: intermediate
6266 
6267 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6268 @*/
6269 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode) {
6270   PetscSection    clSection;
6271   IS              clPoints;
6272   PetscScalar    *array;
6273   PetscInt       *points = NULL;
6274   const PetscInt *clp, *clperm = NULL;
6275   PetscInt        depth, numFields, numPoints, p, clsize;
6276 
6277   PetscFunctionBeginHot;
6278   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6279   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6280   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6281   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6282   PetscCall(DMPlexGetDepth(dm, &depth));
6283   PetscCall(PetscSectionGetNumFields(section, &numFields));
6284   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6285     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6286     PetscFunctionReturn(0);
6287   }
6288   /* Get points */
6289   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6290   for (clsize = 0, p = 0; p < numPoints; p++) {
6291     PetscInt dof;
6292     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6293     clsize += dof;
6294   }
6295   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6296   /* Get array */
6297   PetscCall(VecGetArray(v, &array));
6298   /* Get values */
6299   if (numFields > 0) {
6300     PetscInt offset = 0, f;
6301     for (f = 0; f < numFields; ++f) {
6302       const PetscInt    **perms = NULL;
6303       const PetscScalar **flips = NULL;
6304 
6305       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6306       switch (mode) {
6307       case INSERT_VALUES:
6308         for (p = 0; p < numPoints; p++) {
6309           const PetscInt     point = points[2 * p];
6310           const PetscInt    *perm  = perms ? perms[p] : NULL;
6311           const PetscScalar *flip  = flips ? flips[p] : NULL;
6312           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6313         }
6314         break;
6315       case INSERT_ALL_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, insert, PETSC_TRUE, clperm, values, &offset, array);
6321         }
6322         break;
6323       case INSERT_BC_VALUES:
6324         for (p = 0; p < numPoints; p++) {
6325           const PetscInt     point = points[2 * p];
6326           const PetscInt    *perm  = perms ? perms[p] : NULL;
6327           const PetscScalar *flip  = flips ? flips[p] : NULL;
6328           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6329         }
6330         break;
6331       case ADD_VALUES:
6332         for (p = 0; p < numPoints; p++) {
6333           const PetscInt     point = points[2 * p];
6334           const PetscInt    *perm  = perms ? perms[p] : NULL;
6335           const PetscScalar *flip  = flips ? flips[p] : NULL;
6336           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6337         }
6338         break;
6339       case ADD_ALL_VALUES:
6340         for (p = 0; p < numPoints; p++) {
6341           const PetscInt     point = points[2 * p];
6342           const PetscInt    *perm  = perms ? perms[p] : NULL;
6343           const PetscScalar *flip  = flips ? flips[p] : NULL;
6344           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6345         }
6346         break;
6347       case ADD_BC_VALUES:
6348         for (p = 0; p < numPoints; p++) {
6349           const PetscInt     point = points[2 * p];
6350           const PetscInt    *perm  = perms ? perms[p] : NULL;
6351           const PetscScalar *flip  = flips ? flips[p] : NULL;
6352           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6353         }
6354         break;
6355       default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6356       }
6357       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6358     }
6359   } else {
6360     PetscInt            dof, off;
6361     const PetscInt    **perms = NULL;
6362     const PetscScalar **flips = NULL;
6363 
6364     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6365     switch (mode) {
6366     case INSERT_VALUES:
6367       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6368         const PetscInt     point = points[2 * p];
6369         const PetscInt    *perm  = perms ? perms[p] : NULL;
6370         const PetscScalar *flip  = flips ? flips[p] : NULL;
6371         PetscCall(PetscSectionGetDof(section, point, &dof));
6372         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6373       }
6374       break;
6375     case INSERT_ALL_VALUES:
6376       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6377         const PetscInt     point = points[2 * p];
6378         const PetscInt    *perm  = perms ? perms[p] : NULL;
6379         const PetscScalar *flip  = flips ? flips[p] : NULL;
6380         PetscCall(PetscSectionGetDof(section, point, &dof));
6381         updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array);
6382       }
6383       break;
6384     case INSERT_BC_VALUES:
6385       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6386         const PetscInt     point = points[2 * p];
6387         const PetscInt    *perm  = perms ? perms[p] : NULL;
6388         const PetscScalar *flip  = flips ? flips[p] : NULL;
6389         PetscCall(PetscSectionGetDof(section, point, &dof));
6390         updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array);
6391       }
6392       break;
6393     case ADD_VALUES:
6394       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6395         const PetscInt     point = points[2 * p];
6396         const PetscInt    *perm  = perms ? perms[p] : NULL;
6397         const PetscScalar *flip  = flips ? flips[p] : NULL;
6398         PetscCall(PetscSectionGetDof(section, point, &dof));
6399         updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array);
6400       }
6401       break;
6402     case ADD_ALL_VALUES:
6403       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6404         const PetscInt     point = points[2 * p];
6405         const PetscInt    *perm  = perms ? perms[p] : NULL;
6406         const PetscScalar *flip  = flips ? flips[p] : NULL;
6407         PetscCall(PetscSectionGetDof(section, point, &dof));
6408         updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array);
6409       }
6410       break;
6411     case ADD_BC_VALUES:
6412       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6413         const PetscInt     point = points[2 * p];
6414         const PetscInt    *perm  = perms ? perms[p] : NULL;
6415         const PetscScalar *flip  = flips ? flips[p] : NULL;
6416         PetscCall(PetscSectionGetDof(section, point, &dof));
6417         updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array);
6418       }
6419       break;
6420     default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6421     }
6422     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6423   }
6424   /* Cleanup points */
6425   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6426   /* Cleanup array */
6427   PetscCall(VecRestoreArray(v, &array));
6428   PetscFunctionReturn(0);
6429 }
6430 
6431 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6432 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains) {
6433   PetscFunctionBegin;
6434   *contains = PETSC_TRUE;
6435   if (label) {
6436     PetscInt fdof;
6437 
6438     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6439     if (!*contains) {
6440       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6441       *offset += fdof;
6442       PetscFunctionReturn(0);
6443     }
6444   }
6445   PetscFunctionReturn(0);
6446 }
6447 
6448 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6449 PetscErrorCode DMPlexVecSetFieldClosure_Internal(DM dm, PetscSection section, Vec v, PetscBool fieldActive[], PetscInt point, PetscInt Ncc, const PetscInt comps[], DMLabel label, PetscInt labelId, const PetscScalar values[], InsertMode mode) {
6450   PetscSection    clSection;
6451   IS              clPoints;
6452   PetscScalar    *array;
6453   PetscInt       *points = NULL;
6454   const PetscInt *clp;
6455   PetscInt        numFields, numPoints, p;
6456   PetscInt        offset = 0, f;
6457 
6458   PetscFunctionBeginHot;
6459   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6460   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6461   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6462   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6463   PetscCall(PetscSectionGetNumFields(section, &numFields));
6464   /* Get points */
6465   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6466   /* Get array */
6467   PetscCall(VecGetArray(v, &array));
6468   /* Get values */
6469   for (f = 0; f < numFields; ++f) {
6470     const PetscInt    **perms = NULL;
6471     const PetscScalar **flips = NULL;
6472     PetscBool           contains;
6473 
6474     if (!fieldActive[f]) {
6475       for (p = 0; p < numPoints * 2; p += 2) {
6476         PetscInt fdof;
6477         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6478         offset += fdof;
6479       }
6480       continue;
6481     }
6482     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6483     switch (mode) {
6484     case INSERT_VALUES:
6485       for (p = 0; p < numPoints; p++) {
6486         const PetscInt     point = points[2 * p];
6487         const PetscInt    *perm  = perms ? perms[p] : NULL;
6488         const PetscScalar *flip  = flips ? flips[p] : NULL;
6489         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6490         if (!contains) continue;
6491         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6492       }
6493       break;
6494     case INSERT_ALL_VALUES:
6495       for (p = 0; p < numPoints; p++) {
6496         const PetscInt     point = points[2 * p];
6497         const PetscInt    *perm  = perms ? perms[p] : NULL;
6498         const PetscScalar *flip  = flips ? flips[p] : NULL;
6499         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6500         if (!contains) continue;
6501         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6502       }
6503       break;
6504     case INSERT_BC_VALUES:
6505       for (p = 0; p < numPoints; p++) {
6506         const PetscInt     point = points[2 * p];
6507         const PetscInt    *perm  = perms ? perms[p] : NULL;
6508         const PetscScalar *flip  = flips ? flips[p] : NULL;
6509         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6510         if (!contains) continue;
6511         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6512       }
6513       break;
6514     case ADD_VALUES:
6515       for (p = 0; p < numPoints; p++) {
6516         const PetscInt     point = points[2 * p];
6517         const PetscInt    *perm  = perms ? perms[p] : NULL;
6518         const PetscScalar *flip  = flips ? flips[p] : NULL;
6519         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6520         if (!contains) continue;
6521         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6522       }
6523       break;
6524     case ADD_ALL_VALUES:
6525       for (p = 0; p < numPoints; p++) {
6526         const PetscInt     point = points[2 * p];
6527         const PetscInt    *perm  = perms ? perms[p] : NULL;
6528         const PetscScalar *flip  = flips ? flips[p] : NULL;
6529         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6530         if (!contains) continue;
6531         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6532       }
6533       break;
6534     default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6535     }
6536     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6537   }
6538   /* Cleanup points */
6539   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6540   /* Cleanup array */
6541   PetscCall(VecRestoreArray(v, &array));
6542   PetscFunctionReturn(0);
6543 }
6544 
6545 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[]) {
6546   PetscMPIInt rank;
6547   PetscInt    i, j;
6548 
6549   PetscFunctionBegin;
6550   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6551   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6552   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6553   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6554   numCIndices = numCIndices ? numCIndices : numRIndices;
6555   if (!values) PetscFunctionReturn(0);
6556   for (i = 0; i < numRIndices; i++) {
6557     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6558     for (j = 0; j < numCIndices; j++) {
6559 #if defined(PETSC_USE_COMPLEX)
6560       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6561 #else
6562       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6563 #endif
6564     }
6565     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6566   }
6567   PetscFunctionReturn(0);
6568 }
6569 
6570 /*
6571   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6572 
6573   Input Parameters:
6574 + section - The section for this data layout
6575 . islocal - Is the section (and thus indices being requested) local or global?
6576 . point   - The point contributing dofs with these indices
6577 . off     - The global offset of this point
6578 . loff    - The local offset of each field
6579 . setBC   - The flag determining whether to include indices of boundary values
6580 . perm    - A permutation of the dofs on this point, or NULL
6581 - indperm - A permutation of the entire indices array, or NULL
6582 
6583   Output Parameter:
6584 . indices - Indices for dofs on this point
6585 
6586   Level: developer
6587 
6588   Note: The indices could be local or global, depending on the value of 'off'.
6589 */
6590 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[]) {
6591   PetscInt        dof;   /* The number of unknowns on this point */
6592   PetscInt        cdof;  /* The number of constraints on this point */
6593   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6594   PetscInt        cind = 0, k;
6595 
6596   PetscFunctionBegin;
6597   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6598   PetscCall(PetscSectionGetDof(section, point, &dof));
6599   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6600   if (!cdof || setBC) {
6601     for (k = 0; k < dof; ++k) {
6602       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6603       const PetscInt ind    = indperm ? indperm[preind] : preind;
6604 
6605       indices[ind] = off + k;
6606     }
6607   } else {
6608     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6609     for (k = 0; k < dof; ++k) {
6610       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6611       const PetscInt ind    = indperm ? indperm[preind] : preind;
6612 
6613       if ((cind < cdof) && (k == cdofs[cind])) {
6614         /* Insert check for returning constrained indices */
6615         indices[ind] = -(off + k + 1);
6616         ++cind;
6617       } else {
6618         indices[ind] = off + k - (islocal ? 0 : cind);
6619       }
6620     }
6621   }
6622   *loff += dof;
6623   PetscFunctionReturn(0);
6624 }
6625 
6626 /*
6627  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6628 
6629  Input Parameters:
6630 + section - a section (global or local)
6631 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6632 . point - point within section
6633 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6634 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6635 . setBC - identify constrained (boundary condition) points via involution.
6636 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6637 . permsoff - offset
6638 - indperm - index permutation
6639 
6640  Output Parameter:
6641 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6642 . indices - array to hold indices (as defined by section) of each dof associated with point
6643 
6644  Notes:
6645  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6646  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6647  in the local vector.
6648 
6649  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6650  significant).  It is invalid to call with a global section and setBC=true.
6651 
6652  Developer Note:
6653  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6654  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6655  offset could be obtained from the section instead of passing it explicitly as we do now.
6656 
6657  Example:
6658  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6659  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6660  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6661  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.
6662 
6663  Level: developer
6664 */
6665 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[]) {
6666   PetscInt numFields, foff, f;
6667 
6668   PetscFunctionBegin;
6669   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6670   PetscCall(PetscSectionGetNumFields(section, &numFields));
6671   for (f = 0, foff = 0; f < numFields; ++f) {
6672     PetscInt        fdof, cfdof;
6673     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6674     PetscInt        cind = 0, b;
6675     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6676 
6677     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6678     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6679     if (!cfdof || setBC) {
6680       for (b = 0; b < fdof; ++b) {
6681         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6682         const PetscInt ind    = indperm ? indperm[preind] : preind;
6683 
6684         indices[ind] = off + foff + b;
6685       }
6686     } else {
6687       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6688       for (b = 0; b < fdof; ++b) {
6689         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6690         const PetscInt ind    = indperm ? indperm[preind] : preind;
6691 
6692         if ((cind < cfdof) && (b == fcdofs[cind])) {
6693           indices[ind] = -(off + foff + b + 1);
6694           ++cind;
6695         } else {
6696           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6697         }
6698       }
6699     }
6700     foff += (setBC || islocal ? fdof : (fdof - cfdof));
6701     foffs[f] += fdof;
6702   }
6703   PetscFunctionReturn(0);
6704 }
6705 
6706 /*
6707   This version believes the globalSection offsets for each field, rather than just the point offset
6708 
6709  . foffs - The offset into 'indices' for each field, since it is segregated by field
6710 
6711  Notes:
6712  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6713  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6714 */
6715 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[]) {
6716   PetscInt numFields, foff, f;
6717 
6718   PetscFunctionBegin;
6719   PetscCall(PetscSectionGetNumFields(section, &numFields));
6720   for (f = 0; f < numFields; ++f) {
6721     PetscInt        fdof, cfdof;
6722     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6723     PetscInt        cind = 0, b;
6724     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6725 
6726     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6727     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6728     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6729     if (!cfdof) {
6730       for (b = 0; b < fdof; ++b) {
6731         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6732         const PetscInt ind    = indperm ? indperm[preind] : preind;
6733 
6734         indices[ind] = foff + b;
6735       }
6736     } else {
6737       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6738       for (b = 0; b < fdof; ++b) {
6739         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6740         const PetscInt ind    = indperm ? indperm[preind] : preind;
6741 
6742         if ((cind < cfdof) && (b == fcdofs[cind])) {
6743           indices[ind] = -(foff + b + 1);
6744           ++cind;
6745         } else {
6746           indices[ind] = foff + b - cind;
6747         }
6748       }
6749     }
6750     foffs[f] += fdof;
6751   }
6752   PetscFunctionReturn(0);
6753 }
6754 
6755 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) {
6756   Mat             cMat;
6757   PetscSection    aSec, cSec;
6758   IS              aIS;
6759   PetscInt        aStart = -1, aEnd = -1;
6760   const PetscInt *anchors;
6761   PetscInt        numFields, f, p, q, newP = 0;
6762   PetscInt        newNumPoints = 0, newNumIndices = 0;
6763   PetscInt       *newPoints, *indices, *newIndices;
6764   PetscInt        maxAnchor, maxDof;
6765   PetscInt        newOffsets[32];
6766   PetscInt       *pointMatOffsets[32];
6767   PetscInt       *newPointOffsets[32];
6768   PetscScalar    *pointMat[32];
6769   PetscScalar    *newValues      = NULL, *tmpValues;
6770   PetscBool       anyConstrained = PETSC_FALSE;
6771 
6772   PetscFunctionBegin;
6773   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6774   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6775   PetscCall(PetscSectionGetNumFields(section, &numFields));
6776 
6777   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
6778   /* if there are point-to-point constraints */
6779   if (aSec) {
6780     PetscCall(PetscArrayzero(newOffsets, 32));
6781     PetscCall(ISGetIndices(aIS, &anchors));
6782     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
6783     /* figure out how many points are going to be in the new element matrix
6784      * (we allow double counting, because it's all just going to be summed
6785      * into the global matrix anyway) */
6786     for (p = 0; p < 2 * numPoints; p += 2) {
6787       PetscInt b    = points[p];
6788       PetscInt bDof = 0, bSecDof;
6789 
6790       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6791       if (!bSecDof) continue;
6792       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6793       if (bDof) {
6794         /* this point is constrained */
6795         /* it is going to be replaced by its anchors */
6796         PetscInt bOff, q;
6797 
6798         anyConstrained = PETSC_TRUE;
6799         newNumPoints += bDof;
6800         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6801         for (q = 0; q < bDof; q++) {
6802           PetscInt a = anchors[bOff + q];
6803           PetscInt aDof;
6804 
6805           PetscCall(PetscSectionGetDof(section, a, &aDof));
6806           newNumIndices += aDof;
6807           for (f = 0; f < numFields; ++f) {
6808             PetscInt fDof;
6809 
6810             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6811             newOffsets[f + 1] += fDof;
6812           }
6813         }
6814       } else {
6815         /* this point is not constrained */
6816         newNumPoints++;
6817         newNumIndices += bSecDof;
6818         for (f = 0; f < numFields; ++f) {
6819           PetscInt fDof;
6820 
6821           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6822           newOffsets[f + 1] += fDof;
6823         }
6824       }
6825     }
6826   }
6827   if (!anyConstrained) {
6828     if (outNumPoints) *outNumPoints = 0;
6829     if (outNumIndices) *outNumIndices = 0;
6830     if (outPoints) *outPoints = NULL;
6831     if (outValues) *outValues = NULL;
6832     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
6833     PetscFunctionReturn(0);
6834   }
6835 
6836   if (outNumPoints) *outNumPoints = newNumPoints;
6837   if (outNumIndices) *outNumIndices = newNumIndices;
6838 
6839   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
6840 
6841   if (!outPoints && !outValues) {
6842     if (offsets) {
6843       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
6844     }
6845     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
6846     PetscFunctionReturn(0);
6847   }
6848 
6849   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6850 
6851   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6852 
6853   /* workspaces */
6854   if (numFields) {
6855     for (f = 0; f < numFields; f++) {
6856       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
6857       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
6858     }
6859   } else {
6860     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
6861     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
6862   }
6863 
6864   /* get workspaces for the point-to-point matrices */
6865   if (numFields) {
6866     PetscInt totalOffset, totalMatOffset;
6867 
6868     for (p = 0; p < numPoints; p++) {
6869       PetscInt b    = points[2 * p];
6870       PetscInt bDof = 0, bSecDof;
6871 
6872       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6873       if (!bSecDof) {
6874         for (f = 0; f < numFields; f++) {
6875           newPointOffsets[f][p + 1] = 0;
6876           pointMatOffsets[f][p + 1] = 0;
6877         }
6878         continue;
6879       }
6880       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6881       if (bDof) {
6882         for (f = 0; f < numFields; f++) {
6883           PetscInt fDof, q, bOff, allFDof = 0;
6884 
6885           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6886           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6887           for (q = 0; q < bDof; q++) {
6888             PetscInt a = anchors[bOff + q];
6889             PetscInt aFDof;
6890 
6891             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6892             allFDof += aFDof;
6893           }
6894           newPointOffsets[f][p + 1] = allFDof;
6895           pointMatOffsets[f][p + 1] = fDof * allFDof;
6896         }
6897       } else {
6898         for (f = 0; f < numFields; f++) {
6899           PetscInt fDof;
6900 
6901           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6902           newPointOffsets[f][p + 1] = fDof;
6903           pointMatOffsets[f][p + 1] = 0;
6904         }
6905       }
6906     }
6907     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6908       newPointOffsets[f][0] = totalOffset;
6909       pointMatOffsets[f][0] = totalMatOffset;
6910       for (p = 0; p < numPoints; p++) {
6911         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
6912         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
6913       }
6914       totalOffset    = newPointOffsets[f][numPoints];
6915       totalMatOffset = pointMatOffsets[f][numPoints];
6916       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
6917     }
6918   } else {
6919     for (p = 0; p < numPoints; p++) {
6920       PetscInt b    = points[2 * p];
6921       PetscInt bDof = 0, bSecDof;
6922 
6923       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6924       if (!bSecDof) {
6925         newPointOffsets[0][p + 1] = 0;
6926         pointMatOffsets[0][p + 1] = 0;
6927         continue;
6928       }
6929       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6930       if (bDof) {
6931         PetscInt bOff, q, allDof = 0;
6932 
6933         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6934         for (q = 0; q < bDof; q++) {
6935           PetscInt a = anchors[bOff + q], aDof;
6936 
6937           PetscCall(PetscSectionGetDof(section, a, &aDof));
6938           allDof += aDof;
6939         }
6940         newPointOffsets[0][p + 1] = allDof;
6941         pointMatOffsets[0][p + 1] = bSecDof * allDof;
6942       } else {
6943         newPointOffsets[0][p + 1] = bSecDof;
6944         pointMatOffsets[0][p + 1] = 0;
6945       }
6946     }
6947     newPointOffsets[0][0] = 0;
6948     pointMatOffsets[0][0] = 0;
6949     for (p = 0; p < numPoints; p++) {
6950       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
6951       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
6952     }
6953     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
6954   }
6955 
6956   /* output arrays */
6957   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
6958 
6959   /* get the point-to-point matrices; construct newPoints */
6960   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6961   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6962   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
6963   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
6964   if (numFields) {
6965     for (p = 0, newP = 0; p < numPoints; p++) {
6966       PetscInt b    = points[2 * p];
6967       PetscInt o    = points[2 * p + 1];
6968       PetscInt bDof = 0, bSecDof;
6969 
6970       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6971       if (!bSecDof) continue;
6972       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6973       if (bDof) {
6974         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6975 
6976         fStart[0] = 0;
6977         fEnd[0]   = 0;
6978         for (f = 0; f < numFields; f++) {
6979           PetscInt fDof;
6980 
6981           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
6982           fStart[f + 1] = fStart[f] + fDof;
6983           fEnd[f + 1]   = fStart[f + 1];
6984         }
6985         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
6986         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
6987 
6988         fAnchorStart[0] = 0;
6989         fAnchorEnd[0]   = 0;
6990         for (f = 0; f < numFields; f++) {
6991           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
6992 
6993           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
6994           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
6995         }
6996         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6997         for (q = 0; q < bDof; q++) {
6998           PetscInt a = anchors[bOff + q], aOff;
6999 
7000           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7001           newPoints[2 * (newP + q)]     = a;
7002           newPoints[2 * (newP + q) + 1] = 0;
7003           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7004           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7005         }
7006         newP += bDof;
7007 
7008         if (outValues) {
7009           /* get the point-to-point submatrix */
7010           for (f = 0; f < numFields; f++) PetscCall(MatGetValues(cMat, fEnd[f] - fStart[f], indices + fStart[f], fAnchorEnd[f] - fAnchorStart[f], newIndices + fAnchorStart[f], pointMat[f] + pointMatOffsets[f][p]));
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) continue;
7026       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7027       if (bDof) {
7028         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7029 
7030         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7031         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7032 
7033         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7034         for (q = 0; q < bDof; q++) {
7035           PetscInt a = anchors[bOff + q], aOff;
7036 
7037           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7038 
7039           newPoints[2 * (newP + q)]     = a;
7040           newPoints[2 * (newP + q) + 1] = 0;
7041           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7042           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7043         }
7044         newP += bDof;
7045 
7046         /* get the point-to-point submatrix */
7047         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7048       } else {
7049         newPoints[2 * newP]     = b;
7050         newPoints[2 * newP + 1] = o;
7051         newP++;
7052       }
7053     }
7054   }
7055 
7056   if (outValues) {
7057     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7058     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7059     /* multiply constraints on the right */
7060     if (numFields) {
7061       for (f = 0; f < numFields; f++) {
7062         PetscInt oldOff = offsets[f];
7063 
7064         for (p = 0; p < numPoints; p++) {
7065           PetscInt cStart = newPointOffsets[f][p];
7066           PetscInt b      = points[2 * p];
7067           PetscInt c, r, k;
7068           PetscInt dof;
7069 
7070           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7071           if (!dof) continue;
7072           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7073             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7074             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7075 
7076             for (r = 0; r < numIndices; r++) {
7077               for (c = 0; c < nCols; c++) {
7078                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7079               }
7080             }
7081           } else {
7082             /* copy this column as is */
7083             for (r = 0; r < numIndices; r++) {
7084               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7085             }
7086           }
7087           oldOff += dof;
7088         }
7089       }
7090     } else {
7091       PetscInt oldOff = 0;
7092       for (p = 0; p < numPoints; p++) {
7093         PetscInt cStart = newPointOffsets[0][p];
7094         PetscInt b      = points[2 * p];
7095         PetscInt c, r, k;
7096         PetscInt dof;
7097 
7098         PetscCall(PetscSectionGetDof(section, b, &dof));
7099         if (!dof) continue;
7100         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7101           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7102           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7103 
7104           for (r = 0; r < numIndices; r++) {
7105             for (c = 0; c < nCols; c++) {
7106               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7107             }
7108           }
7109         } else {
7110           /* copy this column as is */
7111           for (r = 0; r < numIndices; r++) {
7112             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7113           }
7114         }
7115         oldOff += dof;
7116       }
7117     }
7118 
7119     if (multiplyLeft) {
7120       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7121       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7122       /* multiply constraints transpose on the left */
7123       if (numFields) {
7124         for (f = 0; f < numFields; f++) {
7125           PetscInt oldOff = offsets[f];
7126 
7127           for (p = 0; p < numPoints; p++) {
7128             PetscInt rStart = newPointOffsets[f][p];
7129             PetscInt b      = points[2 * p];
7130             PetscInt c, r, k;
7131             PetscInt dof;
7132 
7133             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7134             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7135               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7136               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7137 
7138               for (r = 0; r < nRows; r++) {
7139                 for (c = 0; c < newNumIndices; c++) {
7140                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7141                 }
7142               }
7143             } else {
7144               /* copy this row as is */
7145               for (r = 0; r < dof; r++) {
7146                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7147               }
7148             }
7149             oldOff += dof;
7150           }
7151         }
7152       } else {
7153         PetscInt oldOff = 0;
7154 
7155         for (p = 0; p < numPoints; p++) {
7156           PetscInt rStart = newPointOffsets[0][p];
7157           PetscInt b      = points[2 * p];
7158           PetscInt c, r, k;
7159           PetscInt dof;
7160 
7161           PetscCall(PetscSectionGetDof(section, b, &dof));
7162           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7163             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7164             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7165 
7166             for (r = 0; r < nRows; r++) {
7167               for (c = 0; c < newNumIndices; c++) {
7168                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7169               }
7170             }
7171           } else {
7172             /* copy this row as is */
7173             for (r = 0; r < dof; r++) {
7174               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7175             }
7176           }
7177           oldOff += dof;
7178         }
7179       }
7180 
7181       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7182     } else {
7183       newValues = tmpValues;
7184     }
7185   }
7186 
7187   /* clean up */
7188   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7189   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7190 
7191   if (numFields) {
7192     for (f = 0; f < numFields; f++) {
7193       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7194       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7195       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7196     }
7197   } else {
7198     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7199     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7200     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7201   }
7202   PetscCall(ISRestoreIndices(aIS, &anchors));
7203 
7204   /* output */
7205   if (outPoints) {
7206     *outPoints = newPoints;
7207   } else {
7208     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7209   }
7210   if (outValues) *outValues = newValues;
7211   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7212   PetscFunctionReturn(0);
7213 }
7214 
7215 /*@C
7216   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7217 
7218   Not collective
7219 
7220   Input Parameters:
7221 + dm         - The DM
7222 . section    - The PetscSection describing the points (a local section)
7223 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7224 . point      - The point defining the closure
7225 - useClPerm  - Use the closure point permutation if available
7226 
7227   Output Parameters:
7228 + numIndices - The number of dof indices in the closure of point with the input sections
7229 . indices    - The dof indices
7230 . outOffsets - Array to write the field offsets into, or NULL
7231 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7232 
7233   Notes:
7234   Must call DMPlexRestoreClosureIndices() to free allocated memory
7235 
7236   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7237   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7238   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7239   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7240   indices (with the above semantics) are implied.
7241 
7242   Level: advanced
7243 
7244 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7245 @*/
7246 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[]) {
7247   /* Closure ordering */
7248   PetscSection        clSection;
7249   IS                  clPoints;
7250   const PetscInt     *clp;
7251   PetscInt           *points;
7252   const PetscInt     *clperm    = NULL;
7253   /* Dof permutation and sign flips */
7254   const PetscInt    **perms[32] = {NULL};
7255   const PetscScalar **flips[32] = {NULL};
7256   PetscScalar        *valCopy   = NULL;
7257   /* Hanging node constraints */
7258   PetscInt           *pointsC   = NULL;
7259   PetscScalar        *valuesC   = NULL;
7260   PetscInt            NclC, NiC;
7261 
7262   PetscInt *idx;
7263   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7264   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7265 
7266   PetscFunctionBeginHot;
7267   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7268   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7269   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7270   if (numIndices) PetscValidIntPointer(numIndices, 6);
7271   if (indices) PetscValidPointer(indices, 7);
7272   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7273   if (values) PetscValidPointer(values, 9);
7274   PetscCall(PetscSectionGetNumFields(section, &Nf));
7275   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7276   PetscCall(PetscArrayzero(offsets, 32));
7277   /* 1) Get points in closure */
7278   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7279   if (useClPerm) {
7280     PetscInt depth, clsize;
7281     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7282     for (clsize = 0, p = 0; p < Ncl; p++) {
7283       PetscInt dof;
7284       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7285       clsize += dof;
7286     }
7287     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7288   }
7289   /* 2) Get number of indices on these points and field offsets from section */
7290   for (p = 0; p < Ncl * 2; p += 2) {
7291     PetscInt dof, fdof;
7292 
7293     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7294     for (f = 0; f < Nf; ++f) {
7295       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7296       offsets[f + 1] += fdof;
7297     }
7298     Ni += dof;
7299   }
7300   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7301   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7302   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7303   for (f = 0; f < PetscMax(1, Nf); ++f) {
7304     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7305     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7306     /* may need to apply sign changes to the element matrix */
7307     if (values && flips[f]) {
7308       PetscInt foffset = offsets[f];
7309 
7310       for (p = 0; p < Ncl; ++p) {
7311         PetscInt           pnt  = points[2 * p], fdof;
7312         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7313 
7314         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7315         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7316         if (flip) {
7317           PetscInt i, j, k;
7318 
7319           if (!valCopy) {
7320             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7321             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7322             *values = valCopy;
7323           }
7324           for (i = 0; i < fdof; ++i) {
7325             PetscScalar fval = flip[i];
7326 
7327             for (k = 0; k < Ni; ++k) {
7328               valCopy[Ni * (foffset + i) + k] *= fval;
7329               valCopy[Ni * k + (foffset + i)] *= fval;
7330             }
7331           }
7332         }
7333         foffset += fdof;
7334       }
7335     }
7336   }
7337   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7338   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7339   if (NclC) {
7340     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7341     for (f = 0; f < PetscMax(1, Nf); ++f) {
7342       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7343       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7344     }
7345     for (f = 0; f < PetscMax(1, Nf); ++f) {
7346       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7347       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7348     }
7349     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7350     Ncl    = NclC;
7351     Ni     = NiC;
7352     points = pointsC;
7353     if (values) *values = valuesC;
7354   }
7355   /* 5) Calculate indices */
7356   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7357   if (Nf) {
7358     PetscInt  idxOff;
7359     PetscBool useFieldOffsets;
7360 
7361     if (outOffsets) {
7362       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7363     }
7364     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7365     if (useFieldOffsets) {
7366       for (p = 0; p < Ncl; ++p) {
7367         const PetscInt pnt = points[p * 2];
7368 
7369         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7370       }
7371     } else {
7372       for (p = 0; p < Ncl; ++p) {
7373         const PetscInt pnt = points[p * 2];
7374 
7375         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7376         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7377          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7378          * global section. */
7379         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7380       }
7381     }
7382   } else {
7383     PetscInt off = 0, idxOff;
7384 
7385     for (p = 0; p < Ncl; ++p) {
7386       const PetscInt  pnt  = points[p * 2];
7387       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7388 
7389       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7390       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7391        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7392       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7393     }
7394   }
7395   /* 6) Cleanup */
7396   for (f = 0; f < PetscMax(1, Nf); ++f) {
7397     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7398     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7399   }
7400   if (NclC) {
7401     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7402   } else {
7403     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7404   }
7405 
7406   if (numIndices) *numIndices = Ni;
7407   if (indices) *indices = idx;
7408   PetscFunctionReturn(0);
7409 }
7410 
7411 /*@C
7412   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7413 
7414   Not collective
7415 
7416   Input Parameters:
7417 + dm         - The DM
7418 . section    - The PetscSection describing the points (a local section)
7419 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7420 . point      - The point defining the closure
7421 - useClPerm  - Use the closure point permutation if available
7422 
7423   Output Parameters:
7424 + numIndices - The number of dof indices in the closure of point with the input sections
7425 . indices    - The dof indices
7426 . outOffsets - Array to write the field offsets into, or NULL
7427 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7428 
7429   Notes:
7430   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7431 
7432   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7433   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7434   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7435   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7436   indices (with the above semantics) are implied.
7437 
7438   Level: advanced
7439 
7440 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7441 @*/
7442 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[]) {
7443   PetscFunctionBegin;
7444   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7445   PetscValidPointer(indices, 7);
7446   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7447   PetscFunctionReturn(0);
7448 }
7449 
7450 /*@C
7451   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7452 
7453   Not collective
7454 
7455   Input Parameters:
7456 + dm - The DM
7457 . section - The section describing the layout in v, or NULL to use the default section
7458 . globalSection - The section describing the layout in v, or NULL to use the default global section
7459 . A - The matrix
7460 . point - The point in the DM
7461 . values - The array of values
7462 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7463 
7464   Fortran Notes:
7465   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7466 
7467   Level: intermediate
7468 
7469 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7470 @*/
7471 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode) {
7472   DM_Plex           *mesh = (DM_Plex *)dm->data;
7473   PetscInt          *indices;
7474   PetscInt           numIndices;
7475   const PetscScalar *valuesOrig = values;
7476   PetscErrorCode     ierr;
7477 
7478   PetscFunctionBegin;
7479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7480   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7481   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7482   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7483   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7484   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7485 
7486   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7487 
7488   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7489   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7490   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7491   if (ierr) {
7492     PetscMPIInt rank;
7493 
7494     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7495     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7496     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7497     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7498     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7499     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7500   }
7501   if (mesh->printFEM > 1) {
7502     PetscInt i;
7503     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7504     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7505     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7506   }
7507 
7508   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7509   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7510   PetscFunctionReturn(0);
7511 }
7512 
7513 /*@C
7514   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7515 
7516   Not collective
7517 
7518   Input Parameters:
7519 + dmRow - The DM for the row fields
7520 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7521 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7522 . dmCol - The DM for the column fields
7523 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7524 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7525 . A - The matrix
7526 . point - The point in the DMs
7527 . values - The array of values
7528 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7529 
7530   Level: intermediate
7531 
7532 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7533 @*/
7534 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode) {
7535   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7536   PetscInt          *indicesRow, *indicesCol;
7537   PetscInt           numIndicesRow, numIndicesCol;
7538   const PetscScalar *valuesOrig = values;
7539   PetscErrorCode     ierr;
7540 
7541   PetscFunctionBegin;
7542   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7543   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7544   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7545   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7546   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7547   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7548   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7549   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7550   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7551   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7552   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7553 
7554   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7555   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7556 
7557   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7558   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7559   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7560   if (ierr) {
7561     PetscMPIInt rank;
7562 
7563     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7564     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7565     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7566     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7567     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7568     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7569   }
7570 
7571   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7572   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7573   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7574   PetscFunctionReturn(0);
7575 }
7576 
7577 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode) {
7578   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7579   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7580   PetscInt       *cpoints = NULL;
7581   PetscInt       *findices, *cindices;
7582   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7583   PetscInt        foffsets[32], coffsets[32];
7584   DMPolytopeType  ct;
7585   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7586   PetscErrorCode  ierr;
7587 
7588   PetscFunctionBegin;
7589   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7590   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7591   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7592   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7593   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7594   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7595   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7596   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7597   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7598   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7599   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7600   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7601   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7602   PetscCall(PetscArrayzero(foffsets, 32));
7603   PetscCall(PetscArrayzero(coffsets, 32));
7604   /* Column indices */
7605   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7606   maxFPoints = numCPoints;
7607   /* Compress out points not in the section */
7608   /*   TODO: Squeeze out points with 0 dof as well */
7609   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7610   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7611     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7612       cpoints[q * 2]     = cpoints[p];
7613       cpoints[q * 2 + 1] = cpoints[p + 1];
7614       ++q;
7615     }
7616   }
7617   numCPoints = q;
7618   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7619     PetscInt fdof;
7620 
7621     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7622     if (!dof) continue;
7623     for (f = 0; f < numFields; ++f) {
7624       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7625       coffsets[f + 1] += fdof;
7626     }
7627     numCIndices += dof;
7628   }
7629   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7630   /* Row indices */
7631   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7632   {
7633     DMPlexTransform tr;
7634     DMPolytopeType *rct;
7635     PetscInt       *rsize, *rcone, *rornt, Nt;
7636 
7637     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7638     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7639     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7640     numSubcells = rsize[Nt - 1];
7641     PetscCall(DMPlexTransformDestroy(&tr));
7642   }
7643   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7644   for (r = 0, q = 0; r < numSubcells; ++r) {
7645     /* TODO Map from coarse to fine cells */
7646     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7647     /* Compress out points not in the section */
7648     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7649     for (p = 0; p < numFPoints * 2; p += 2) {
7650       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7651         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7652         if (!dof) continue;
7653         for (s = 0; s < q; ++s)
7654           if (fpoints[p] == ftotpoints[s * 2]) break;
7655         if (s < q) continue;
7656         ftotpoints[q * 2]     = fpoints[p];
7657         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7658         ++q;
7659       }
7660     }
7661     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7662   }
7663   numFPoints = q;
7664   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7665     PetscInt fdof;
7666 
7667     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7668     if (!dof) continue;
7669     for (f = 0; f < numFields; ++f) {
7670       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7671       foffsets[f + 1] += fdof;
7672     }
7673     numFIndices += dof;
7674   }
7675   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7676 
7677   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7678   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7679   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7680   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7681   if (numFields) {
7682     const PetscInt **permsF[32] = {NULL};
7683     const PetscInt **permsC[32] = {NULL};
7684 
7685     for (f = 0; f < numFields; f++) {
7686       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7687       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7688     }
7689     for (p = 0; p < numFPoints; p++) {
7690       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7691       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7692     }
7693     for (p = 0; p < numCPoints; p++) {
7694       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7695       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7696     }
7697     for (f = 0; f < numFields; f++) {
7698       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7699       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7700     }
7701   } else {
7702     const PetscInt **permsF = NULL;
7703     const PetscInt **permsC = NULL;
7704 
7705     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7706     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7707     for (p = 0, off = 0; p < numFPoints; p++) {
7708       const PetscInt *perm = permsF ? permsF[p] : NULL;
7709 
7710       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7711       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7712     }
7713     for (p = 0, off = 0; p < numCPoints; p++) {
7714       const PetscInt *perm = permsC ? permsC[p] : NULL;
7715 
7716       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7717       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7718     }
7719     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7720     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7721   }
7722   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7723   /* TODO: flips */
7724   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7725   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7726   if (ierr) {
7727     PetscMPIInt rank;
7728 
7729     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7730     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7731     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7732     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7733     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7734   }
7735   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
7736   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7737   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7738   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7739   PetscFunctionReturn(0);
7740 }
7741 
7742 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[]) {
7743   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7744   PetscInt       *cpoints = NULL;
7745   PetscInt        foffsets[32], coffsets[32];
7746   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7747   DMPolytopeType  ct;
7748   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7749 
7750   PetscFunctionBegin;
7751   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7752   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7753   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7754   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7755   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7756   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7757   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7758   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7759   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7760   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7761   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7762   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7763   PetscCall(PetscArrayzero(foffsets, 32));
7764   PetscCall(PetscArrayzero(coffsets, 32));
7765   /* Column indices */
7766   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7767   maxFPoints = numCPoints;
7768   /* Compress out points not in the section */
7769   /*   TODO: Squeeze out points with 0 dof as well */
7770   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7771   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7772     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7773       cpoints[q * 2]     = cpoints[p];
7774       cpoints[q * 2 + 1] = cpoints[p + 1];
7775       ++q;
7776     }
7777   }
7778   numCPoints = q;
7779   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7780     PetscInt fdof;
7781 
7782     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7783     if (!dof) continue;
7784     for (f = 0; f < numFields; ++f) {
7785       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7786       coffsets[f + 1] += fdof;
7787     }
7788     numCIndices += dof;
7789   }
7790   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7791   /* Row indices */
7792   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7793   {
7794     DMPlexTransform tr;
7795     DMPolytopeType *rct;
7796     PetscInt       *rsize, *rcone, *rornt, Nt;
7797 
7798     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7799     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7800     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7801     numSubcells = rsize[Nt - 1];
7802     PetscCall(DMPlexTransformDestroy(&tr));
7803   }
7804   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7805   for (r = 0, q = 0; r < numSubcells; ++r) {
7806     /* TODO Map from coarse to fine cells */
7807     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7808     /* Compress out points not in the section */
7809     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7810     for (p = 0; p < numFPoints * 2; p += 2) {
7811       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7812         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7813         if (!dof) continue;
7814         for (s = 0; s < q; ++s)
7815           if (fpoints[p] == ftotpoints[s * 2]) break;
7816         if (s < q) continue;
7817         ftotpoints[q * 2]     = fpoints[p];
7818         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7819         ++q;
7820       }
7821     }
7822     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7823   }
7824   numFPoints = q;
7825   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7826     PetscInt fdof;
7827 
7828     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7829     if (!dof) continue;
7830     for (f = 0; f < numFields; ++f) {
7831       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7832       foffsets[f + 1] += fdof;
7833     }
7834     numFIndices += dof;
7835   }
7836   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7837 
7838   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7839   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7840   if (numFields) {
7841     const PetscInt **permsF[32] = {NULL};
7842     const PetscInt **permsC[32] = {NULL};
7843 
7844     for (f = 0; f < numFields; f++) {
7845       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7846       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7847     }
7848     for (p = 0; p < numFPoints; p++) {
7849       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7850       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7851     }
7852     for (p = 0; p < numCPoints; p++) {
7853       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7854       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7855     }
7856     for (f = 0; f < numFields; f++) {
7857       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7858       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7859     }
7860   } else {
7861     const PetscInt **permsF = NULL;
7862     const PetscInt **permsC = NULL;
7863 
7864     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7865     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7866     for (p = 0, off = 0; p < numFPoints; p++) {
7867       const PetscInt *perm = permsF ? permsF[p] : NULL;
7868 
7869       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7870       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7871     }
7872     for (p = 0, off = 0; p < numCPoints; p++) {
7873       const PetscInt *perm = permsC ? permsC[p] : NULL;
7874 
7875       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7876       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7877     }
7878     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7879     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7880   }
7881   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
7882   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7883   PetscFunctionReturn(0);
7884 }
7885 
7886 /*@C
7887   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7888 
7889   Input Parameter:
7890 . dm   - The DMPlex object
7891 
7892   Output Parameter:
7893 . cellHeight - The height of a cell
7894 
7895   Level: developer
7896 
7897 .seealso `DMPlexSetVTKCellHeight()`
7898 @*/
7899 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight) {
7900   DM_Plex *mesh = (DM_Plex *)dm->data;
7901 
7902   PetscFunctionBegin;
7903   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7904   PetscValidIntPointer(cellHeight, 2);
7905   *cellHeight = mesh->vtkCellHeight;
7906   PetscFunctionReturn(0);
7907 }
7908 
7909 /*@C
7910   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7911 
7912   Input Parameters:
7913 + dm   - The DMPlex object
7914 - cellHeight - The height of a cell
7915 
7916   Level: developer
7917 
7918 .seealso `DMPlexGetVTKCellHeight()`
7919 @*/
7920 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight) {
7921   DM_Plex *mesh = (DM_Plex *)dm->data;
7922 
7923   PetscFunctionBegin;
7924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7925   mesh->vtkCellHeight = cellHeight;
7926   PetscFunctionReturn(0);
7927 }
7928 
7929 /*@
7930   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7931 
7932   Input Parameter:
7933 . dm - The DMPlex object
7934 
7935   Output Parameters:
7936 + gcStart - The first ghost cell, or NULL
7937 - gcEnd   - The upper bound on ghost cells, or NULL
7938 
7939   Level: advanced
7940 
7941 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
7942 @*/
7943 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd) {
7944   DMLabel ctLabel;
7945 
7946   PetscFunctionBegin;
7947   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7948   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
7949   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
7950   // Reset label for fast lookup
7951   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
7952   PetscFunctionReturn(0);
7953 }
7954 
7955 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering) {
7956   PetscSection section, globalSection;
7957   PetscInt    *numbers, p;
7958 
7959   PetscFunctionBegin;
7960   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
7961   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
7962   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
7963   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
7964   PetscCall(PetscSectionSetUp(section));
7965   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
7966   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
7967   for (p = pStart; p < pEnd; ++p) {
7968     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
7969     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
7970     else numbers[p - pStart] += shift;
7971   }
7972   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
7973   if (globalSize) {
7974     PetscLayout layout;
7975     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
7976     PetscCall(PetscLayoutGetSize(layout, globalSize));
7977     PetscCall(PetscLayoutDestroy(&layout));
7978   }
7979   PetscCall(PetscSectionDestroy(&section));
7980   PetscCall(PetscSectionDestroy(&globalSection));
7981   PetscFunctionReturn(0);
7982 }
7983 
7984 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers) {
7985   PetscInt cellHeight, cStart, cEnd;
7986 
7987   PetscFunctionBegin;
7988   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
7989   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
7990   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
7991   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
7992   PetscFunctionReturn(0);
7993 }
7994 
7995 /*@
7996   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
7997 
7998   Input Parameter:
7999 . dm   - The DMPlex object
8000 
8001   Output Parameter:
8002 . globalCellNumbers - Global cell numbers for all cells on this process
8003 
8004   Level: developer
8005 
8006 .seealso `DMPlexGetVertexNumbering()`
8007 @*/
8008 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers) {
8009   DM_Plex *mesh = (DM_Plex *)dm->data;
8010 
8011   PetscFunctionBegin;
8012   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8013   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8014   *globalCellNumbers = mesh->globalCellNumbers;
8015   PetscFunctionReturn(0);
8016 }
8017 
8018 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers) {
8019   PetscInt vStart, vEnd;
8020 
8021   PetscFunctionBegin;
8022   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8023   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8024   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8025   PetscFunctionReturn(0);
8026 }
8027 
8028 /*@
8029   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8030 
8031   Input Parameter:
8032 . dm   - The DMPlex object
8033 
8034   Output Parameter:
8035 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8036 
8037   Level: developer
8038 
8039 .seealso `DMPlexGetCellNumbering()`
8040 @*/
8041 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers) {
8042   DM_Plex *mesh = (DM_Plex *)dm->data;
8043 
8044   PetscFunctionBegin;
8045   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8046   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8047   *globalVertexNumbers = mesh->globalVertexNumbers;
8048   PetscFunctionReturn(0);
8049 }
8050 
8051 /*@
8052   DMPlexCreatePointNumbering - Create a global numbering for all points.
8053 
8054   Collective on dm
8055 
8056   Input Parameter:
8057 . dm   - The DMPlex object
8058 
8059   Output Parameter:
8060 . globalPointNumbers - Global numbers for all points on this process
8061 
8062   Notes:
8063 
8064   The point numbering IS is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8065   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8066   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8067   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8068 
8069   The partitioned mesh is
8070 ```
8071  (2)--0--(3)--1--(4)    (1)--0--(2)
8072 ```
8073   and its global numbering is
8074 ```
8075   (3)--0--(4)--1--(5)--2--(6)
8076 ```
8077   Then the global numbering is provided as
8078 ```
8079 [0] Number of indices in set 5
8080 [0] 0 0
8081 [0] 1 1
8082 [0] 2 3
8083 [0] 3 4
8084 [0] 4 -6
8085 [1] Number of indices in set 3
8086 [1] 0 2
8087 [1] 1 5
8088 [1] 2 6
8089 ```
8090 
8091   Level: developer
8092 
8093 .seealso `DMPlexGetCellNumbering()`
8094 @*/
8095 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers) {
8096   IS       nums[4];
8097   PetscInt depths[4], gdepths[4], starts[4];
8098   PetscInt depth, d, shift = 0;
8099 
8100   PetscFunctionBegin;
8101   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8102   PetscCall(DMPlexGetDepth(dm, &depth));
8103   /* For unstratified meshes use dim instead of depth */
8104   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8105   for (d = 0; d <= depth; ++d) {
8106     PetscInt end;
8107 
8108     depths[d] = depth - d;
8109     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8110     if (!(starts[d] - end)) starts[d] = depths[d] = -1;
8111   }
8112   PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8113   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8114   for (d = 0; d <= depth; ++d) 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]);
8115   for (d = 0; d <= depth; ++d) {
8116     PetscInt pStart, pEnd, gsize;
8117 
8118     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8119     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8120     shift += gsize;
8121   }
8122   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8123   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8124   PetscFunctionReturn(0);
8125 }
8126 
8127 /*@
8128   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8129 
8130   Input Parameter:
8131 . dm - The DMPlex object
8132 
8133   Output Parameter:
8134 . ranks - The rank field
8135 
8136   Options Database Keys:
8137 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8138 
8139   Level: intermediate
8140 
8141 .seealso: `DMView()`
8142 @*/
8143 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks) {
8144   DM             rdm;
8145   PetscFE        fe;
8146   PetscScalar   *r;
8147   PetscMPIInt    rank;
8148   DMPolytopeType ct;
8149   PetscInt       dim, cStart, cEnd, c;
8150   PetscBool      simplex;
8151 
8152   PetscFunctionBeginUser;
8153   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8154   PetscValidPointer(ranks, 2);
8155   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8156   PetscCall(DMClone(dm, &rdm));
8157   PetscCall(DMGetDimension(rdm, &dim));
8158   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8159   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8160   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8161   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8162   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8163   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8164   PetscCall(PetscFEDestroy(&fe));
8165   PetscCall(DMCreateDS(rdm));
8166   PetscCall(DMCreateGlobalVector(rdm, ranks));
8167   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8168   PetscCall(VecGetArray(*ranks, &r));
8169   for (c = cStart; c < cEnd; ++c) {
8170     PetscScalar *lr;
8171 
8172     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8173     if (lr) *lr = rank;
8174   }
8175   PetscCall(VecRestoreArray(*ranks, &r));
8176   PetscCall(DMDestroy(&rdm));
8177   PetscFunctionReturn(0);
8178 }
8179 
8180 /*@
8181   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8182 
8183   Input Parameters:
8184 + dm    - The DMPlex
8185 - label - The DMLabel
8186 
8187   Output Parameter:
8188 . val - The label value field
8189 
8190   Options Database Keys:
8191 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8192 
8193   Level: intermediate
8194 
8195 .seealso: `DMView()`
8196 @*/
8197 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val) {
8198   DM           rdm;
8199   PetscFE      fe;
8200   PetscScalar *v;
8201   PetscInt     dim, cStart, cEnd, c;
8202 
8203   PetscFunctionBeginUser;
8204   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8205   PetscValidPointer(label, 2);
8206   PetscValidPointer(val, 3);
8207   PetscCall(DMClone(dm, &rdm));
8208   PetscCall(DMGetDimension(rdm, &dim));
8209   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8210   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8211   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8212   PetscCall(PetscFEDestroy(&fe));
8213   PetscCall(DMCreateDS(rdm));
8214   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8215   PetscCall(DMCreateGlobalVector(rdm, val));
8216   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8217   PetscCall(VecGetArray(*val, &v));
8218   for (c = cStart; c < cEnd; ++c) {
8219     PetscScalar *lv;
8220     PetscInt     cval;
8221 
8222     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8223     PetscCall(DMLabelGetValue(label, c, &cval));
8224     *lv = cval;
8225   }
8226   PetscCall(VecRestoreArray(*val, &v));
8227   PetscCall(DMDestroy(&rdm));
8228   PetscFunctionReturn(0);
8229 }
8230 
8231 /*@
8232   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8233 
8234   Input Parameter:
8235 . dm - The DMPlex object
8236 
8237   Notes:
8238   This is a useful diagnostic when creating meshes programmatically.
8239 
8240   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8241 
8242   Level: developer
8243 
8244 .seealso: `DMCreate()`, `DMSetFromOptions()`
8245 @*/
8246 PetscErrorCode DMPlexCheckSymmetry(DM dm) {
8247   PetscSection    coneSection, supportSection;
8248   const PetscInt *cone, *support;
8249   PetscInt        coneSize, c, supportSize, s;
8250   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8251   PetscBool       storagecheck = PETSC_TRUE;
8252 
8253   PetscFunctionBegin;
8254   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8255   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8256   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8257   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8258   /* Check that point p is found in the support of its cone points, and vice versa */
8259   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8260   for (p = pStart; p < pEnd; ++p) {
8261     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8262     PetscCall(DMPlexGetCone(dm, p, &cone));
8263     for (c = 0; c < coneSize; ++c) {
8264       PetscBool dup = PETSC_FALSE;
8265       PetscInt  d;
8266       for (d = c - 1; d >= 0; --d) {
8267         if (cone[c] == cone[d]) {
8268           dup = PETSC_TRUE;
8269           break;
8270         }
8271       }
8272       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8273       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8274       for (s = 0; s < supportSize; ++s) {
8275         if (support[s] == p) break;
8276       }
8277       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8278         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8279         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8280         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8281         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8282         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8283         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8284         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]);
8285         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8286       }
8287     }
8288     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8289     if (p != pp) {
8290       storagecheck = PETSC_FALSE;
8291       continue;
8292     }
8293     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8294     PetscCall(DMPlexGetSupport(dm, p, &support));
8295     for (s = 0; s < supportSize; ++s) {
8296       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8297       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8298       for (c = 0; c < coneSize; ++c) {
8299         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8300         if (cone[c] != pp) {
8301           c = 0;
8302           break;
8303         }
8304         if (cone[c] == p) break;
8305       }
8306       if (c >= coneSize) {
8307         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8308         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8309         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8310         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8311         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8312         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8313         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8314       }
8315     }
8316   }
8317   if (storagecheck) {
8318     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8319     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8320     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8321   }
8322   PetscFunctionReturn(0);
8323 }
8324 
8325 /*
8326   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.
8327 */
8328 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit) {
8329   DMPolytopeType  cct;
8330   PetscInt        ptpoints[4];
8331   const PetscInt *cone, *ccone, *ptcone;
8332   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8333 
8334   PetscFunctionBegin;
8335   *unsplit = 0;
8336   switch (ct) {
8337   case DM_POLYTOPE_POINT_PRISM_TENSOR: ptpoints[npt++] = c; break;
8338   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8339     PetscCall(DMPlexGetCone(dm, c, &cone));
8340     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8341     for (cp = 0; cp < coneSize; ++cp) {
8342       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8343       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8344     }
8345     break;
8346   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8347   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8348     PetscCall(DMPlexGetCone(dm, c, &cone));
8349     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8350     for (cp = 0; cp < coneSize; ++cp) {
8351       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8352       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8353       for (ccp = 0; ccp < cconeSize; ++ccp) {
8354         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8355         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8356           PetscInt p;
8357           for (p = 0; p < npt; ++p)
8358             if (ptpoints[p] == ccone[ccp]) break;
8359           if (p == npt) ptpoints[npt++] = ccone[ccp];
8360         }
8361       }
8362     }
8363     break;
8364   default: break;
8365   }
8366   for (pt = 0; pt < npt; ++pt) {
8367     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8368     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8369   }
8370   PetscFunctionReturn(0);
8371 }
8372 
8373 /*@
8374   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8375 
8376   Input Parameters:
8377 + dm - The DMPlex object
8378 - cellHeight - Normally 0
8379 
8380   Notes:
8381   This is a useful diagnostic when creating meshes programmatically.
8382   Currently applicable only to homogeneous simplex or tensor meshes.
8383 
8384   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8385 
8386   Level: developer
8387 
8388 .seealso: `DMCreate()`, `DMSetFromOptions()`
8389 @*/
8390 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight) {
8391   DMPlexInterpolatedFlag interp;
8392   DMPolytopeType         ct;
8393   PetscInt               vStart, vEnd, cStart, cEnd, c;
8394 
8395   PetscFunctionBegin;
8396   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8397   PetscCall(DMPlexIsInterpolated(dm, &interp));
8398   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8399   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8400   for (c = cStart; c < cEnd; ++c) {
8401     PetscInt *closure = NULL;
8402     PetscInt  coneSize, closureSize, cl, Nv = 0;
8403 
8404     PetscCall(DMPlexGetCellType(dm, c, &ct));
8405     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8406     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8407     if (interp == DMPLEX_INTERPOLATED_FULL) {
8408       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8409       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));
8410     }
8411     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8412     for (cl = 0; cl < closureSize * 2; cl += 2) {
8413       const PetscInt p = closure[cl];
8414       if ((p >= vStart) && (p < vEnd)) ++Nv;
8415     }
8416     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8417     /* Special Case: Tensor faces with identified vertices */
8418     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8419       PetscInt unsplit;
8420 
8421       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8422       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8423     }
8424     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));
8425   }
8426   PetscFunctionReturn(0);
8427 }
8428 
8429 /*@
8430   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8431 
8432   Collective
8433 
8434   Input Parameters:
8435 + dm - The DMPlex object
8436 - cellHeight - Normally 0
8437 
8438   Notes:
8439   This is a useful diagnostic when creating meshes programmatically.
8440   This routine is only relevant for meshes that are fully interpolated across all ranks.
8441   It will error out if a partially interpolated mesh is given on some rank.
8442   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8443 
8444   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8445 
8446   Level: developer
8447 
8448 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8449 @*/
8450 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight) {
8451   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8452   DMPlexInterpolatedFlag interpEnum;
8453 
8454   PetscFunctionBegin;
8455   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8456   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8457   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8458   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8459     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8460     PetscFunctionReturn(0);
8461   }
8462 
8463   PetscCall(DMGetDimension(dm, &dim));
8464   PetscCall(DMPlexGetDepth(dm, &depth));
8465   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8466   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8467     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8468     for (c = cStart; c < cEnd; ++c) {
8469       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8470       const DMPolytopeType *faceTypes;
8471       DMPolytopeType        ct;
8472       PetscInt              numFaces, coneSize, f;
8473       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8474 
8475       PetscCall(DMPlexGetCellType(dm, c, &ct));
8476       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8477       if (unsplit) continue;
8478       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8479       PetscCall(DMPlexGetCone(dm, c, &cone));
8480       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8481       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8482       for (cl = 0; cl < closureSize * 2; cl += 2) {
8483         const PetscInt p = closure[cl];
8484         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8485       }
8486       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8487       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);
8488       for (f = 0; f < numFaces; ++f) {
8489         DMPolytopeType fct;
8490         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8491 
8492         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8493         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8494         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8495           const PetscInt p = fclosure[cl];
8496           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8497         }
8498         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]);
8499         for (v = 0; v < fnumCorners; ++v) {
8500           if (fclosure[v] != faces[fOff + v]) {
8501             PetscInt v1;
8502 
8503             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8504             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8505             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8506             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8507             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8508             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]);
8509           }
8510         }
8511         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8512         fOff += faceSizes[f];
8513       }
8514       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8515       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8516     }
8517   }
8518   PetscFunctionReturn(0);
8519 }
8520 
8521 /*@
8522   DMPlexCheckGeometry - Check the geometry of mesh cells
8523 
8524   Input Parameter:
8525 . dm - The DMPlex object
8526 
8527   Notes:
8528   This is a useful diagnostic when creating meshes programmatically.
8529 
8530   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8531 
8532   Level: developer
8533 
8534 .seealso: `DMCreate()`, `DMSetFromOptions()`
8535 @*/
8536 PetscErrorCode DMPlexCheckGeometry(DM dm) {
8537   Vec       coordinates;
8538   PetscReal detJ, J[9], refVol = 1.0;
8539   PetscReal vol;
8540   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8541 
8542   PetscFunctionBegin;
8543   PetscCall(DMGetDimension(dm, &dim));
8544   PetscCall(DMGetCoordinateDim(dm, &dE));
8545   if (dim != dE) PetscFunctionReturn(0);
8546   PetscCall(DMPlexGetDepth(dm, &depth));
8547   for (d = 0; d < dim; ++d) refVol *= 2.0;
8548   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8549   /* Make sure local coordinates are created, because that step is collective */
8550   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8551   for (c = cStart; c < cEnd; ++c) {
8552     DMPolytopeType ct;
8553     PetscInt       unsplit;
8554     PetscBool      ignoreZeroVol = PETSC_FALSE;
8555 
8556     PetscCall(DMPlexGetCellType(dm, c, &ct));
8557     switch (ct) {
8558     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8559     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8560     case DM_POLYTOPE_QUAD_PRISM_TENSOR: ignoreZeroVol = PETSC_TRUE; break;
8561     default: break;
8562     }
8563     switch (ct) {
8564     case DM_POLYTOPE_TRI_PRISM:
8565     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8566     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8567     case DM_POLYTOPE_PYRAMID: continue;
8568     default: break;
8569     }
8570     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8571     if (unsplit) continue;
8572     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8573     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);
8574     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8575     /* This should work with periodicity since DG coordinates should be used */
8576     if (depth > 1) {
8577       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8578       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);
8579       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8580     }
8581   }
8582   PetscFunctionReturn(0);
8583 }
8584 
8585 /*@
8586   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8587 
8588   Collective
8589 
8590   Input Parameters:
8591 + dm - The DMPlex object
8592 - pointSF - The Point SF, or NULL for Point SF attached to DM
8593 
8594   Notes:
8595   This is mainly intended for debugging/testing purposes.
8596 
8597   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8598 
8599   Level: developer
8600 
8601 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8602 @*/
8603 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF) {
8604   PetscInt           l, nleaves, nroots, overlap;
8605   const PetscInt    *locals;
8606   const PetscSFNode *remotes;
8607   PetscBool          distributed;
8608   MPI_Comm           comm;
8609   PetscMPIInt        rank;
8610 
8611   PetscFunctionBegin;
8612   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8613   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8614   else pointSF = dm->sf;
8615   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8616   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8617   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8618   {
8619     PetscMPIInt mpiFlag;
8620 
8621     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
8622     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
8623   }
8624   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8625   PetscCall(DMPlexIsDistributed(dm, &distributed));
8626   if (!distributed) {
8627     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);
8628     PetscFunctionReturn(0);
8629   }
8630   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);
8631   PetscCall(DMPlexGetOverlap(dm, &overlap));
8632 
8633   /* Check SF graph is compatible with DMPlex chart */
8634   {
8635     PetscInt pStart, pEnd, maxLeaf;
8636 
8637     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8638     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8639     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
8640     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8641   }
8642 
8643   /* Check Point SF has no local points referenced */
8644   for (l = 0; l < nleaves; l++) {
8645     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);
8646   }
8647 
8648   /* Check there are no cells in interface */
8649   if (!overlap) {
8650     PetscInt cellHeight, cStart, cEnd;
8651 
8652     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8653     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8654     for (l = 0; l < nleaves; ++l) {
8655       const PetscInt point = locals ? locals[l] : l;
8656 
8657       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8658     }
8659   }
8660 
8661   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8662   {
8663     const PetscInt *rootdegree;
8664 
8665     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8666     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8667     for (l = 0; l < nleaves; ++l) {
8668       const PetscInt  point = locals ? locals[l] : l;
8669       const PetscInt *cone;
8670       PetscInt        coneSize, c, idx;
8671 
8672       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8673       PetscCall(DMPlexGetCone(dm, point, &cone));
8674       for (c = 0; c < coneSize; ++c) {
8675         if (!rootdegree[cone[c]]) {
8676           if (locals) {
8677             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8678           } else {
8679             idx = (cone[c] < nleaves) ? cone[c] : -1;
8680           }
8681           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8682         }
8683       }
8684     }
8685   }
8686   PetscFunctionReturn(0);
8687 }
8688 
8689 /*@
8690   DMPlexCheck - Perform various checks of Plex sanity
8691 
8692   Input Parameter:
8693 . dm - The DMPlex object
8694 
8695   Notes:
8696   This is a useful diagnostic when creating meshes programmatically.
8697 
8698   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8699 
8700   Currently does not include DMPlexCheckCellShape().
8701 
8702   Level: developer
8703 
8704 .seealso: DMCreate(), DMSetFromOptions()
8705 @*/
8706 PetscErrorCode DMPlexCheck(DM dm) {
8707   PetscInt cellHeight;
8708 
8709   PetscFunctionBegin;
8710   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8711   PetscCall(DMPlexCheckSymmetry(dm));
8712   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8713   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8714   PetscCall(DMPlexCheckGeometry(dm));
8715   PetscCall(DMPlexCheckPointSF(dm, NULL));
8716   PetscCall(DMPlexCheckInterfaceCones(dm));
8717   PetscFunctionReturn(0);
8718 }
8719 
8720 typedef struct cell_stats {
8721   PetscReal min, max, sum, squaresum;
8722   PetscInt  count;
8723 } cell_stats_t;
8724 
8725 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype) {
8726   PetscInt i, N = *len;
8727 
8728   for (i = 0; i < N; i++) {
8729     cell_stats_t *A = (cell_stats_t *)a;
8730     cell_stats_t *B = (cell_stats_t *)b;
8731 
8732     B->min = PetscMin(A->min, B->min);
8733     B->max = PetscMax(A->max, B->max);
8734     B->sum += A->sum;
8735     B->squaresum += A->squaresum;
8736     B->count += A->count;
8737   }
8738 }
8739 
8740 /*@
8741   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8742 
8743   Collective on dm
8744 
8745   Input Parameters:
8746 + dm        - The DMPlex object
8747 . output    - If true, statistics will be displayed on stdout
8748 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8749 
8750   Notes:
8751   This is mainly intended for debugging/testing purposes.
8752 
8753   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8754 
8755   Level: developer
8756 
8757 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8758 @*/
8759 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit) {
8760   DM           dmCoarse;
8761   cell_stats_t stats, globalStats;
8762   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
8763   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8764   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8765   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8766   PetscMPIInt  rank, size;
8767 
8768   PetscFunctionBegin;
8769   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8770   stats.min = PETSC_MAX_REAL;
8771   stats.max = PETSC_MIN_REAL;
8772   stats.sum = stats.squaresum = 0.;
8773   stats.count                 = 0;
8774 
8775   PetscCallMPI(MPI_Comm_size(comm, &size));
8776   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8777   PetscCall(DMGetCoordinateDim(dm, &cdim));
8778   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8779   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
8780   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8781   for (c = cStart; c < cEnd; c++) {
8782     PetscInt  i;
8783     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8784 
8785     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
8786     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8787     for (i = 0; i < PetscSqr(cdim); ++i) {
8788       frobJ += J[i] * J[i];
8789       frobInvJ += invJ[i] * invJ[i];
8790     }
8791     cond2 = frobJ * frobInvJ;
8792     cond  = PetscSqrtReal(cond2);
8793 
8794     stats.min = PetscMin(stats.min, cond);
8795     stats.max = PetscMax(stats.max, cond);
8796     stats.sum += cond;
8797     stats.squaresum += cond2;
8798     stats.count++;
8799     if (output && cond > limit) {
8800       PetscSection coordSection;
8801       Vec          coordsLocal;
8802       PetscScalar *coords = NULL;
8803       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8804 
8805       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8806       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8807       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8808       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
8809       for (i = 0; i < Nv / cdim; ++i) {
8810         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8811         for (d = 0; d < cdim; ++d) {
8812           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8813           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
8814         }
8815         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8816       }
8817       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8818       for (cl = 0; cl < clSize * 2; cl += 2) {
8819         const PetscInt edge = closure[cl];
8820 
8821         if ((edge >= eStart) && (edge < eEnd)) {
8822           PetscReal len;
8823 
8824           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8825           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
8826         }
8827       }
8828       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8829       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8830     }
8831   }
8832   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8833 
8834   if (size > 1) {
8835     PetscMPIInt  blockLengths[2] = {4, 1};
8836     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
8837     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
8838     MPI_Op       statReduce;
8839 
8840     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
8841     PetscCallMPI(MPI_Type_commit(&statType));
8842     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8843     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
8844     PetscCallMPI(MPI_Op_free(&statReduce));
8845     PetscCallMPI(MPI_Type_free(&statType));
8846   } else {
8847     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
8848   }
8849   if (rank == 0) {
8850     count = globalStats.count;
8851     min   = globalStats.min;
8852     max   = globalStats.max;
8853     mean  = globalStats.sum / globalStats.count;
8854     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
8855   }
8856 
8857   if (output) 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));
8858   PetscCall(PetscFree2(J, invJ));
8859 
8860   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
8861   if (dmCoarse) {
8862     PetscBool isplex;
8863 
8864     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
8865     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
8866   }
8867   PetscFunctionReturn(0);
8868 }
8869 
8870 /*@
8871   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8872   orthogonal quality below given tolerance.
8873 
8874   Collective on dm
8875 
8876   Input Parameters:
8877 + dm   - The DMPlex object
8878 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8879 - atol - [0, 1] Absolute tolerance for tagging cells.
8880 
8881   Output Parameters:
8882 + OrthQual      - Vec containing orthogonal quality per cell
8883 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8884 
8885   Options Database Keys:
8886 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8887 supported.
8888 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8889 
8890   Notes:
8891   Orthogonal quality is given by the following formula:
8892 
8893   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8894 
8895   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
8896   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8897   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8898   calculating the cosine of the angle between these vectors.
8899 
8900   Orthogonal quality ranges from 1 (best) to 0 (worst).
8901 
8902   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8903   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8904 
8905   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8906 
8907   Level: intermediate
8908 
8909 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8910 @*/
8911 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel) {
8912   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8913   PetscInt              *idx;
8914   PetscScalar           *oqVals;
8915   const PetscScalar     *cellGeomArr, *faceGeomArr;
8916   PetscReal             *ci, *fi, *Ai;
8917   MPI_Comm               comm;
8918   Vec                    cellgeom, facegeom;
8919   DM                     dmFace, dmCell;
8920   IS                     glob;
8921   ISLocalToGlobalMapping ltog;
8922   PetscViewer            vwr;
8923 
8924   PetscFunctionBegin;
8925   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8926   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
8927   PetscValidPointer(OrthQual, 4);
8928   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
8929   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8930   PetscCall(DMGetDimension(dm, &nc));
8931   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
8932   {
8933     DMPlexInterpolatedFlag interpFlag;
8934 
8935     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
8936     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
8937       PetscMPIInt rank;
8938 
8939       PetscCallMPI(MPI_Comm_rank(comm, &rank));
8940       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
8941     }
8942   }
8943   if (OrthQualLabel) {
8944     PetscValidPointer(OrthQualLabel, 5);
8945     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
8946     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
8947   } else {
8948     *OrthQualLabel = NULL;
8949   }
8950   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8951   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8952   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
8953   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
8954   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
8955   PetscCall(VecCreate(comm, OrthQual));
8956   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
8957   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
8958   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
8959   PetscCall(VecSetUp(*OrthQual));
8960   PetscCall(ISDestroy(&glob));
8961   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
8962   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
8963   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
8964   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
8965   PetscCall(VecGetDM(cellgeom, &dmCell));
8966   PetscCall(VecGetDM(facegeom, &dmFace));
8967   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
8968   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
8969     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
8970     PetscInt         cellarr[2], *adj = NULL;
8971     PetscScalar     *cArr, *fArr;
8972     PetscReal        minvalc = 1.0, minvalf = 1.0;
8973     PetscFVCellGeom *cg;
8974 
8975     idx[cellIter] = cell - cStart;
8976     cellarr[0]    = cell;
8977     /* Make indexing into cellGeom easier */
8978     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
8979     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
8980     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
8981     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
8982     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
8983       PetscInt         i;
8984       const PetscInt   neigh  = adj[cellneigh];
8985       PetscReal        normci = 0, normfi = 0, normai = 0;
8986       PetscFVCellGeom *cgneigh;
8987       PetscFVFaceGeom *fg;
8988 
8989       /* Don't count ourselves in the neighbor list */
8990       if (neigh == cell) continue;
8991       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
8992       cellarr[1] = neigh;
8993       {
8994         PetscInt        numcovpts;
8995         const PetscInt *covpts;
8996 
8997         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
8998         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
8999         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9000       }
9001 
9002       /* Compute c_i, f_i and their norms */
9003       for (i = 0; i < nc; i++) {
9004         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9005         fi[i] = fg->centroid[i] - cg->centroid[i];
9006         Ai[i] = fg->normal[i];
9007         normci += PetscPowReal(ci[i], 2);
9008         normfi += PetscPowReal(fi[i], 2);
9009         normai += PetscPowReal(Ai[i], 2);
9010       }
9011       normci = PetscSqrtReal(normci);
9012       normfi = PetscSqrtReal(normfi);
9013       normai = PetscSqrtReal(normai);
9014 
9015       /* Normalize and compute for each face-cell-normal pair */
9016       for (i = 0; i < nc; i++) {
9017         ci[i] = ci[i] / normci;
9018         fi[i] = fi[i] / normfi;
9019         Ai[i] = Ai[i] / normai;
9020         /* PetscAbs because I don't know if normals are guaranteed to point out */
9021         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9022         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9023       }
9024       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9025       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9026     }
9027     PetscCall(PetscFree(adj));
9028     PetscCall(PetscFree2(cArr, fArr));
9029     /* Defer to cell if they're equal */
9030     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9031     if (OrthQualLabel) {
9032       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9033     }
9034   }
9035   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9036   PetscCall(VecAssemblyBegin(*OrthQual));
9037   PetscCall(VecAssemblyEnd(*OrthQual));
9038   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9039   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9040   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9041   if (OrthQualLabel) {
9042     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9043   }
9044   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9045   PetscCall(PetscViewerDestroy(&vwr));
9046   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9047   PetscFunctionReturn(0);
9048 }
9049 
9050 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9051  * interpolator construction */
9052 static PetscErrorCode DMGetFullDM(DM dm, DM *odm) {
9053   PetscSection section, newSection, gsection;
9054   PetscSF      sf;
9055   PetscBool    hasConstraints, ghasConstraints;
9056 
9057   PetscFunctionBegin;
9058   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9059   PetscValidPointer(odm, 2);
9060   PetscCall(DMGetLocalSection(dm, &section));
9061   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9062   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9063   if (!ghasConstraints) {
9064     PetscCall(PetscObjectReference((PetscObject)dm));
9065     *odm = dm;
9066     PetscFunctionReturn(0);
9067   }
9068   PetscCall(DMClone(dm, odm));
9069   PetscCall(DMCopyFields(dm, *odm));
9070   PetscCall(DMGetLocalSection(*odm, &newSection));
9071   PetscCall(DMGetPointSF(*odm, &sf));
9072   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9073   PetscCall(DMSetGlobalSection(*odm, gsection));
9074   PetscCall(PetscSectionDestroy(&gsection));
9075   PetscFunctionReturn(0);
9076 }
9077 
9078 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift) {
9079   DM        dmco, dmfo;
9080   Mat       interpo;
9081   Vec       rscale;
9082   Vec       cglobalo, clocal;
9083   Vec       fglobal, fglobalo, flocal;
9084   PetscBool regular;
9085 
9086   PetscFunctionBegin;
9087   PetscCall(DMGetFullDM(dmc, &dmco));
9088   PetscCall(DMGetFullDM(dmf, &dmfo));
9089   PetscCall(DMSetCoarseDM(dmfo, dmco));
9090   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9091   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9092   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9093   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9094   PetscCall(DMCreateLocalVector(dmc, &clocal));
9095   PetscCall(VecSet(cglobalo, 0.));
9096   PetscCall(VecSet(clocal, 0.));
9097   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9098   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9099   PetscCall(DMCreateLocalVector(dmf, &flocal));
9100   PetscCall(VecSet(fglobal, 0.));
9101   PetscCall(VecSet(fglobalo, 0.));
9102   PetscCall(VecSet(flocal, 0.));
9103   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9104   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9105   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9106   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9107   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9108   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9109   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9110   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9111   *shift = fglobal;
9112   PetscCall(VecDestroy(&flocal));
9113   PetscCall(VecDestroy(&fglobalo));
9114   PetscCall(VecDestroy(&clocal));
9115   PetscCall(VecDestroy(&cglobalo));
9116   PetscCall(VecDestroy(&rscale));
9117   PetscCall(MatDestroy(&interpo));
9118   PetscCall(DMDestroy(&dmfo));
9119   PetscCall(DMDestroy(&dmco));
9120   PetscFunctionReturn(0);
9121 }
9122 
9123 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol) {
9124   PetscObject shifto;
9125   Vec         shift;
9126 
9127   PetscFunctionBegin;
9128   if (!interp) {
9129     Vec rscale;
9130 
9131     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9132     PetscCall(VecDestroy(&rscale));
9133   } else {
9134     PetscCall(PetscObjectReference((PetscObject)interp));
9135   }
9136   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9137   if (!shifto) {
9138     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9139     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9140     shifto = (PetscObject)shift;
9141     PetscCall(VecDestroy(&shift));
9142   }
9143   shift = (Vec)shifto;
9144   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9145   PetscCall(VecAXPY(fineSol, 1.0, shift));
9146   PetscCall(MatDestroy(&interp));
9147   PetscFunctionReturn(0);
9148 }
9149 
9150 /* Pointwise interpolation
9151      Just code FEM for now
9152      u^f = I u^c
9153      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9154      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9155      I_{ij} = psi^f_i phi^c_j
9156 */
9157 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling) {
9158   PetscSection gsc, gsf;
9159   PetscInt     m, n;
9160   void        *ctx;
9161   DM           cdm;
9162   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9163 
9164   PetscFunctionBegin;
9165   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9166   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9167   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9168   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9169 
9170   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9171   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9172   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9173   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9174   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9175 
9176   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9177   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9178   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9179   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9180   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9181   if (scaling) {
9182     /* Use naive scaling */
9183     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9184   }
9185   PetscFunctionReturn(0);
9186 }
9187 
9188 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat) {
9189   VecScatter ctx;
9190 
9191   PetscFunctionBegin;
9192   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9193   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9194   PetscCall(VecScatterDestroy(&ctx));
9195   PetscFunctionReturn(0);
9196 }
9197 
9198 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[]) {
9199   const PetscInt Nc = uOff[1] - uOff[0];
9200   PetscInt       c;
9201   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9202 }
9203 
9204 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass) {
9205   DM           dmc;
9206   PetscDS      ds;
9207   Vec          ones, locmass;
9208   IS           cellIS;
9209   PetscFormKey key;
9210   PetscInt     depth;
9211 
9212   PetscFunctionBegin;
9213   PetscCall(DMClone(dm, &dmc));
9214   PetscCall(DMCopyDisc(dm, dmc));
9215   PetscCall(DMGetDS(dmc, &ds));
9216   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9217   PetscCall(DMCreateGlobalVector(dmc, mass));
9218   PetscCall(DMGetLocalVector(dmc, &ones));
9219   PetscCall(DMGetLocalVector(dmc, &locmass));
9220   PetscCall(DMPlexGetDepth(dmc, &depth));
9221   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9222   PetscCall(VecSet(locmass, 0.0));
9223   PetscCall(VecSet(ones, 1.0));
9224   key.label = NULL;
9225   key.value = 0;
9226   key.field = 0;
9227   key.part  = 0;
9228   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9229   PetscCall(ISDestroy(&cellIS));
9230   PetscCall(VecSet(*mass, 0.0));
9231   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9232   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9233   PetscCall(DMRestoreLocalVector(dmc, &ones));
9234   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9235   PetscCall(DMDestroy(&dmc));
9236   PetscFunctionReturn(0);
9237 }
9238 
9239 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass) {
9240   PetscSection gsc, gsf;
9241   PetscInt     m, n;
9242   void        *ctx;
9243   DM           cdm;
9244   PetscBool    regular;
9245 
9246   PetscFunctionBegin;
9247   if (dmFine == dmCoarse) {
9248     DM            dmc;
9249     PetscDS       ds;
9250     PetscWeakForm wf;
9251     Vec           u;
9252     IS            cellIS;
9253     PetscFormKey  key;
9254     PetscInt      depth;
9255 
9256     PetscCall(DMClone(dmFine, &dmc));
9257     PetscCall(DMCopyDisc(dmFine, dmc));
9258     PetscCall(DMGetDS(dmc, &ds));
9259     PetscCall(PetscDSGetWeakForm(ds, &wf));
9260     PetscCall(PetscWeakFormClear(wf));
9261     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9262     PetscCall(DMCreateMatrix(dmc, mass));
9263     PetscCall(DMGetLocalVector(dmc, &u));
9264     PetscCall(DMPlexGetDepth(dmc, &depth));
9265     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9266     PetscCall(MatZeroEntries(*mass));
9267     key.label = NULL;
9268     key.value = 0;
9269     key.field = 0;
9270     key.part  = 0;
9271     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9272     PetscCall(ISDestroy(&cellIS));
9273     PetscCall(DMRestoreLocalVector(dmc, &u));
9274     PetscCall(DMDestroy(&dmc));
9275   } else {
9276     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9277     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9278     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9279     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9280 
9281     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9282     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9283     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9284     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9285 
9286     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9287     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9288     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9289     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9290   }
9291   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9292   PetscFunctionReturn(0);
9293 }
9294 
9295 /*@
9296   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9297 
9298   Input Parameter:
9299 . dm - The DMPlex object
9300 
9301   Output Parameter:
9302 . regular - The flag
9303 
9304   Level: intermediate
9305 
9306 .seealso: `DMPlexSetRegularRefinement()`
9307 @*/
9308 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular) {
9309   PetscFunctionBegin;
9310   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9311   PetscValidBoolPointer(regular, 2);
9312   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9313   PetscFunctionReturn(0);
9314 }
9315 
9316 /*@
9317   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9318 
9319   Input Parameters:
9320 + dm - The DMPlex object
9321 - regular - The flag
9322 
9323   Level: intermediate
9324 
9325 .seealso: `DMPlexGetRegularRefinement()`
9326 @*/
9327 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular) {
9328   PetscFunctionBegin;
9329   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9330   ((DM_Plex *)dm->data)->regularRefinement = regular;
9331   PetscFunctionReturn(0);
9332 }
9333 
9334 /* anchors */
9335 /*@
9336   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9337   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9338 
9339   not collective
9340 
9341   Input Parameter:
9342 . dm - The DMPlex object
9343 
9344   Output Parameters:
9345 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9346 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9347 
9348   Level: intermediate
9349 
9350 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9351 @*/
9352 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS) {
9353   DM_Plex *plex = (DM_Plex *)dm->data;
9354 
9355   PetscFunctionBegin;
9356   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9357   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9358   if (anchorSection) *anchorSection = plex->anchorSection;
9359   if (anchorIS) *anchorIS = plex->anchorIS;
9360   PetscFunctionReturn(0);
9361 }
9362 
9363 /*@
9364   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9365   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9366   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9367 
9368   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9369   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9370 
9371   collective on dm
9372 
9373   Input Parameters:
9374 + dm - The DMPlex object
9375 . 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).
9376 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9377 
9378   The reference counts of anchorSection and anchorIS are incremented.
9379 
9380   Level: intermediate
9381 
9382 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9383 @*/
9384 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS) {
9385   DM_Plex    *plex = (DM_Plex *)dm->data;
9386   PetscMPIInt result;
9387 
9388   PetscFunctionBegin;
9389   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9390   if (anchorSection) {
9391     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9392     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9393     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9394   }
9395   if (anchorIS) {
9396     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9397     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9398     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9399   }
9400 
9401   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9402   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9403   plex->anchorSection = anchorSection;
9404 
9405   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9406   PetscCall(ISDestroy(&plex->anchorIS));
9407   plex->anchorIS = anchorIS;
9408 
9409   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9410     PetscInt        size, a, pStart, pEnd;
9411     const PetscInt *anchors;
9412 
9413     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9414     PetscCall(ISGetLocalSize(anchorIS, &size));
9415     PetscCall(ISGetIndices(anchorIS, &anchors));
9416     for (a = 0; a < size; a++) {
9417       PetscInt p;
9418 
9419       p = anchors[a];
9420       if (p >= pStart && p < pEnd) {
9421         PetscInt dof;
9422 
9423         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9424         if (dof) {
9425           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9426           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9427         }
9428       }
9429     }
9430     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9431   }
9432   /* reset the generic constraints */
9433   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9434   PetscFunctionReturn(0);
9435 }
9436 
9437 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec) {
9438   PetscSection anchorSection;
9439   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9440 
9441   PetscFunctionBegin;
9442   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9443   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9444   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9445   PetscCall(PetscSectionGetNumFields(section, &numFields));
9446   if (numFields) {
9447     PetscInt f;
9448     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9449 
9450     for (f = 0; f < numFields; f++) {
9451       PetscInt numComp;
9452 
9453       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9454       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9455     }
9456   }
9457   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9458   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9459   pStart = PetscMax(pStart, sStart);
9460   pEnd   = PetscMin(pEnd, sEnd);
9461   pEnd   = PetscMax(pStart, pEnd);
9462   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9463   for (p = pStart; p < pEnd; p++) {
9464     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9465     if (dof) {
9466       PetscCall(PetscSectionGetDof(section, p, &dof));
9467       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9468       for (f = 0; f < numFields; f++) {
9469         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9470         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9471       }
9472     }
9473   }
9474   PetscCall(PetscSectionSetUp(*cSec));
9475   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9476   PetscFunctionReturn(0);
9477 }
9478 
9479 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat) {
9480   PetscSection    aSec;
9481   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9482   const PetscInt *anchors;
9483   PetscInt        numFields, f;
9484   IS              aIS;
9485   MatType         mtype;
9486   PetscBool       iscuda, iskokkos;
9487 
9488   PetscFunctionBegin;
9489   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9490   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9491   PetscCall(PetscSectionGetStorageSize(section, &n));
9492   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9493   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9494   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9495   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9496   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9497   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9498   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9499   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9500   else mtype = MATSEQAIJ;
9501   PetscCall(MatSetType(*cMat, mtype));
9502   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9503   PetscCall(ISGetIndices(aIS, &anchors));
9504   /* cSec will be a subset of aSec and section */
9505   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9506   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9507   PetscCall(PetscMalloc1(m + 1, &i));
9508   i[0] = 0;
9509   PetscCall(PetscSectionGetNumFields(section, &numFields));
9510   for (p = pStart; p < pEnd; p++) {
9511     PetscInt rDof, rOff, r;
9512 
9513     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9514     if (!rDof) continue;
9515     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9516     if (numFields) {
9517       for (f = 0; f < numFields; f++) {
9518         annz = 0;
9519         for (r = 0; r < rDof; r++) {
9520           a = anchors[rOff + r];
9521           if (a < sStart || a >= sEnd) continue;
9522           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9523           annz += aDof;
9524         }
9525         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9526         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9527         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9528       }
9529     } else {
9530       annz = 0;
9531       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9532       for (q = 0; q < dof; q++) {
9533         a = anchors[rOff + q];
9534         if (a < sStart || a >= sEnd) continue;
9535         PetscCall(PetscSectionGetDof(section, a, &aDof));
9536         annz += aDof;
9537       }
9538       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9539       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9540       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9541     }
9542   }
9543   nnz = i[m];
9544   PetscCall(PetscMalloc1(nnz, &j));
9545   offset = 0;
9546   for (p = pStart; p < pEnd; p++) {
9547     if (numFields) {
9548       for (f = 0; f < numFields; f++) {
9549         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9550         for (q = 0; q < dof; q++) {
9551           PetscInt rDof, rOff, r;
9552           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9553           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9554           for (r = 0; r < rDof; r++) {
9555             PetscInt s;
9556 
9557             a = anchors[rOff + r];
9558             if (a < sStart || a >= sEnd) continue;
9559             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9560             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9561             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9562           }
9563         }
9564       }
9565     } else {
9566       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9567       for (q = 0; q < dof; q++) {
9568         PetscInt rDof, rOff, r;
9569         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9570         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9571         for (r = 0; r < rDof; r++) {
9572           PetscInt s;
9573 
9574           a = anchors[rOff + r];
9575           if (a < sStart || a >= sEnd) continue;
9576           PetscCall(PetscSectionGetDof(section, a, &aDof));
9577           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9578           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9579         }
9580       }
9581     }
9582   }
9583   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9584   PetscCall(PetscFree(i));
9585   PetscCall(PetscFree(j));
9586   PetscCall(ISRestoreIndices(aIS, &anchors));
9587   PetscFunctionReturn(0);
9588 }
9589 
9590 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm) {
9591   DM_Plex     *plex = (DM_Plex *)dm->data;
9592   PetscSection anchorSection, section, cSec;
9593   Mat          cMat;
9594 
9595   PetscFunctionBegin;
9596   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9597   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9598   if (anchorSection) {
9599     PetscInt Nf;
9600 
9601     PetscCall(DMGetLocalSection(dm, &section));
9602     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
9603     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
9604     PetscCall(DMGetNumFields(dm, &Nf));
9605     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
9606     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
9607     PetscCall(PetscSectionDestroy(&cSec));
9608     PetscCall(MatDestroy(&cMat));
9609   }
9610   PetscFunctionReturn(0);
9611 }
9612 
9613 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm) {
9614   IS           subis;
9615   PetscSection section, subsection;
9616 
9617   PetscFunctionBegin;
9618   PetscCall(DMGetLocalSection(dm, &section));
9619   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9620   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9621   /* Create subdomain */
9622   PetscCall(DMPlexFilter(dm, label, value, subdm));
9623   /* Create submodel */
9624   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9625   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9626   PetscCall(DMSetLocalSection(*subdm, subsection));
9627   PetscCall(PetscSectionDestroy(&subsection));
9628   PetscCall(DMCopyDisc(dm, *subdm));
9629   /* Create map from submodel to global model */
9630   if (is) {
9631     PetscSection    sectionGlobal, subsectionGlobal;
9632     IS              spIS;
9633     const PetscInt *spmap;
9634     PetscInt       *subIndices;
9635     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9636     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9637 
9638     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9639     PetscCall(ISGetIndices(spIS, &spmap));
9640     PetscCall(PetscSectionGetNumFields(section, &Nf));
9641     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9642     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9643     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9644     for (p = pStart; p < pEnd; ++p) {
9645       PetscInt gdof, pSubSize = 0;
9646 
9647       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9648       if (gdof > 0) {
9649         for (f = 0; f < Nf; ++f) {
9650           PetscInt fdof, fcdof;
9651 
9652           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9653           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9654           pSubSize += fdof - fcdof;
9655         }
9656         subSize += pSubSize;
9657         if (pSubSize) {
9658           if (bs < 0) {
9659             bs = pSubSize;
9660           } else if (bs != pSubSize) {
9661             /* Layout does not admit a pointwise block size */
9662             bs = 1;
9663           }
9664         }
9665       }
9666     }
9667     /* Must have same blocksize on all procs (some might have no points) */
9668     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
9669     bsLocal[1] = bs;
9670     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
9671     if (bsMinMax[0] != bsMinMax[1]) {
9672       bs = 1;
9673     } else {
9674       bs = bsMinMax[0];
9675     }
9676     PetscCall(PetscMalloc1(subSize, &subIndices));
9677     for (p = pStart; p < pEnd; ++p) {
9678       PetscInt gdof, goff;
9679 
9680       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9681       if (gdof > 0) {
9682         const PetscInt point = spmap[p];
9683 
9684         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9685         for (f = 0; f < Nf; ++f) {
9686           PetscInt fdof, fcdof, fc, f2, poff = 0;
9687 
9688           /* Can get rid of this loop by storing field information in the global section */
9689           for (f2 = 0; f2 < f; ++f2) {
9690             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9691             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9692             poff += fdof - fcdof;
9693           }
9694           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9695           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9696           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
9697         }
9698       }
9699     }
9700     PetscCall(ISRestoreIndices(spIS, &spmap));
9701     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9702     if (bs > 1) {
9703       /* We need to check that the block size does not come from non-contiguous fields */
9704       PetscInt i, j, set = 1;
9705       for (i = 0; i < subSize; i += bs) {
9706         for (j = 0; j < bs; ++j) {
9707           if (subIndices[i + j] != subIndices[i] + j) {
9708             set = 0;
9709             break;
9710           }
9711         }
9712       }
9713       if (set) PetscCall(ISSetBlockSize(*is, bs));
9714     }
9715     /* Attach nullspace */
9716     for (f = 0; f < Nf; ++f) {
9717       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9718       if ((*subdm)->nullspaceConstructors[f]) break;
9719     }
9720     if (f < Nf) {
9721       MatNullSpace nullSpace;
9722       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9723 
9724       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
9725       PetscCall(MatNullSpaceDestroy(&nullSpace));
9726     }
9727   }
9728   PetscFunctionReturn(0);
9729 }
9730 
9731 /*@
9732   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9733 
9734   Input Parameter:
9735 - dm - The DM
9736 
9737   Level: developer
9738 
9739   Options Database Keys:
9740 . -dm_plex_monitor_throughput - Activate the monitor
9741 
9742 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9743 @*/
9744 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy) {
9745 #if defined(PETSC_USE_LOG)
9746   PetscStageLog      stageLog;
9747   PetscLogEvent      event;
9748   PetscLogStage      stage;
9749   PetscEventPerfInfo eventInfo;
9750   PetscReal          cellRate, flopRate;
9751   PetscInt           cStart, cEnd, Nf, N;
9752   const char        *name;
9753 #endif
9754 
9755   PetscFunctionBegin;
9756   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9757 #if defined(PETSC_USE_LOG)
9758   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
9759   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9760   PetscCall(DMGetNumFields(dm, &Nf));
9761   PetscCall(PetscLogGetStageLog(&stageLog));
9762   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9763   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9764   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9765   N        = (cEnd - cStart) * Nf * eventInfo.count;
9766   flopRate = eventInfo.flops / eventInfo.time;
9767   cellRate = N / eventInfo.time;
9768   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)));
9769 #else
9770   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9771 #endif
9772   PetscFunctionReturn(0);
9773 }
9774