xref: /petsc/src/dm/impls/plex/plex.c (revision a8d3b578d34eb8d5be1b3eaad4a98fb37a558ece)
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   Level: intermediate
28 
29   Note:
30   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
31   If the mesh has no cells, this returns `PETSC_FALSE`.
32 
33 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
34 @*/
35 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
36 {
37   DMPolytopeType ct;
38   PetscInt       cStart, cEnd;
39 
40   PetscFunctionBegin;
41   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
42   if (cEnd <= cStart) {
43     *simplex = PETSC_FALSE;
44     PetscFunctionReturn(0);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(0);
49 }
50 
51 /*@
52   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
53 
54   Input Parameters:
55 + dm     - The `DMPLEX` object
56 - height - The cell height in the Plex, 0 is the default
57 
58   Output Parameters:
59 + cStart - The first "normal" cell
60 - cEnd   - The upper bound on "normal"" cells
61 
62   Level: developer
63 
64   Note:
65   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
66 
67 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
68 @*/
69 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
70 {
71   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
72   PetscInt       cS, cE, c;
73 
74   PetscFunctionBegin;
75   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
76   for (c = cS; c < cE; ++c) {
77     DMPolytopeType cct;
78 
79     PetscCall(DMPlexGetCellType(dm, c, &cct));
80     if ((PetscInt)cct < 0) break;
81     switch (cct) {
82     case DM_POLYTOPE_POINT:
83     case DM_POLYTOPE_SEGMENT:
84     case DM_POLYTOPE_TRIANGLE:
85     case DM_POLYTOPE_QUADRILATERAL:
86     case DM_POLYTOPE_TETRAHEDRON:
87     case DM_POLYTOPE_HEXAHEDRON:
88       ct = cct;
89       break;
90     default:
91       break;
92     }
93     if (ct != DM_POLYTOPE_UNKNOWN) break;
94   }
95   if (ct != DM_POLYTOPE_UNKNOWN) {
96     DMLabel ctLabel;
97 
98     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
99     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
100     // Reset label for fast lookup
101     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
102   }
103   if (cStart) *cStart = cS;
104   if (cEnd) *cEnd = cE;
105   PetscFunctionReturn(0);
106 }
107 
108 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
109 {
110   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
111   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
112 
113   PetscFunctionBegin;
114   *ft = PETSC_VTK_INVALID;
115   PetscCall(DMGetCoordinateDim(dm, &cdim));
116   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
117   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
118   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
119   if (field >= 0) {
120     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
121     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
122   } else {
123     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
124     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
125   }
126   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(0);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective on dm
154 
155   Input Parameters:
156 + dm - The `DMPLEX` object
157 . n  - The number of vectors
158 . u  - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(0);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrcpy(tmpname, vname));
202         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
203         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(0);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(0);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *coords, *array;
253   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(VecGetArrayRead(coordinates, &coords));
278   for (c = 0; c < N; c += dim) {
279     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
280     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
281     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
282     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
283   }
284   PetscCall(VecRestoreArrayRead(coordinates, &coords));
285   PetscCall(PetscDrawClear(draw));
286 
287   /* Could implement something like DMDASelectFields() */
288   for (f = 0; f < Nf; ++f) {
289     DM          fdm = dm;
290     Vec         fv  = v;
291     IS          fis;
292     char        prefix[PETSC_MAX_PATH_LEN];
293     const char *fname;
294 
295     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
296     PetscCall(PetscSectionGetFieldName(s, f, &fname));
297 
298     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
299     else prefix[0] = '\0';
300     if (Nf > 1) {
301       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
302       PetscCall(VecGetSubVector(v, fis, &fv));
303       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
304       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
305     }
306     for (comp = 0; comp < Nc; ++comp, ++w) {
307       PetscInt nmax = 2;
308 
309       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
310       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
311       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
312       PetscCall(PetscDrawSetTitle(draw, title));
313 
314       /* TODO Get max and min only for this component */
315       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
316       if (!flg) {
317         PetscCall(VecMin(fv, NULL, &vbound[0]));
318         PetscCall(VecMax(fv, NULL, &vbound[1]));
319         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
320       }
321       PetscCall(PetscDrawGetPopup(draw, &popup));
322       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
323       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
324 
325       PetscCall(VecGetArrayRead(fv, &array));
326       for (c = cStart; c < cEnd; ++c) {
327         PetscScalar *coords = NULL, *a   = NULL;
328         PetscInt     numCoords, color[4] = {-1, -1, -1, -1};
329 
330         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
331         if (a) {
332           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
333           color[1] = color[2] = color[3] = color[0];
334         } else {
335           PetscScalar *vals = NULL;
336           PetscInt     numVals, va;
337 
338           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
339           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);
340           switch (numVals / Nc) {
341           case 3: /* P1 Triangle */
342           case 4: /* P1 Quadrangle */
343             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
344             break;
345           case 6: /* P2 Triangle */
346           case 8: /* P2 Quadrangle */
347             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
348             break;
349           default:
350             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
351           }
352           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
353         }
354         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
355         switch (numCoords) {
356         case 6:
357         case 12: /* Localized triangle */
358           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]));
359           break;
360         case 8:
361         case 16: /* Localized quadrilateral */
362           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]));
363           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]));
364           break;
365         default:
366           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
367         }
368         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
369       }
370       PetscCall(VecRestoreArrayRead(fv, &array));
371       PetscCall(PetscDrawFlush(draw));
372       PetscCall(PetscDrawPause(draw));
373       PetscCall(PetscDrawSave(draw));
374     }
375     if (Nf > 1) {
376       PetscCall(VecRestoreSubVector(v, fis, &fv));
377       PetscCall(ISDestroy(&fis));
378       PetscCall(DMDestroy(&fdm));
379     }
380   }
381   PetscFunctionReturn(0);
382 }
383 
384 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
385 {
386   DM        dm;
387   PetscDraw draw;
388   PetscInt  dim;
389   PetscBool isnull;
390 
391   PetscFunctionBegin;
392   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
393   PetscCall(PetscDrawIsNull(draw, &isnull));
394   if (isnull) PetscFunctionReturn(0);
395 
396   PetscCall(VecGetDM(v, &dm));
397   PetscCall(DMGetCoordinateDim(dm, &dim));
398   switch (dim) {
399   case 1:
400     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
401     break;
402   case 2:
403     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
404     break;
405   default:
406     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
407   }
408   PetscFunctionReturn(0);
409 }
410 
411 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
412 {
413   DM                      dm;
414   Vec                     locv;
415   const char             *name;
416   PetscSection            section;
417   PetscInt                pStart, pEnd;
418   PetscInt                numFields;
419   PetscViewerVTKFieldType ft;
420 
421   PetscFunctionBegin;
422   PetscCall(VecGetDM(v, &dm));
423   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
424   PetscCall(PetscObjectGetName((PetscObject)v, &name));
425   PetscCall(PetscObjectSetName((PetscObject)locv, name));
426   PetscCall(VecCopy(v, locv));
427   PetscCall(DMGetLocalSection(dm, &section));
428   PetscCall(PetscSectionGetNumFields(section, &numFields));
429   if (!numFields) {
430     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
431     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
432   } else {
433     PetscInt f;
434 
435     for (f = 0; f < numFields; f++) {
436       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
437       if (ft == PETSC_VTK_INVALID) continue;
438       PetscCall(PetscObjectReference((PetscObject)locv));
439       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
440     }
441     PetscCall(VecDestroy(&locv));
442   }
443   PetscFunctionReturn(0);
444 }
445 
446 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
447 {
448   DM        dm;
449   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
450 
451   PetscFunctionBegin;
452   PetscCall(VecGetDM(v, &dm));
453   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
454   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
455   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
456   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
457   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
458   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
459   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
460     PetscInt    i, numFields;
461     PetscObject fe;
462     PetscBool   fem  = PETSC_FALSE;
463     Vec         locv = v;
464     const char *name;
465     PetscInt    step;
466     PetscReal   time;
467 
468     PetscCall(DMGetNumFields(dm, &numFields));
469     for (i = 0; i < numFields; i++) {
470       PetscCall(DMGetField(dm, i, NULL, &fe));
471       if (fe->classid == PETSCFE_CLASSID) {
472         fem = PETSC_TRUE;
473         break;
474       }
475     }
476     if (fem) {
477       PetscObject isZero;
478 
479       PetscCall(DMGetLocalVector(dm, &locv));
480       PetscCall(PetscObjectGetName((PetscObject)v, &name));
481       PetscCall(PetscObjectSetName((PetscObject)locv, name));
482       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
483       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
484       PetscCall(VecCopy(v, locv));
485       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
486       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
487     }
488     if (isvtk) {
489       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
490     } else if (ishdf5) {
491 #if defined(PETSC_HAVE_HDF5)
492       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
493 #else
494       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
495 #endif
496     } else if (isdraw) {
497       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
498     } else if (isglvis) {
499       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
500       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
501       PetscCall(VecView_GLVis(locv, viewer));
502     } else if (iscgns) {
503 #if defined(PETSC_HAVE_CGNS)
504       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
505 #else
506       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
507 #endif
508     }
509     if (fem) {
510       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
511       PetscCall(DMRestoreLocalVector(dm, &locv));
512     }
513   } else {
514     PetscBool isseq;
515 
516     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
517     if (isseq) PetscCall(VecView_Seq(v, viewer));
518     else PetscCall(VecView_MPI(v, viewer));
519   }
520   PetscFunctionReturn(0);
521 }
522 
523 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
524 {
525   DM        dm;
526   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
527 
528   PetscFunctionBegin;
529   PetscCall(VecGetDM(v, &dm));
530   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
532   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
533   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
534   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
535   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
536   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
537   if (isvtk || isdraw || isglvis || iscgns) {
538     Vec         locv;
539     PetscObject isZero;
540     const char *name;
541 
542     PetscCall(DMGetLocalVector(dm, &locv));
543     PetscCall(PetscObjectGetName((PetscObject)v, &name));
544     PetscCall(PetscObjectSetName((PetscObject)locv, name));
545     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
546     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
547     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
548     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
549     PetscCall(VecView_Plex_Local(locv, viewer));
550     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
551     PetscCall(DMRestoreLocalVector(dm, &locv));
552   } else if (ishdf5) {
553 #if defined(PETSC_HAVE_HDF5)
554     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
555 #else
556     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
557 #endif
558   } else if (isexodusii) {
559 #if defined(PETSC_HAVE_EXODUSII)
560     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
561 #else
562     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
563 #endif
564   } else {
565     PetscBool isseq;
566 
567     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
568     if (isseq) PetscCall(VecView_Seq(v, viewer));
569     else PetscCall(VecView_MPI(v, viewer));
570   }
571   PetscFunctionReturn(0);
572 }
573 
574 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
575 {
576   DM                dm;
577   MPI_Comm          comm;
578   PetscViewerFormat format;
579   Vec               v;
580   PetscBool         isvtk, ishdf5;
581 
582   PetscFunctionBegin;
583   PetscCall(VecGetDM(originalv, &dm));
584   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
585   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
586   PetscCall(PetscViewerGetFormat(viewer, &format));
587   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
588   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
589   if (format == PETSC_VIEWER_NATIVE) {
590     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
591     /* this need a better fix */
592     if (dm->useNatural) {
593       if (dm->sfNatural) {
594         const char *vecname;
595         PetscInt    n, nroots;
596 
597         PetscCall(VecGetLocalSize(originalv, &n));
598         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
599         if (n == nroots) {
600           PetscCall(DMGetGlobalVector(dm, &v));
601           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
602           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
603           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
604           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
605         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
606       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
607     } else v = originalv;
608   } else v = originalv;
609 
610   if (ishdf5) {
611 #if defined(PETSC_HAVE_HDF5)
612     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
613 #else
614     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
615 #endif
616   } else if (isvtk) {
617     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
618   } else {
619     PetscBool isseq;
620 
621     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
622     if (isseq) PetscCall(VecView_Seq(v, viewer));
623     else PetscCall(VecView_MPI(v, viewer));
624   }
625   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
626   PetscFunctionReturn(0);
627 }
628 
629 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
630 {
631   DM        dm;
632   PetscBool ishdf5;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
638   if (ishdf5) {
639     DM          dmBC;
640     Vec         gv;
641     const char *name;
642 
643     PetscCall(DMGetOutputDM(dm, &dmBC));
644     PetscCall(DMGetGlobalVector(dmBC, &gv));
645     PetscCall(PetscObjectGetName((PetscObject)v, &name));
646     PetscCall(PetscObjectSetName((PetscObject)gv, name));
647     PetscCall(VecLoad_Default(gv, viewer));
648     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
649     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
650     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
651   } else PetscCall(VecLoad_Default(v, viewer));
652   PetscFunctionReturn(0);
653 }
654 
655 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
656 {
657   DM        dm;
658   PetscBool ishdf5, isexodusii;
659 
660   PetscFunctionBegin;
661   PetscCall(VecGetDM(v, &dm));
662   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
663   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
664   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
665   if (ishdf5) {
666 #if defined(PETSC_HAVE_HDF5)
667     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
668 #else
669     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
670 #endif
671   } else if (isexodusii) {
672 #if defined(PETSC_HAVE_EXODUSII)
673     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
674 #else
675     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
676 #endif
677   } else PetscCall(VecLoad_Default(v, viewer));
678   PetscFunctionReturn(0);
679 }
680 
681 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
682 {
683   DM                dm;
684   PetscViewerFormat format;
685   PetscBool         ishdf5;
686 
687   PetscFunctionBegin;
688   PetscCall(VecGetDM(originalv, &dm));
689   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
690   PetscCall(PetscViewerGetFormat(viewer, &format));
691   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
692   if (format == PETSC_VIEWER_NATIVE) {
693     if (dm->useNatural) {
694       if (dm->sfNatural) {
695         if (ishdf5) {
696 #if defined(PETSC_HAVE_HDF5)
697           Vec         v;
698           const char *vecname;
699 
700           PetscCall(DMGetGlobalVector(dm, &v));
701           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
702           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
703           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
704           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
705           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
706           PetscCall(DMRestoreGlobalVector(dm, &v));
707 #else
708           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
709 #endif
710         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
711       }
712     } else PetscCall(VecLoad_Default(originalv, viewer));
713   }
714   PetscFunctionReturn(0);
715 }
716 
717 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
718 {
719   PetscSection       coordSection;
720   Vec                coordinates;
721   DMLabel            depthLabel, celltypeLabel;
722   const char        *name[4];
723   const PetscScalar *a;
724   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
725 
726   PetscFunctionBegin;
727   PetscCall(DMGetDimension(dm, &dim));
728   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
729   PetscCall(DMGetCoordinateSection(dm, &coordSection));
730   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
731   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
732   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
733   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
734   PetscCall(VecGetArrayRead(coordinates, &a));
735   name[0]       = "vertex";
736   name[1]       = "edge";
737   name[dim - 1] = "face";
738   name[dim]     = "cell";
739   for (c = cStart; c < cEnd; ++c) {
740     PetscInt *closure = NULL;
741     PetscInt  closureSize, cl, ct;
742 
743     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
744     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
745     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
746     PetscCall(PetscViewerASCIIPushTab(viewer));
747     for (cl = 0; cl < closureSize * 2; cl += 2) {
748       PetscInt point = closure[cl], depth, dof, off, d, p;
749 
750       if ((point < pStart) || (point >= pEnd)) continue;
751       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
752       if (!dof) continue;
753       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
754       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
755       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
756       for (p = 0; p < dof / dim; ++p) {
757         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
758         for (d = 0; d < dim; ++d) {
759           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
760           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
761         }
762         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
763       }
764       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
765     }
766     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
767     PetscCall(PetscViewerASCIIPopTab(viewer));
768   }
769   PetscCall(VecRestoreArrayRead(coordinates, &a));
770   PetscFunctionReturn(0);
771 }
772 
773 typedef enum {
774   CS_CARTESIAN,
775   CS_POLAR,
776   CS_CYLINDRICAL,
777   CS_SPHERICAL
778 } CoordSystem;
779 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
780 
781 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
782 {
783   PetscInt i;
784 
785   PetscFunctionBegin;
786   if (dim > 3) {
787     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
788   } else {
789     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
790 
791     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
792     switch (cs) {
793     case CS_CARTESIAN:
794       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
795       break;
796     case CS_POLAR:
797       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       break;
801     case CS_CYLINDRICAL:
802       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
803       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
804       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
805       trcoords[2] = coords[2];
806       break;
807     case CS_SPHERICAL:
808       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
809       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
810       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
811       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
812       break;
813     }
814     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
815   }
816   PetscFunctionReturn(0);
817 }
818 
819 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
820 {
821   DM_Plex          *mesh = (DM_Plex *)dm->data;
822   DM                cdm, cdmCell;
823   PetscSection      coordSection, coordSectionCell;
824   Vec               coordinates, coordinatesCell;
825   PetscViewerFormat format;
826 
827   PetscFunctionBegin;
828   PetscCall(DMGetCoordinateDM(dm, &cdm));
829   PetscCall(DMGetCoordinateSection(dm, &coordSection));
830   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
831   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
832   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
833   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
834   PetscCall(PetscViewerGetFormat(viewer, &format));
835   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
836     const char *name;
837     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
838     PetscInt    pStart, pEnd, p, numLabels, l;
839     PetscMPIInt rank, size;
840 
841     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
842     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
843     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
844     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
845     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
846     PetscCall(DMGetDimension(dm, &dim));
847     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
848     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
849     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
850     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
851     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
852     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
853     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
854     for (p = pStart; p < pEnd; ++p) {
855       PetscInt dof, off, s;
856 
857       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
858       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
859       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
860     }
861     PetscCall(PetscViewerFlush(viewer));
862     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
863     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
864     for (p = pStart; p < pEnd; ++p) {
865       PetscInt dof, off, c;
866 
867       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
868       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
869       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]));
870     }
871     PetscCall(PetscViewerFlush(viewer));
872     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
873     if (coordSection && coordinates) {
874       CoordSystem        cs = CS_CARTESIAN;
875       const PetscScalar *array, *arrayCell = NULL;
876       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
877       PetscMPIInt        rank;
878       const char        *name;
879 
880       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
881       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
882       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
883       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
884       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
885       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
886       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
887       pStart = PetscMin(pvStart, pcStart);
888       pEnd   = PetscMax(pvEnd, pcEnd);
889       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
890       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
891       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
892       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
893 
894       PetscCall(VecGetArrayRead(coordinates, &array));
895       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
896       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
897       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
898       for (p = pStart; p < pEnd; ++p) {
899         PetscInt dof, off;
900 
901         if (p >= pvStart && p < pvEnd) {
902           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
903           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
904           if (dof) {
905             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
906             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
907             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
908           }
909         }
910         if (cdmCell && p >= pcStart && p < pcEnd) {
911           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
912           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
913           if (dof) {
914             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
915             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
916             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
917           }
918         }
919       }
920       PetscCall(PetscViewerFlush(viewer));
921       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
922       PetscCall(VecRestoreArrayRead(coordinates, &array));
923       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
924     }
925     PetscCall(DMGetNumLabels(dm, &numLabels));
926     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
927     for (l = 0; l < numLabels; ++l) {
928       DMLabel     label;
929       PetscBool   isdepth;
930       const char *name;
931 
932       PetscCall(DMGetLabelName(dm, l, &name));
933       PetscCall(PetscStrcmp(name, "depth", &isdepth));
934       if (isdepth) continue;
935       PetscCall(DMGetLabel(dm, name, &label));
936       PetscCall(DMLabelView(label, viewer));
937     }
938     if (size > 1) {
939       PetscSF sf;
940 
941       PetscCall(DMGetPointSF(dm, &sf));
942       PetscCall(PetscSFView(sf, viewer));
943     }
944     PetscCall(PetscViewerFlush(viewer));
945   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
946     const char  *name, *color;
947     const char  *defcolors[3]  = {"gray", "orange", "green"};
948     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
949     char         lname[PETSC_MAX_PATH_LEN];
950     PetscReal    scale      = 2.0;
951     PetscReal    tikzscale  = 1.0;
952     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
953     double       tcoords[3];
954     PetscScalar *coords;
955     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
956     PetscMPIInt  rank, size;
957     char       **names, **colors, **lcolors;
958     PetscBool    flg, lflg;
959     PetscBT      wp = NULL;
960     PetscInt     pEnd, pStart;
961 
962     PetscCall(DMGetDimension(dm, &dim));
963     PetscCall(DMPlexGetDepth(dm, &depth));
964     PetscCall(DMGetNumLabels(dm, &numLabels));
965     numLabels  = PetscMax(numLabels, 10);
966     numColors  = 10;
967     numLColors = 10;
968     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
969     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
970     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
971     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
972     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
973     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
974     n = 4;
975     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
976     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
977     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
978     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
979     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
980     if (!useLabels) numLabels = 0;
981     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
982     if (!useColors) {
983       numColors = 3;
984       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
985     }
986     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
987     if (!useColors) {
988       numLColors = 4;
989       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
990     }
991     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
992     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
993     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
994     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
995     if (depth < dim) plotEdges = PETSC_FALSE;
996     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
997 
998     /* filter points with labelvalue != labeldefaultvalue */
999     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1000     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1001     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1002     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1003     if (lflg) {
1004       DMLabel lbl;
1005 
1006       PetscCall(DMGetLabel(dm, lname, &lbl));
1007       if (lbl) {
1008         PetscInt val, defval;
1009 
1010         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1011         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1012         for (c = pStart; c < pEnd; c++) {
1013           PetscInt *closure = NULL;
1014           PetscInt  closureSize;
1015 
1016           PetscCall(DMLabelGetValue(lbl, c, &val));
1017           if (val == defval) continue;
1018 
1019           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1020           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1021           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1022         }
1023       }
1024     }
1025 
1026     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1027     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1028     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1029     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1030 \\documentclass[tikz]{standalone}\n\n\
1031 \\usepackage{pgflibraryshapes}\n\
1032 \\usetikzlibrary{backgrounds}\n\
1033 \\usetikzlibrary{arrows}\n\
1034 \\begin{document}\n"));
1035     if (size > 1) {
1036       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1037       for (p = 0; p < size; ++p) {
1038         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1039         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1040       }
1041       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1042     }
1043     if (drawHasse) {
1044       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1045 
1046       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1047       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1048       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1049       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1058     }
1059     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1060 
1061     /* Plot vertices */
1062     PetscCall(VecGetArray(coordinates, &coords));
1063     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1064     for (v = vStart; v < vEnd; ++v) {
1065       PetscInt  off, dof, d;
1066       PetscBool isLabeled = PETSC_FALSE;
1067 
1068       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1069       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1070       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1071       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1072       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1073       for (d = 0; d < dof; ++d) {
1074         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1075         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1076       }
1077       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1078       if (dim == 3) {
1079         PetscReal tmp = tcoords[1];
1080         tcoords[1]    = tcoords[2];
1081         tcoords[2]    = -tmp;
1082       }
1083       for (d = 0; d < dof; ++d) {
1084         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1085         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1086       }
1087       if (drawHasse) color = colors[0 % numColors];
1088       else color = colors[rank % numColors];
1089       for (l = 0; l < numLabels; ++l) {
1090         PetscInt val;
1091         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1092         if (val >= 0) {
1093           color     = lcolors[l % numLColors];
1094           isLabeled = PETSC_TRUE;
1095           break;
1096         }
1097       }
1098       if (drawNumbers[0]) {
1099         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1100       } else if (drawColors[0]) {
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1102       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1103     }
1104     PetscCall(VecRestoreArray(coordinates, &coords));
1105     PetscCall(PetscViewerFlush(viewer));
1106     /* Plot edges */
1107     if (plotEdges) {
1108       PetscCall(VecGetArray(coordinates, &coords));
1109       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1110       for (e = eStart; e < eEnd; ++e) {
1111         const PetscInt *cone;
1112         PetscInt        coneSize, offA, offB, dof, d;
1113 
1114         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1115         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1116         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1117         PetscCall(DMPlexGetCone(dm, e, &cone));
1118         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1119         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1120         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1121         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1122         for (d = 0; d < dof; ++d) {
1123           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1124           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1125         }
1126         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1127         if (dim == 3) {
1128           PetscReal tmp = tcoords[1];
1129           tcoords[1]    = tcoords[2];
1130           tcoords[2]    = -tmp;
1131         }
1132         for (d = 0; d < dof; ++d) {
1133           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1134           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1135         }
1136         if (drawHasse) color = colors[1 % numColors];
1137         else color = colors[rank % numColors];
1138         for (l = 0; l < numLabels; ++l) {
1139           PetscInt val;
1140           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1141           if (val >= 0) {
1142             color = lcolors[l % numLColors];
1143             break;
1144           }
1145         }
1146         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1147       }
1148       PetscCall(VecRestoreArray(coordinates, &coords));
1149       PetscCall(PetscViewerFlush(viewer));
1150       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1151     }
1152     /* Plot cells */
1153     if (dim == 3 || !drawNumbers[1]) {
1154       for (e = eStart; e < eEnd; ++e) {
1155         const PetscInt *cone;
1156 
1157         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1158         color = colors[rank % numColors];
1159         for (l = 0; l < numLabels; ++l) {
1160           PetscInt val;
1161           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1162           if (val >= 0) {
1163             color = lcolors[l % numLColors];
1164             break;
1165           }
1166         }
1167         PetscCall(DMPlexGetCone(dm, e, &cone));
1168         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1169       }
1170     } else {
1171       DMPolytopeType ct;
1172 
1173       /* Drawing a 2D polygon */
1174       for (c = cStart; c < cEnd; ++c) {
1175         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1176         PetscCall(DMPlexGetCellType(dm, c, &ct));
1177         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1178           const PetscInt *cone;
1179           PetscInt        coneSize, e;
1180 
1181           PetscCall(DMPlexGetCone(dm, c, &cone));
1182           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1183           for (e = 0; e < coneSize; ++e) {
1184             const PetscInt *econe;
1185 
1186             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1187             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));
1188           }
1189         } else {
1190           PetscInt *closure = NULL;
1191           PetscInt  closureSize, Nv = 0, v;
1192 
1193           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1194           for (p = 0; p < closureSize * 2; p += 2) {
1195             const PetscInt point = closure[p];
1196 
1197             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1198           }
1199           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1200           for (v = 0; v <= Nv; ++v) {
1201             const PetscInt vertex = closure[v % Nv];
1202 
1203             if (v > 0) {
1204               if (plotEdges) {
1205                 const PetscInt *edge;
1206                 PetscInt        endpoints[2], ne;
1207 
1208                 endpoints[0] = closure[v - 1];
1209                 endpoints[1] = vertex;
1210                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1211                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1212                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1213                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1214               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1215             }
1216             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1217           }
1218           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1219           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1220         }
1221       }
1222     }
1223     for (c = cStart; c < cEnd; ++c) {
1224       double             ccoords[3] = {0.0, 0.0, 0.0};
1225       PetscBool          isLabeled  = PETSC_FALSE;
1226       PetscScalar       *cellCoords = NULL;
1227       const PetscScalar *array;
1228       PetscInt           numCoords, cdim, d;
1229       PetscBool          isDG;
1230 
1231       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1232       PetscCall(DMGetCoordinateDim(dm, &cdim));
1233       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1234       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1235       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1236       for (p = 0; p < numCoords / cdim; ++p) {
1237         for (d = 0; d < cdim; ++d) {
1238           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1239           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1240         }
1241         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1242         if (cdim == 3) {
1243           PetscReal tmp = tcoords[1];
1244           tcoords[1]    = tcoords[2];
1245           tcoords[2]    = -tmp;
1246         }
1247         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1248       }
1249       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1250       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1251       for (d = 0; d < cdim; ++d) {
1252         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1253         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1254       }
1255       if (drawHasse) color = colors[depth % numColors];
1256       else color = colors[rank % numColors];
1257       for (l = 0; l < numLabels; ++l) {
1258         PetscInt val;
1259         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1260         if (val >= 0) {
1261           color     = lcolors[l % numLColors];
1262           isLabeled = PETSC_TRUE;
1263           break;
1264         }
1265       }
1266       if (drawNumbers[dim]) {
1267         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1268       } else if (drawColors[dim]) {
1269         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1270       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1271     }
1272     if (drawHasse) {
1273       color = colors[depth % numColors];
1274       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1275       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1276       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1277       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1278       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1279 
1280       color = colors[1 % numColors];
1281       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1282       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1286 
1287       color = colors[0 % numColors];
1288       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1289       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1293 
1294       for (p = pStart; p < pEnd; ++p) {
1295         const PetscInt *cone;
1296         PetscInt        coneSize, cp;
1297 
1298         PetscCall(DMPlexGetCone(dm, p, &cone));
1299         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1300         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1301       }
1302     }
1303     PetscCall(PetscViewerFlush(viewer));
1304     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1305     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1306     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1307     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1308     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1309     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1310     PetscCall(PetscFree3(names, colors, lcolors));
1311     PetscCall(PetscBTDestroy(&wp));
1312   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1313     Vec                    cown, acown;
1314     VecScatter             sct;
1315     ISLocalToGlobalMapping g2l;
1316     IS                     gid, acis;
1317     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1318     MPI_Group              ggroup, ngroup;
1319     PetscScalar           *array, nid;
1320     const PetscInt        *idxs;
1321     PetscInt              *idxs2, *start, *adjacency, *work;
1322     PetscInt64             lm[3], gm[3];
1323     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1324     PetscMPIInt            d1, d2, rank;
1325 
1326     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1327     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1328 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1329     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1330 #endif
1331     if (ncomm != MPI_COMM_NULL) {
1332       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1333       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1334       d1 = 0;
1335       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1336       nid = d2;
1337       PetscCallMPI(MPI_Group_free(&ggroup));
1338       PetscCallMPI(MPI_Group_free(&ngroup));
1339       PetscCallMPI(MPI_Comm_free(&ncomm));
1340     } else nid = 0.0;
1341 
1342     /* Get connectivity */
1343     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1344     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1345 
1346     /* filter overlapped local cells */
1347     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1348     PetscCall(ISGetIndices(gid, &idxs));
1349     PetscCall(ISGetLocalSize(gid, &cum));
1350     PetscCall(PetscMalloc1(cum, &idxs2));
1351     for (c = cStart, cum = 0; c < cEnd; c++) {
1352       if (idxs[c - cStart] < 0) continue;
1353       idxs2[cum++] = idxs[c - cStart];
1354     }
1355     PetscCall(ISRestoreIndices(gid, &idxs));
1356     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1357     PetscCall(ISDestroy(&gid));
1358     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1359 
1360     /* support for node-aware cell locality */
1361     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1362     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1363     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1364     PetscCall(VecGetArray(cown, &array));
1365     for (c = 0; c < numVertices; c++) array[c] = nid;
1366     PetscCall(VecRestoreArray(cown, &array));
1367     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1368     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1369     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1370     PetscCall(ISDestroy(&acis));
1371     PetscCall(VecScatterDestroy(&sct));
1372     PetscCall(VecDestroy(&cown));
1373 
1374     /* compute edgeCut */
1375     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1376     PetscCall(PetscMalloc1(cum, &work));
1377     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1378     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1379     PetscCall(ISDestroy(&gid));
1380     PetscCall(VecGetArray(acown, &array));
1381     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1382       PetscInt totl;
1383 
1384       totl = start[c + 1] - start[c];
1385       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1386       for (i = 0; i < totl; i++) {
1387         if (work[i] < 0) {
1388           ect += 1;
1389           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1390         }
1391       }
1392     }
1393     PetscCall(PetscFree(work));
1394     PetscCall(VecRestoreArray(acown, &array));
1395     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1396     lm[1] = -numVertices;
1397     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1398     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1399     lm[0] = ect;                     /* edgeCut */
1400     lm[1] = ectn;                    /* node-aware edgeCut */
1401     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1402     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1403     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1404 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1405     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1406 #else
1407     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1408 #endif
1409     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1410     PetscCall(PetscFree(start));
1411     PetscCall(PetscFree(adjacency));
1412     PetscCall(VecDestroy(&acown));
1413   } else {
1414     const char    *name;
1415     PetscInt      *sizes, *hybsizes, *ghostsizes;
1416     PetscInt       locDepth, depth, cellHeight, dim, d;
1417     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1418     PetscInt       numLabels, l, maxSize = 17;
1419     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1420     MPI_Comm       comm;
1421     PetscMPIInt    size, rank;
1422 
1423     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1424     PetscCallMPI(MPI_Comm_size(comm, &size));
1425     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1426     PetscCall(DMGetDimension(dm, &dim));
1427     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1428     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1429     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1430     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1431     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1432     PetscCall(DMPlexGetDepth(dm, &locDepth));
1433     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1434     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1435     gcNum = gcEnd - gcStart;
1436     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1437     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1438     for (d = 0; d <= depth; d++) {
1439       PetscInt Nc[2] = {0, 0}, ict;
1440 
1441       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1442       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1443       ict = ct0;
1444       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1445       ct0 = (DMPolytopeType)ict;
1446       for (p = pStart; p < pEnd; ++p) {
1447         DMPolytopeType ct;
1448 
1449         PetscCall(DMPlexGetCellType(dm, p, &ct));
1450         if (ct == ct0) ++Nc[0];
1451         else ++Nc[1];
1452       }
1453       if (size < maxSize) {
1454         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1455         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1456         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1457         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1458         for (p = 0; p < size; ++p) {
1459           if (rank == 0) {
1460             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1461             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1462             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1463           }
1464         }
1465       } else {
1466         PetscInt locMinMax[2];
1467 
1468         locMinMax[0] = Nc[0] + Nc[1];
1469         locMinMax[1] = Nc[0] + Nc[1];
1470         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1471         locMinMax[0] = Nc[1];
1472         locMinMax[1] = Nc[1];
1473         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1474         if (d == depth) {
1475           locMinMax[0] = gcNum;
1476           locMinMax[1] = gcNum;
1477           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1478         }
1479         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1480         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1481         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1482         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1483       }
1484       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1485     }
1486     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1487     {
1488       const PetscReal *maxCell;
1489       const PetscReal *L;
1490       PetscBool        localized;
1491 
1492       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1493       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1494       if (L || localized) {
1495         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1496         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1497         if (L) {
1498           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1499           for (d = 0; d < dim; ++d) {
1500             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1501             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1502           }
1503           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1504         }
1505         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1506         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1507       }
1508     }
1509     PetscCall(DMGetNumLabels(dm, &numLabels));
1510     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1511     for (l = 0; l < numLabels; ++l) {
1512       DMLabel         label;
1513       const char     *name;
1514       IS              valueIS;
1515       const PetscInt *values;
1516       PetscInt        numValues, v;
1517 
1518       PetscCall(DMGetLabelName(dm, l, &name));
1519       PetscCall(DMGetLabel(dm, name, &label));
1520       PetscCall(DMLabelGetNumValues(label, &numValues));
1521       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1522       PetscCall(DMLabelGetValueIS(label, &valueIS));
1523       PetscCall(ISGetIndices(valueIS, &values));
1524       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1525       for (v = 0; v < numValues; ++v) {
1526         PetscInt size;
1527 
1528         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1529         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1530         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1531       }
1532       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1533       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1534       PetscCall(ISRestoreIndices(valueIS, &values));
1535       PetscCall(ISDestroy(&valueIS));
1536     }
1537     {
1538       char    **labelNames;
1539       PetscInt  Nl = numLabels;
1540       PetscBool flg;
1541 
1542       PetscCall(PetscMalloc1(Nl, &labelNames));
1543       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1544       for (l = 0; l < Nl; ++l) {
1545         DMLabel label;
1546 
1547         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1548         if (flg) {
1549           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1550           PetscCall(DMLabelView(label, viewer));
1551         }
1552         PetscCall(PetscFree(labelNames[l]));
1553       }
1554       PetscCall(PetscFree(labelNames));
1555     }
1556     /* If no fields are specified, people do not want to see adjacency */
1557     if (dm->Nf) {
1558       PetscInt f;
1559 
1560       for (f = 0; f < dm->Nf; ++f) {
1561         const char *name;
1562 
1563         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1564         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1565         PetscCall(PetscViewerASCIIPushTab(viewer));
1566         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1567         if (dm->fields[f].adjacency[0]) {
1568           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1569           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1570         } else {
1571           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1572           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1573         }
1574         PetscCall(PetscViewerASCIIPopTab(viewer));
1575       }
1576     }
1577     PetscCall(DMGetCoarseDM(dm, &cdm));
1578     if (cdm) {
1579       PetscCall(PetscViewerASCIIPushTab(viewer));
1580       PetscCall(DMPlexView_Ascii(cdm, viewer));
1581       PetscCall(PetscViewerASCIIPopTab(viewer));
1582     }
1583   }
1584   PetscFunctionReturn(0);
1585 }
1586 
1587 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1588 {
1589   DMPolytopeType ct;
1590   PetscMPIInt    rank;
1591   PetscInt       cdim;
1592 
1593   PetscFunctionBegin;
1594   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1595   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1596   PetscCall(DMGetCoordinateDim(dm, &cdim));
1597   switch (ct) {
1598   case DM_POLYTOPE_SEGMENT:
1599   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1600     switch (cdim) {
1601     case 1: {
1602       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1603       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1604 
1605       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1606       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1607       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1608     } break;
1609     case 2: {
1610       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1611       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1612       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1613 
1614       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1615       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));
1616       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));
1617     } break;
1618     default:
1619       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1620     }
1621     break;
1622   case DM_POLYTOPE_TRIANGLE:
1623     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));
1624     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1625     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1626     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1627     break;
1628   case DM_POLYTOPE_QUADRILATERAL:
1629     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));
1630     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));
1631     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1632     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1633     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1634     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1635     break;
1636   case DM_POLYTOPE_FV_GHOST:
1637     break;
1638   default:
1639     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1640   }
1641   PetscFunctionReturn(0);
1642 }
1643 
1644 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1645 {
1646   DMPolytopeType ct;
1647   PetscReal      centroid[2] = {0., 0.};
1648   PetscMPIInt    rank;
1649   PetscInt       fillColor, v, e, d;
1650 
1651   PetscFunctionBegin;
1652   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1653   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1654   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1655   switch (ct) {
1656   case DM_POLYTOPE_TRIANGLE: {
1657     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1658 
1659     for (v = 0; v < 3; ++v) {
1660       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1661       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1662     }
1663     for (e = 0; e < 3; ++e) {
1664       refCoords[0] = refVertices[e * 2 + 0];
1665       refCoords[1] = refVertices[e * 2 + 1];
1666       for (d = 1; d <= edgeDiv; ++d) {
1667         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1668         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1669       }
1670       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1671       for (d = 0; d < edgeDiv; ++d) {
1672         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));
1673         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1674       }
1675     }
1676   } break;
1677   default:
1678     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1679   }
1680   PetscFunctionReturn(0);
1681 }
1682 
1683 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1684 {
1685   PetscDraw          draw;
1686   DM                 cdm;
1687   PetscSection       coordSection;
1688   Vec                coordinates;
1689   const PetscScalar *coords;
1690   PetscReal          xyl[2], xyr[2], bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1691   PetscReal         *refCoords, *edgeCoords;
1692   PetscBool          isnull, drawAffine = PETSC_TRUE;
1693   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1694 
1695   PetscFunctionBegin;
1696   PetscCall(DMGetCoordinateDim(dm, &dim));
1697   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1698   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1699   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1700   PetscCall(DMGetCoordinateDM(dm, &cdm));
1701   PetscCall(DMGetLocalSection(cdm, &coordSection));
1702   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1703   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1704   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1705 
1706   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1707   PetscCall(PetscDrawIsNull(draw, &isnull));
1708   if (isnull) PetscFunctionReturn(0);
1709   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1710 
1711   PetscCall(VecGetLocalSize(coordinates, &N));
1712   PetscCall(VecGetArrayRead(coordinates, &coords));
1713   for (c = 0; c < N; c += dim) {
1714     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
1715     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1716     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
1717     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
1718   }
1719   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1720   PetscCall(MPIU_Allreduce(&bound[0], xyl, 2, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
1721   PetscCall(MPIU_Allreduce(&bound[2], xyr, 2, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)dm)));
1722   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1723   PetscCall(PetscDrawClear(draw));
1724 
1725   for (c = cStart; c < cEnd; ++c) {
1726     PetscScalar *coords = NULL;
1727     PetscInt     numCoords;
1728 
1729     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1730     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1731     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1732     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1733   }
1734   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1735   PetscCall(PetscDrawFlush(draw));
1736   PetscCall(PetscDrawPause(draw));
1737   PetscCall(PetscDrawSave(draw));
1738   PetscFunctionReturn(0);
1739 }
1740 
1741 #if defined(PETSC_HAVE_EXODUSII)
1742   #include <exodusII.h>
1743   #include <petscviewerexodusii.h>
1744 #endif
1745 
1746 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1747 {
1748   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1749   char      name[PETSC_MAX_PATH_LEN];
1750 
1751   PetscFunctionBegin;
1752   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1753   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1754   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1755   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1756   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1757   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1758   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1759   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1760   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1761   if (iascii) {
1762     PetscViewerFormat format;
1763     PetscCall(PetscViewerGetFormat(viewer, &format));
1764     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1765     else PetscCall(DMPlexView_Ascii(dm, viewer));
1766   } else if (ishdf5) {
1767 #if defined(PETSC_HAVE_HDF5)
1768     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1769 #else
1770     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1771 #endif
1772   } else if (isvtk) {
1773     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1774   } else if (isdraw) {
1775     PetscCall(DMPlexView_Draw(dm, viewer));
1776   } else if (isglvis) {
1777     PetscCall(DMPlexView_GLVis(dm, viewer));
1778 #if defined(PETSC_HAVE_EXODUSII)
1779   } else if (isexodus) {
1780     /*
1781       exodusII requires that all sets be part of exactly one cell set.
1782       If the dm does not have a "Cell Sets" label defined, we create one
1783       with ID 1, containig all cells.
1784       Note that if the Cell Sets label is defined but does not cover all cells,
1785       we may still have a problem. This should probably be checked here or in the viewer;
1786     */
1787     PetscInt numCS;
1788     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1789     if (!numCS) {
1790       PetscInt cStart, cEnd, c;
1791       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1792       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1793       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1794     }
1795     PetscCall(DMView_PlexExodusII(dm, viewer));
1796 #endif
1797 #if defined(PETSC_HAVE_CGNS)
1798   } else if (iscgns) {
1799     PetscCall(DMView_PlexCGNS(dm, viewer));
1800 #endif
1801   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1802   /* Optionally view the partition */
1803   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1804   if (flg) {
1805     Vec ranks;
1806     PetscCall(DMPlexCreateRankField(dm, &ranks));
1807     PetscCall(VecView(ranks, viewer));
1808     PetscCall(VecDestroy(&ranks));
1809   }
1810   /* Optionally view a label */
1811   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1812   if (flg) {
1813     DMLabel label;
1814     Vec     val;
1815 
1816     PetscCall(DMGetLabel(dm, name, &label));
1817     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1818     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1819     PetscCall(VecView(val, viewer));
1820     PetscCall(VecDestroy(&val));
1821   }
1822   PetscFunctionReturn(0);
1823 }
1824 
1825 /*@
1826   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1827 
1828   Collective on dm
1829 
1830   Input Parameters:
1831 + dm     - The `DM` whose topology is to be saved
1832 - viewer - The `PetscViewer` to save it in
1833 
1834   Level: advanced
1835 
1836 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1837 @*/
1838 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1839 {
1840   PetscBool ishdf5;
1841 
1842   PetscFunctionBegin;
1843   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1844   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1845   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1846   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1847   if (ishdf5) {
1848 #if defined(PETSC_HAVE_HDF5)
1849     PetscViewerFormat format;
1850     PetscCall(PetscViewerGetFormat(viewer, &format));
1851     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1852       IS globalPointNumbering;
1853 
1854       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1855       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1856       PetscCall(ISDestroy(&globalPointNumbering));
1857     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1858 #else
1859     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1860 #endif
1861   }
1862   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1863   PetscFunctionReturn(0);
1864 }
1865 
1866 /*@
1867   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1868 
1869   Collective on dm
1870 
1871   Input Parameters:
1872 + dm     - The `DM` whose coordinates are to be saved
1873 - viewer - The `PetscViewer` for saving
1874 
1875   Level: advanced
1876 
1877 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1878 @*/
1879 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1880 {
1881   PetscBool ishdf5;
1882 
1883   PetscFunctionBegin;
1884   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1885   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1886   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1887   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1888   if (ishdf5) {
1889 #if defined(PETSC_HAVE_HDF5)
1890     PetscViewerFormat format;
1891     PetscCall(PetscViewerGetFormat(viewer, &format));
1892     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1893       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1894     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", 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_CoordinatesView, viewer, 0, 0, 0));
1900   PetscFunctionReturn(0);
1901 }
1902 
1903 /*@
1904   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1905 
1906   Collective on dm
1907 
1908   Input Parameters:
1909 + dm     - The `DM` whose labels are to be saved
1910 - viewer - The `PetscViewer` for saving
1911 
1912   Level: advanced
1913 
1914 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1915 @*/
1916 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1917 {
1918   PetscBool ishdf5;
1919 
1920   PetscFunctionBegin;
1921   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1922   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1923   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1924   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1925   if (ishdf5) {
1926 #if defined(PETSC_HAVE_HDF5)
1927     IS                globalPointNumbering;
1928     PetscViewerFormat format;
1929 
1930     PetscCall(PetscViewerGetFormat(viewer, &format));
1931     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1932       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1933       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1934       PetscCall(ISDestroy(&globalPointNumbering));
1935     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1936 #else
1937     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1938 #endif
1939   }
1940   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1941   PetscFunctionReturn(0);
1942 }
1943 
1944 /*@
1945   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1946 
1947   Collective on dm
1948 
1949   Input Parameters:
1950 + dm         - The `DM` that contains the topology on which the section to be saved is defined
1951 . viewer     - The `PetscViewer` for saving
1952 - sectiondm  - The `DM` that contains the section to be saved
1953 
1954   Level: advanced
1955 
1956   Notes:
1957   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.
1958 
1959   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.
1960 
1961 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1962 @*/
1963 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1964 {
1965   PetscBool ishdf5;
1966 
1967   PetscFunctionBegin;
1968   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1969   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1970   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1971   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1972   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1973   if (ishdf5) {
1974 #if defined(PETSC_HAVE_HDF5)
1975     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1976 #else
1977     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1978 #endif
1979   }
1980   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1981   PetscFunctionReturn(0);
1982 }
1983 
1984 /*@
1985   DMPlexGlobalVectorView - Saves a global vector
1986 
1987   Collective on dm
1988 
1989   Input Parameters:
1990 + dm        - The `DM` that represents the topology
1991 . viewer    - The `PetscViewer` to save data with
1992 . sectiondm - The `DM` that contains the global section on which vec is defined
1993 - vec       - The global vector to be saved
1994 
1995   Level: advanced
1996 
1997   Notes:
1998   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.
1999 
2000   Typical calling sequence:
2001 .vb
2002        DMCreate(PETSC_COMM_WORLD, &dm);
2003        DMSetType(dm, DMPLEX);
2004        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2005        DMClone(dm, &sectiondm);
2006        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2007        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2008        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2009        PetscSectionSetChart(section, pStart, pEnd);
2010        PetscSectionSetUp(section);
2011        DMSetLocalSection(sectiondm, section);
2012        PetscSectionDestroy(&section);
2013        DMGetGlobalVector(sectiondm, &vec);
2014        PetscObjectSetName((PetscObject)vec, "vec_name");
2015        DMPlexTopologyView(dm, viewer);
2016        DMPlexSectionView(dm, viewer, sectiondm);
2017        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2018        DMRestoreGlobalVector(sectiondm, &vec);
2019        DMDestroy(&sectiondm);
2020        DMDestroy(&dm);
2021 .ve
2022 
2023 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2024 @*/
2025 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2026 {
2027   PetscBool ishdf5;
2028 
2029   PetscFunctionBegin;
2030   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2031   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2032   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2033   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2034   /* Check consistency */
2035   {
2036     PetscSection section;
2037     PetscBool    includesConstraints;
2038     PetscInt     m, m1;
2039 
2040     PetscCall(VecGetLocalSize(vec, &m1));
2041     PetscCall(DMGetGlobalSection(sectiondm, &section));
2042     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2043     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2044     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2045     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2046   }
2047   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2048   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2049   if (ishdf5) {
2050 #if defined(PETSC_HAVE_HDF5)
2051     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2052 #else
2053     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2054 #endif
2055   }
2056   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2057   PetscFunctionReturn(0);
2058 }
2059 
2060 /*@
2061   DMPlexLocalVectorView - Saves a local vector
2062 
2063   Collective on dm
2064 
2065   Input Parameters:
2066 + dm        - The `DM` that represents the topology
2067 . viewer    - The `PetscViewer` to save data with
2068 . sectiondm - The `DM` that contains the local section on which vec is defined; may be the same as dm
2069 - vec       - The local vector to be saved
2070 
2071   Level: advanced
2072 
2073   Note:
2074   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.
2075 
2076   Typical calling sequence:
2077 .vb
2078        DMCreate(PETSC_COMM_WORLD, &dm);
2079        DMSetType(dm, DMPLEX);
2080        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2081        DMClone(dm, &sectiondm);
2082        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2083        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2084        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2085        PetscSectionSetChart(section, pStart, pEnd);
2086        PetscSectionSetUp(section);
2087        DMSetLocalSection(sectiondm, section);
2088        DMGetLocalVector(sectiondm, &vec);
2089        PetscObjectSetName((PetscObject)vec, "vec_name");
2090        DMPlexTopologyView(dm, viewer);
2091        DMPlexSectionView(dm, viewer, sectiondm);
2092        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2093        DMRestoreLocalVector(sectiondm, &vec);
2094        DMDestroy(&sectiondm);
2095        DMDestroy(&dm);
2096 .ve
2097 
2098 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2099 @*/
2100 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2101 {
2102   PetscBool ishdf5;
2103 
2104   PetscFunctionBegin;
2105   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2106   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2107   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2108   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2109   /* Check consistency */
2110   {
2111     PetscSection section;
2112     PetscBool    includesConstraints;
2113     PetscInt     m, m1;
2114 
2115     PetscCall(VecGetLocalSize(vec, &m1));
2116     PetscCall(DMGetLocalSection(sectiondm, &section));
2117     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2118     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2119     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2120     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2121   }
2122   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2123   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2124   if (ishdf5) {
2125 #if defined(PETSC_HAVE_HDF5)
2126     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2127 #else
2128     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2129 #endif
2130   }
2131   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2132   PetscFunctionReturn(0);
2133 }
2134 
2135 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2136 {
2137   PetscBool ishdf5;
2138 
2139   PetscFunctionBegin;
2140   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2141   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2142   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2143   if (ishdf5) {
2144 #if defined(PETSC_HAVE_HDF5)
2145     PetscViewerFormat format;
2146     PetscCall(PetscViewerGetFormat(viewer, &format));
2147     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2148       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2149     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2150       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2151     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2152     PetscFunctionReturn(0);
2153 #else
2154     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2155 #endif
2156   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2157 }
2158 
2159 /*@
2160   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2161 
2162   Collective on dm
2163 
2164   Input Parameters:
2165 + dm                - The `DM` into which the topology is loaded
2166 - viewer            - The `PetscViewer` for the saved topology
2167 
2168   Output Parameters:
2169 . 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
2170 
2171   Level: advanced
2172 
2173 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2174           `PetscViewer`, `PetscSF`
2175 @*/
2176 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2177 {
2178   PetscBool ishdf5;
2179 
2180   PetscFunctionBegin;
2181   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2182   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2183   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2184   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2185   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2186   if (ishdf5) {
2187 #if defined(PETSC_HAVE_HDF5)
2188     PetscViewerFormat format;
2189     PetscCall(PetscViewerGetFormat(viewer, &format));
2190     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2191       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2192     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2193 #else
2194     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2195 #endif
2196   }
2197   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2198   PetscFunctionReturn(0);
2199 }
2200 
2201 /*@
2202   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2203 
2204   Collective on dm
2205 
2206   Input Parameters:
2207 + dm     - The `DM` into which the coordinates are loaded
2208 . viewer - The `PetscViewer` for the saved coordinates
2209 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2210 
2211   Level: advanced
2212 
2213 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2214           `PetscSF`, `PetscViewer`
2215 @*/
2216 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2217 {
2218   PetscBool ishdf5;
2219 
2220   PetscFunctionBegin;
2221   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2222   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2223   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2224   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2225   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2226   if (ishdf5) {
2227 #if defined(PETSC_HAVE_HDF5)
2228     PetscViewerFormat format;
2229     PetscCall(PetscViewerGetFormat(viewer, &format));
2230     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2231       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2232     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2233 #else
2234     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2235 #endif
2236   }
2237   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2238   PetscFunctionReturn(0);
2239 }
2240 
2241 /*@
2242   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2243 
2244   Collective on dm
2245 
2246   Input Parameters:
2247 + dm     - The `DM` into which the labels are loaded
2248 . viewer - The `PetscViewer` for the saved labels
2249 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2250 
2251   Level: advanced
2252 
2253   Note:
2254   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2255 
2256 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2257           `PetscSF`, `PetscViewer`
2258 @*/
2259 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2260 {
2261   PetscBool ishdf5;
2262 
2263   PetscFunctionBegin;
2264   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2265   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2266   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2267   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2268   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2269   if (ishdf5) {
2270 #if defined(PETSC_HAVE_HDF5)
2271     PetscViewerFormat format;
2272 
2273     PetscCall(PetscViewerGetFormat(viewer, &format));
2274     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2275       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2276     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2277 #else
2278     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2279 #endif
2280   }
2281   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2282   PetscFunctionReturn(0);
2283 }
2284 
2285 /*@
2286   DMPlexSectionLoad - Loads section into a `DMPLEX`
2287 
2288   Collective on dm
2289 
2290   Input Parameters:
2291 + dm          - The `DM` that represents the topology
2292 . viewer      - The `PetscViewer` that represents the on-disk section (sectionA)
2293 . sectiondm   - The `DM` into which the on-disk section (sectionA) is migrated
2294 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2295 
2296   Output Parameters
2297 + 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)
2298 - 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)
2299 
2300   Level: advanced
2301 
2302   Notes:
2303   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.
2304 
2305   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.
2306 
2307   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.
2308 
2309   Example using 2 processes:
2310 .vb
2311   NX (number of points on dm): 4
2312   sectionA                   : the on-disk section
2313   vecA                       : a vector associated with sectionA
2314   sectionB                   : sectiondm's local section constructed in this function
2315   vecB (local)               : a vector associated with sectiondm's local section
2316   vecB (global)              : a vector associated with sectiondm's global section
2317 
2318                                      rank 0    rank 1
2319   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2320   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2321   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2322   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2323   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2324   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2325   sectionB->atlasDof             :     1 0 1 | 1 3
2326   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2327   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2328   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2329 .ve
2330   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2331 
2332 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2333 @*/
2334 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2335 {
2336   PetscBool ishdf5;
2337 
2338   PetscFunctionBegin;
2339   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2340   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2341   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2342   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2343   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2344   if (localDofSF) PetscValidPointer(localDofSF, 6);
2345   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2346   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2347   if (ishdf5) {
2348 #if defined(PETSC_HAVE_HDF5)
2349     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2350 #else
2351     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2352 #endif
2353   }
2354   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2355   PetscFunctionReturn(0);
2356 }
2357 
2358 /*@
2359   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2360 
2361   Collective on dm
2362 
2363   Input Parameters:
2364 + dm        - The `DM` that represents the topology
2365 . viewer    - The `PetscViewer` that represents the on-disk vector data
2366 . sectiondm - The `DM` that contains the global section on which vec is defined
2367 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2368 - vec       - The global vector to set values of
2369 
2370   Level: advanced
2371 
2372   Notes:
2373   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.
2374 
2375   Typical calling sequence:
2376 .vb
2377        DMCreate(PETSC_COMM_WORLD, &dm);
2378        DMSetType(dm, DMPLEX);
2379        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2380        DMPlexTopologyLoad(dm, viewer, &sfX);
2381        DMClone(dm, &sectiondm);
2382        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2383        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2384        DMGetGlobalVector(sectiondm, &vec);
2385        PetscObjectSetName((PetscObject)vec, "vec_name");
2386        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2387        DMRestoreGlobalVector(sectiondm, &vec);
2388        PetscSFDestroy(&gsf);
2389        PetscSFDestroy(&sfX);
2390        DMDestroy(&sectiondm);
2391        DMDestroy(&dm);
2392 .ve
2393 
2394 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2395           `PetscSF`, `PetscViewer`
2396 @*/
2397 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2398 {
2399   PetscBool ishdf5;
2400 
2401   PetscFunctionBegin;
2402   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2403   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2404   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2405   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2406   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2407   /* Check consistency */
2408   {
2409     PetscSection section;
2410     PetscBool    includesConstraints;
2411     PetscInt     m, m1;
2412 
2413     PetscCall(VecGetLocalSize(vec, &m1));
2414     PetscCall(DMGetGlobalSection(sectiondm, &section));
2415     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2416     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2417     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2418     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2419   }
2420   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2421   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2422   if (ishdf5) {
2423 #if defined(PETSC_HAVE_HDF5)
2424     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2425 #else
2426     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2427 #endif
2428   }
2429   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2430   PetscFunctionReturn(0);
2431 }
2432 
2433 /*@
2434   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2435 
2436   Collective on dm
2437 
2438   Input Parameters:
2439 + dm        - The `DM` that represents the topology
2440 . viewer    - The `PetscViewer` that represents the on-disk vector data
2441 . sectiondm - The `DM` that contains the local section on which vec is defined
2442 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2443 - vec       - The local vector to set values of
2444 
2445   Level: advanced
2446 
2447   Notes:
2448   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.
2449 
2450   Typical calling sequence:
2451 .vb
2452        DMCreate(PETSC_COMM_WORLD, &dm);
2453        DMSetType(dm, DMPLEX);
2454        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2455        DMPlexTopologyLoad(dm, viewer, &sfX);
2456        DMClone(dm, &sectiondm);
2457        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2458        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2459        DMGetLocalVector(sectiondm, &vec);
2460        PetscObjectSetName((PetscObject)vec, "vec_name");
2461        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2462        DMRestoreLocalVector(sectiondm, &vec);
2463        PetscSFDestroy(&lsf);
2464        PetscSFDestroy(&sfX);
2465        DMDestroy(&sectiondm);
2466        DMDestroy(&dm);
2467 .ve
2468 
2469 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2470           `PetscSF`, `PetscViewer`
2471 @*/
2472 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2473 {
2474   PetscBool ishdf5;
2475 
2476   PetscFunctionBegin;
2477   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2478   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2479   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2480   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2481   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2482   /* Check consistency */
2483   {
2484     PetscSection section;
2485     PetscBool    includesConstraints;
2486     PetscInt     m, m1;
2487 
2488     PetscCall(VecGetLocalSize(vec, &m1));
2489     PetscCall(DMGetLocalSection(sectiondm, &section));
2490     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2491     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2492     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2493     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2494   }
2495   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2496   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2497   if (ishdf5) {
2498 #if defined(PETSC_HAVE_HDF5)
2499     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2500 #else
2501     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2502 #endif
2503   }
2504   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2505   PetscFunctionReturn(0);
2506 }
2507 
2508 PetscErrorCode DMDestroy_Plex(DM dm)
2509 {
2510   DM_Plex *mesh = (DM_Plex *)dm->data;
2511 
2512   PetscFunctionBegin;
2513   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2514   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2515   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2516   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2517   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2518   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2519   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2520   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2521   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2522   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2523   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2524   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2525   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2526   if (--mesh->refct > 0) PetscFunctionReturn(0);
2527   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2528   PetscCall(PetscFree(mesh->cones));
2529   PetscCall(PetscFree(mesh->coneOrientations));
2530   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2531   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2532   PetscCall(PetscFree(mesh->supports));
2533   PetscCall(PetscFree(mesh->facesTmp));
2534   PetscCall(PetscFree(mesh->tetgenOpts));
2535   PetscCall(PetscFree(mesh->triangleOpts));
2536   PetscCall(PetscFree(mesh->transformType));
2537   PetscCall(PetscFree(mesh->distributionName));
2538   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2539   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2540   PetscCall(ISDestroy(&mesh->subpointIS));
2541   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2542   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2543   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2544   PetscCall(ISDestroy(&mesh->anchorIS));
2545   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2546   PetscCall(PetscFree(mesh->parents));
2547   PetscCall(PetscFree(mesh->childIDs));
2548   PetscCall(PetscSectionDestroy(&mesh->childSection));
2549   PetscCall(PetscFree(mesh->children));
2550   PetscCall(DMDestroy(&mesh->referenceTree));
2551   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2552   PetscCall(PetscFree(mesh->neighbors));
2553   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2554   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2555   PetscCall(PetscFree(mesh));
2556   PetscFunctionReturn(0);
2557 }
2558 
2559 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2560 {
2561   PetscSection           sectionGlobal;
2562   PetscInt               bs = -1, mbs;
2563   PetscInt               localSize, localStart = 0;
2564   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2565   MatType                mtype;
2566   ISLocalToGlobalMapping ltog;
2567 
2568   PetscFunctionBegin;
2569   PetscCall(MatInitializePackage());
2570   mtype = dm->mattype;
2571   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2572   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2573   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2574   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2575   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2576   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2577   PetscCall(MatSetType(*J, mtype));
2578   PetscCall(MatSetFromOptions(*J));
2579   PetscCall(MatGetBlockSize(*J, &mbs));
2580   if (mbs > 1) bs = mbs;
2581   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2582   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2583   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2584   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2585   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2586   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2587   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2588   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2589   if (!isShell) {
2590     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2591     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2592     PetscInt  pStart, pEnd, p, dof, cdof;
2593 
2594     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2595 
2596     PetscCall(PetscCalloc1(localSize, &pblocks));
2597     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2598     for (p = pStart; p < pEnd; ++p) {
2599       PetscInt bdof, offset;
2600 
2601       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2602       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2603       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2604       for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2605       dof  = dof < 0 ? -(dof + 1) : dof;
2606       bdof = cdof && (dof - cdof) ? 1 : dof;
2607       if (dof) {
2608         if (bs < 0) {
2609           bs = bdof;
2610         } else if (bs != bdof) {
2611           bs = 1;
2612         }
2613       }
2614     }
2615     /* Must have same blocksize on all procs (some might have no points) */
2616     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2617     bsLocal[1] = bs;
2618     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2619     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2620     else bs = bsMinMax[0];
2621     bs = PetscMax(1, bs);
2622     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2623     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2624       PetscCall(MatSetBlockSize(*J, bs));
2625       PetscCall(MatSetUp(*J));
2626     } else {
2627       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2628       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2629       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2630     }
2631     { // Consolidate blocks
2632       PetscInt nblocks = 0;
2633       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2634         if (pblocks[i] == 0) continue;
2635         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2636         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]);
2637       }
2638       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2639     }
2640     PetscCall(PetscFree(pblocks));
2641   }
2642   PetscCall(MatSetDM(*J, dm));
2643   PetscFunctionReturn(0);
2644 }
2645 
2646 /*@
2647   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2648 
2649   Not Collective
2650 
2651   Input Parameter:
2652 . mesh - The `DMPLEX`
2653 
2654   Output Parameters:
2655 . subsection - The subdomain section
2656 
2657   Level: developer
2658 
2659 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `PetscSection`
2660 @*/
2661 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2662 {
2663   DM_Plex *mesh = (DM_Plex *)dm->data;
2664 
2665   PetscFunctionBegin;
2666   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2667   if (!mesh->subdomainSection) {
2668     PetscSection section;
2669     PetscSF      sf;
2670 
2671     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2672     PetscCall(DMGetLocalSection(dm, &section));
2673     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2674     PetscCall(PetscSFDestroy(&sf));
2675   }
2676   *subsection = mesh->subdomainSection;
2677   PetscFunctionReturn(0);
2678 }
2679 
2680 /*@
2681   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2682 
2683   Not Collective
2684 
2685   Input Parameter:
2686 . mesh - The `DMPLEX`
2687 
2688   Output Parameters:
2689 + pStart - The first mesh point
2690 - pEnd   - The upper bound for mesh points
2691 
2692   Level: beginner
2693 
2694 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2695 @*/
2696 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2697 {
2698   DM_Plex *mesh = (DM_Plex *)dm->data;
2699 
2700   PetscFunctionBegin;
2701   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2702   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2703   PetscFunctionReturn(0);
2704 }
2705 
2706 /*@
2707   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2708 
2709   Not Collective
2710 
2711   Input Parameters:
2712 + mesh - The `DMPLEX`
2713 . pStart - The first mesh point
2714 - pEnd   - The upper bound for mesh points
2715 
2716   Level: beginner
2717 
2718 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2719 @*/
2720 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2721 {
2722   DM_Plex *mesh = (DM_Plex *)dm->data;
2723 
2724   PetscFunctionBegin;
2725   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2726   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2727   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2728   PetscFunctionReturn(0);
2729 }
2730 
2731 /*@
2732   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2733 
2734   Not Collective
2735 
2736   Input Parameters:
2737 + mesh - The `DMPLEX`
2738 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2739 
2740   Output Parameter:
2741 . size - The cone size for point p
2742 
2743   Level: beginner
2744 
2745 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2746 @*/
2747 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2748 {
2749   DM_Plex *mesh = (DM_Plex *)dm->data;
2750 
2751   PetscFunctionBegin;
2752   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2753   PetscValidIntPointer(size, 3);
2754   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2755   PetscFunctionReturn(0);
2756 }
2757 
2758 /*@
2759   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2760 
2761   Not Collective
2762 
2763   Input Parameters:
2764 + mesh - The `DMPLEX`
2765 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2766 - size - The cone size for point p
2767 
2768   Level: beginner
2769 
2770   Note:
2771   This should be called after `DMPlexSetChart()`.
2772 
2773 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2774 @*/
2775 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2776 {
2777   DM_Plex *mesh = (DM_Plex *)dm->data;
2778 
2779   PetscFunctionBegin;
2780   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2781   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2782   PetscFunctionReturn(0);
2783 }
2784 
2785 /*@
2786   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2787 
2788   Not Collective
2789 
2790   Input Parameters:
2791 + mesh - The `DMPLEX`
2792 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2793 - size - The additional cone size for point p
2794 
2795   Level: beginner
2796 
2797   Note:
2798   This should be called after `DMPlexSetChart()`.
2799 
2800 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2801 @*/
2802 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2803 {
2804   DM_Plex *mesh = (DM_Plex *)dm->data;
2805   PetscFunctionBegin;
2806   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2807   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2808   PetscFunctionReturn(0);
2809 }
2810 
2811 /*@C
2812   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2813 
2814   Not Collective
2815 
2816   Input Parameters:
2817 + dm - The `DMPLEX`
2818 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2819 
2820   Output Parameter:
2821 . cone - An array of points which are on the in-edges for point p
2822 
2823   Level: beginner
2824 
2825   Fortran Note:
2826   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2827   `DMPlexRestoreCone()` is not needed/available in C.
2828 
2829 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2830 @*/
2831 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2832 {
2833   DM_Plex *mesh = (DM_Plex *)dm->data;
2834   PetscInt off;
2835 
2836   PetscFunctionBegin;
2837   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2838   PetscValidPointer(cone, 3);
2839   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2840   *cone = &mesh->cones[off];
2841   PetscFunctionReturn(0);
2842 }
2843 
2844 /*@C
2845   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2846 
2847   Not Collective
2848 
2849   Input Parameters:
2850 + dm - The `DMPLEX`
2851 - p - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2852 
2853   Output Parameters:
2854 + pConesSection - `PetscSection` describing the layout of pCones
2855 - pCones - An array of points which are on the in-edges for the point set p
2856 
2857   Level: intermediate
2858 
2859 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2860 @*/
2861 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2862 {
2863   PetscSection cs, newcs;
2864   PetscInt    *cones;
2865   PetscInt    *newarr = NULL;
2866   PetscInt     n;
2867 
2868   PetscFunctionBegin;
2869   PetscCall(DMPlexGetCones(dm, &cones));
2870   PetscCall(DMPlexGetConeSection(dm, &cs));
2871   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2872   if (pConesSection) *pConesSection = newcs;
2873   if (pCones) {
2874     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2875     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2876   }
2877   PetscFunctionReturn(0);
2878 }
2879 
2880 /*@
2881   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2882 
2883   Not Collective
2884 
2885   Input Parameters:
2886 + dm - The `DMPLEX`
2887 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2888 
2889   Output Parameter:
2890 . expandedPoints - An array of vertices recursively expanded from input points
2891 
2892   Level: advanced
2893 
2894   Notes:
2895   Like `DMPlexGetConeRecursive()` but returns only the 0-depth IS (i.e. vertices only) and no sections.
2896 
2897   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2898 
2899 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2900           `DMPlexGetDepth()`, `IS`
2901 @*/
2902 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2903 {
2904   IS      *expandedPointsAll;
2905   PetscInt depth;
2906 
2907   PetscFunctionBegin;
2908   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2909   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2910   PetscValidPointer(expandedPoints, 3);
2911   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2912   *expandedPoints = expandedPointsAll[0];
2913   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2914   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2915   PetscFunctionReturn(0);
2916 }
2917 
2918 /*@
2919   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).
2920 
2921   Not Collective
2922 
2923   Input Parameters:
2924 + dm - The `DMPLEX`
2925 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2926 
2927   Output Parameters:
2928 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2929 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2930 - sections - (optional) An array of sections which describe mappings from points to their cone points
2931 
2932   Level: advanced
2933 
2934   Notes:
2935   Like `DMPlexGetConeTuple()` but recursive.
2936 
2937   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.
2938   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2939 
2940   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:
2941   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2942   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2943 
2944 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2945           `DMPlexGetDepth()`, `PetscSection`, `IS`
2946 @*/
2947 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2948 {
2949   const PetscInt *arr0 = NULL, *cone = NULL;
2950   PetscInt       *arr = NULL, *newarr = NULL;
2951   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2952   IS             *expandedPoints_;
2953   PetscSection   *sections_;
2954 
2955   PetscFunctionBegin;
2956   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2957   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2958   if (depth) PetscValidIntPointer(depth, 3);
2959   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2960   if (sections) PetscValidPointer(sections, 5);
2961   PetscCall(ISGetLocalSize(points, &n));
2962   PetscCall(ISGetIndices(points, &arr0));
2963   PetscCall(DMPlexGetDepth(dm, &depth_));
2964   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2965   PetscCall(PetscCalloc1(depth_, &sections_));
2966   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2967   for (d = depth_ - 1; d >= 0; d--) {
2968     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2969     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2970     for (i = 0; i < n; i++) {
2971       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2972       if (arr[i] >= start && arr[i] < end) {
2973         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2974         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2975       } else {
2976         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2977       }
2978     }
2979     PetscCall(PetscSectionSetUp(sections_[d]));
2980     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2981     PetscCall(PetscMalloc1(newn, &newarr));
2982     for (i = 0; i < n; i++) {
2983       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2984       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2985       if (cn > 1) {
2986         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2987         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2988       } else {
2989         newarr[co] = arr[i];
2990       }
2991     }
2992     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2993     arr = newarr;
2994     n   = newn;
2995   }
2996   PetscCall(ISRestoreIndices(points, &arr0));
2997   *depth = depth_;
2998   if (expandedPoints) *expandedPoints = expandedPoints_;
2999   else {
3000     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3001     PetscCall(PetscFree(expandedPoints_));
3002   }
3003   if (sections) *sections = sections_;
3004   else {
3005     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3006     PetscCall(PetscFree(sections_));
3007   }
3008   PetscFunctionReturn(0);
3009 }
3010 
3011 /*@
3012   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3013 
3014   Not Collective
3015 
3016   Input Parameters:
3017 + dm - The `DMPLEX`
3018 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3019 
3020   Output Parameters:
3021 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3022 . expandedPoints - (optional) An array of recursively expanded cones
3023 - sections - (optional) An array of sections which describe mappings from points to their cone points
3024 
3025   Level: advanced
3026 
3027   Note:
3028   See `DMPlexGetConeRecursive()`
3029 
3030 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3031           `DMPlexGetDepth()`, `IS`, `PetscSection`
3032 @*/
3033 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3034 {
3035   PetscInt d, depth_;
3036 
3037   PetscFunctionBegin;
3038   PetscCall(DMPlexGetDepth(dm, &depth_));
3039   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3040   if (depth) *depth = 0;
3041   if (expandedPoints) {
3042     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3043     PetscCall(PetscFree(*expandedPoints));
3044   }
3045   if (sections) {
3046     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3047     PetscCall(PetscFree(*sections));
3048   }
3049   PetscFunctionReturn(0);
3050 }
3051 
3052 /*@
3053   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
3054 
3055   Not Collective
3056 
3057   Input Parameters:
3058 + mesh - The `DMPLEX`
3059 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3060 - cone - An array of points which are on the in-edges for point p
3061 
3062   Level: beginner
3063 
3064   Note:
3065   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3066 
3067 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3068 @*/
3069 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3070 {
3071   DM_Plex *mesh = (DM_Plex *)dm->data;
3072   PetscInt pStart, pEnd;
3073   PetscInt dof, off, c;
3074 
3075   PetscFunctionBegin;
3076   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3077   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3078   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3079   if (dof) PetscValidIntPointer(cone, 3);
3080   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3081   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3082   for (c = 0; c < dof; ++c) {
3083     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);
3084     mesh->cones[off + c] = cone[c];
3085   }
3086   PetscFunctionReturn(0);
3087 }
3088 
3089 /*@C
3090   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3091 
3092   Not Collective
3093 
3094   Input Parameters:
3095 + mesh - The `DMPLEX`
3096 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3097 
3098   Output Parameter:
3099 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3100                     integer giving the prescription for cone traversal.
3101 
3102   Level: beginner
3103 
3104   Note:
3105   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3106   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3107   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3108   with the identity.
3109 
3110   Fortran Note:
3111   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3112   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3113 
3114 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3115 @*/
3116 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3117 {
3118   DM_Plex *mesh = (DM_Plex *)dm->data;
3119   PetscInt off;
3120 
3121   PetscFunctionBegin;
3122   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3123   if (PetscDefined(USE_DEBUG)) {
3124     PetscInt dof;
3125     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3126     if (dof) PetscValidPointer(coneOrientation, 3);
3127   }
3128   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3129 
3130   *coneOrientation = &mesh->coneOrientations[off];
3131   PetscFunctionReturn(0);
3132 }
3133 
3134 /*@
3135   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3136 
3137   Not Collective
3138 
3139   Input Parameters:
3140 + mesh - The `DMPLEX`
3141 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3142 - coneOrientation - An array of orientations
3143 
3144   Level: beginner
3145 
3146   Notes:
3147   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3148 
3149   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3150 
3151 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3152 @*/
3153 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3154 {
3155   DM_Plex *mesh = (DM_Plex *)dm->data;
3156   PetscInt pStart, pEnd;
3157   PetscInt dof, off, c;
3158 
3159   PetscFunctionBegin;
3160   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3161   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3162   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3163   if (dof) PetscValidIntPointer(coneOrientation, 3);
3164   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3165   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);
3166   for (c = 0; c < dof; ++c) {
3167     PetscInt cdof, o = coneOrientation[c];
3168 
3169     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3170     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);
3171     mesh->coneOrientations[off + c] = o;
3172   }
3173   PetscFunctionReturn(0);
3174 }
3175 
3176 /*@
3177   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3178 
3179   Not Collective
3180 
3181   Input Parameters:
3182 + mesh - The `DMPLEX`
3183 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3184 . conePos - The local index in the cone where the point should be put
3185 - conePoint - The mesh point to insert
3186 
3187   Level: beginner
3188 
3189 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3190 @*/
3191 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3192 {
3193   DM_Plex *mesh = (DM_Plex *)dm->data;
3194   PetscInt pStart, pEnd;
3195   PetscInt dof, off;
3196 
3197   PetscFunctionBegin;
3198   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3199   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3200   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);
3201   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);
3202   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3203   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3204   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);
3205   mesh->cones[off + conePos] = conePoint;
3206   PetscFunctionReturn(0);
3207 }
3208 
3209 /*@
3210   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3211 
3212   Not Collective
3213 
3214   Input Parameters:
3215 + mesh - The `DMPLEX`
3216 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3217 . conePos - The local index in the cone where the point should be put
3218 - coneOrientation - The point orientation to insert
3219 
3220   Level: beginner
3221 
3222   Note:
3223   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3224 
3225 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3226 @*/
3227 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3228 {
3229   DM_Plex *mesh = (DM_Plex *)dm->data;
3230   PetscInt pStart, pEnd;
3231   PetscInt dof, off;
3232 
3233   PetscFunctionBegin;
3234   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3235   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3236   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);
3237   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3238   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3239   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);
3240   mesh->coneOrientations[off + conePos] = coneOrientation;
3241   PetscFunctionReturn(0);
3242 }
3243 
3244 /*@
3245   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3246 
3247   Not Collective
3248 
3249   Input Parameters:
3250 + mesh - The `DMPLEX`
3251 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3252 
3253   Output Parameter:
3254 . size - The support size for point p
3255 
3256   Level: beginner
3257 
3258 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3259 @*/
3260 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3261 {
3262   DM_Plex *mesh = (DM_Plex *)dm->data;
3263 
3264   PetscFunctionBegin;
3265   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3266   PetscValidIntPointer(size, 3);
3267   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3268   PetscFunctionReturn(0);
3269 }
3270 
3271 /*@
3272   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3273 
3274   Not Collective
3275 
3276   Input Parameters:
3277 + mesh - The `DMPLEX`
3278 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3279 - size - The support size for point p
3280 
3281   Level: beginner
3282 
3283   Note:
3284   This should be called after DMPlexSetChart().
3285 
3286 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3287 @*/
3288 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3289 {
3290   DM_Plex *mesh = (DM_Plex *)dm->data;
3291 
3292   PetscFunctionBegin;
3293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3294   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3295   PetscFunctionReturn(0);
3296 }
3297 
3298 /*@C
3299   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3300 
3301   Not Collective
3302 
3303   Input Parameters:
3304 + mesh - The `DMPLEX`
3305 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3306 
3307   Output Parameter:
3308 . support - An array of points which are on the out-edges for point p
3309 
3310   Level: beginner
3311 
3312   Fortran Note:
3313   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3314   `DMPlexRestoreSupport()` is not needed/available in C.
3315 
3316 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3317 @*/
3318 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3319 {
3320   DM_Plex *mesh = (DM_Plex *)dm->data;
3321   PetscInt off;
3322 
3323   PetscFunctionBegin;
3324   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3325   PetscValidPointer(support, 3);
3326   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3327   *support = &mesh->supports[off];
3328   PetscFunctionReturn(0);
3329 }
3330 
3331 /*@
3332   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3333 
3334   Not Collective
3335 
3336   Input Parameters:
3337 + mesh - The `DMPLEX`
3338 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3339 - support - An array of points which are on the out-edges for point p
3340 
3341   Level: beginner
3342 
3343   Note:
3344   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3345 
3346 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3347 @*/
3348 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3349 {
3350   DM_Plex *mesh = (DM_Plex *)dm->data;
3351   PetscInt pStart, pEnd;
3352   PetscInt dof, off, c;
3353 
3354   PetscFunctionBegin;
3355   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3356   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3357   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3358   if (dof) PetscValidIntPointer(support, 3);
3359   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3360   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);
3361   for (c = 0; c < dof; ++c) {
3362     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);
3363     mesh->supports[off + c] = support[c];
3364   }
3365   PetscFunctionReturn(0);
3366 }
3367 
3368 /*@
3369   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3370 
3371   Not Collective
3372 
3373   Input Parameters:
3374 + mesh - The `DMPLEX`
3375 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3376 . supportPos - The local index in the cone where the point should be put
3377 - supportPoint - The mesh point to insert
3378 
3379   Level: beginner
3380 
3381 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3382 @*/
3383 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3384 {
3385   DM_Plex *mesh = (DM_Plex *)dm->data;
3386   PetscInt pStart, pEnd;
3387   PetscInt dof, off;
3388 
3389   PetscFunctionBegin;
3390   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3391   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3392   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3393   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3394   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);
3395   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);
3396   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);
3397   mesh->supports[off + supportPos] = supportPoint;
3398   PetscFunctionReturn(0);
3399 }
3400 
3401 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3402 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3403 {
3404   switch (ct) {
3405   case DM_POLYTOPE_SEGMENT:
3406     if (o == -1) return -2;
3407     break;
3408   case DM_POLYTOPE_TRIANGLE:
3409     if (o == -3) return -1;
3410     if (o == -2) return -3;
3411     if (o == -1) return -2;
3412     break;
3413   case DM_POLYTOPE_QUADRILATERAL:
3414     if (o == -4) return -2;
3415     if (o == -3) return -1;
3416     if (o == -2) return -4;
3417     if (o == -1) return -3;
3418     break;
3419   default:
3420     return o;
3421   }
3422   return o;
3423 }
3424 
3425 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3426 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3427 {
3428   switch (ct) {
3429   case DM_POLYTOPE_SEGMENT:
3430     if ((o == -2) || (o == 1)) return -1;
3431     if (o == -1) return 0;
3432     break;
3433   case DM_POLYTOPE_TRIANGLE:
3434     if (o == -3) return -2;
3435     if (o == -2) return -1;
3436     if (o == -1) return -3;
3437     break;
3438   case DM_POLYTOPE_QUADRILATERAL:
3439     if (o == -4) return -2;
3440     if (o == -3) return -1;
3441     if (o == -2) return -4;
3442     if (o == -1) return -3;
3443     break;
3444   default:
3445     return o;
3446   }
3447   return o;
3448 }
3449 
3450 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3451 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3452 {
3453   PetscInt pStart, pEnd, p;
3454 
3455   PetscFunctionBegin;
3456   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3457   for (p = pStart; p < pEnd; ++p) {
3458     const PetscInt *cone, *ornt;
3459     PetscInt        coneSize, c;
3460 
3461     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3462     PetscCall(DMPlexGetCone(dm, p, &cone));
3463     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3464     for (c = 0; c < coneSize; ++c) {
3465       DMPolytopeType ct;
3466       const PetscInt o = ornt[c];
3467 
3468       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3469       switch (ct) {
3470       case DM_POLYTOPE_SEGMENT:
3471         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3472         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3473         break;
3474       case DM_POLYTOPE_TRIANGLE:
3475         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3476         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3477         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3478         break;
3479       case DM_POLYTOPE_QUADRILATERAL:
3480         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3481         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3482         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3483         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3484         break;
3485       default:
3486         break;
3487       }
3488     }
3489   }
3490   PetscFunctionReturn(0);
3491 }
3492 
3493 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3494 {
3495   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3496   PetscInt       *closure;
3497   const PetscInt *tmp = NULL, *tmpO = NULL;
3498   PetscInt        off = 0, tmpSize, t;
3499 
3500   PetscFunctionBeginHot;
3501   if (ornt) {
3502     PetscCall(DMPlexGetCellType(dm, p, &ct));
3503     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3504   }
3505   if (*points) {
3506     closure = *points;
3507   } else {
3508     PetscInt maxConeSize, maxSupportSize;
3509     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3510     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3511   }
3512   if (useCone) {
3513     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3514     PetscCall(DMPlexGetCone(dm, p, &tmp));
3515     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3516   } else {
3517     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3518     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3519   }
3520   if (ct == DM_POLYTOPE_UNKNOWN) {
3521     closure[off++] = p;
3522     closure[off++] = 0;
3523     for (t = 0; t < tmpSize; ++t) {
3524       closure[off++] = tmp[t];
3525       closure[off++] = tmpO ? tmpO[t] : 0;
3526     }
3527   } else {
3528     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3529 
3530     /* We assume that cells with a valid type have faces with a valid type */
3531     closure[off++] = p;
3532     closure[off++] = ornt;
3533     for (t = 0; t < tmpSize; ++t) {
3534       DMPolytopeType ft;
3535 
3536       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3537       closure[off++] = tmp[arr[t]];
3538       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3539     }
3540   }
3541   if (numPoints) *numPoints = tmpSize + 1;
3542   if (points) *points = closure;
3543   PetscFunctionReturn(0);
3544 }
3545 
3546 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3547 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3548 {
3549   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3550   const PetscInt *cone, *ornt;
3551   PetscInt       *pts, *closure = NULL;
3552   DMPolytopeType  ft;
3553   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3554   PetscInt        dim, coneSize, c, d, clSize, cl;
3555 
3556   PetscFunctionBeginHot;
3557   PetscCall(DMGetDimension(dm, &dim));
3558   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3559   PetscCall(DMPlexGetCone(dm, point, &cone));
3560   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3561   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3562   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3563   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3564   maxSize       = PetscMax(coneSeries, supportSeries);
3565   if (*points) {
3566     pts = *points;
3567   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3568   c        = 0;
3569   pts[c++] = point;
3570   pts[c++] = o;
3571   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3572   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3573   for (cl = 0; cl < clSize * 2; cl += 2) {
3574     pts[c++] = closure[cl];
3575     pts[c++] = closure[cl + 1];
3576   }
3577   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3578   for (cl = 0; cl < clSize * 2; cl += 2) {
3579     pts[c++] = closure[cl];
3580     pts[c++] = closure[cl + 1];
3581   }
3582   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3583   for (d = 2; d < coneSize; ++d) {
3584     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3585     pts[c++] = cone[arr[d * 2 + 0]];
3586     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3587   }
3588   if (dim >= 3) {
3589     for (d = 2; d < coneSize; ++d) {
3590       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3591       const PetscInt *fcone, *fornt;
3592       PetscInt        fconeSize, fc, i;
3593 
3594       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3595       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3596       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3597       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3598       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3599       for (fc = 0; fc < fconeSize; ++fc) {
3600         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3601         const PetscInt co = farr[fc * 2 + 1];
3602 
3603         for (i = 0; i < c; i += 2)
3604           if (pts[i] == cp) break;
3605         if (i == c) {
3606           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3607           pts[c++] = cp;
3608           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3609         }
3610       }
3611     }
3612   }
3613   *numPoints = c / 2;
3614   *points    = pts;
3615   PetscFunctionReturn(0);
3616 }
3617 
3618 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3619 {
3620   DMPolytopeType ct;
3621   PetscInt      *closure, *fifo;
3622   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3623   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3624   PetscInt       depth, maxSize;
3625 
3626   PetscFunctionBeginHot;
3627   PetscCall(DMPlexGetDepth(dm, &depth));
3628   if (depth == 1) {
3629     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3630     PetscFunctionReturn(0);
3631   }
3632   PetscCall(DMPlexGetCellType(dm, p, &ct));
3633   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3634   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3635     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3636     PetscFunctionReturn(0);
3637   }
3638   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3639   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3640   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3641   maxSize       = PetscMax(coneSeries, supportSeries);
3642   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3643   if (*points) {
3644     closure = *points;
3645   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3646   closure[closureSize++] = p;
3647   closure[closureSize++] = ornt;
3648   fifo[fifoSize++]       = p;
3649   fifo[fifoSize++]       = ornt;
3650   fifo[fifoSize++]       = ct;
3651   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3652   while (fifoSize - fifoStart) {
3653     const PetscInt       q    = fifo[fifoStart++];
3654     const PetscInt       o    = fifo[fifoStart++];
3655     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3656     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3657     const PetscInt      *tmp, *tmpO;
3658     PetscInt             tmpSize, t;
3659 
3660     if (PetscDefined(USE_DEBUG)) {
3661       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3662       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);
3663     }
3664     if (useCone) {
3665       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3666       PetscCall(DMPlexGetCone(dm, q, &tmp));
3667       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3668     } else {
3669       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3670       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3671       tmpO = NULL;
3672     }
3673     for (t = 0; t < tmpSize; ++t) {
3674       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3675       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3676       const PetscInt cp = tmp[ip];
3677       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3678       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3679       PetscInt       c;
3680 
3681       /* Check for duplicate */
3682       for (c = 0; c < closureSize; c += 2) {
3683         if (closure[c] == cp) break;
3684       }
3685       if (c == closureSize) {
3686         closure[closureSize++] = cp;
3687         closure[closureSize++] = co;
3688         fifo[fifoSize++]       = cp;
3689         fifo[fifoSize++]       = co;
3690         fifo[fifoSize++]       = ct;
3691       }
3692     }
3693   }
3694   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3695   if (numPoints) *numPoints = closureSize / 2;
3696   if (points) *points = closure;
3697   PetscFunctionReturn(0);
3698 }
3699 
3700 /*@C
3701   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3702 
3703   Not Collective
3704 
3705   Input Parameters:
3706 + dm      - The `DMPLEX`
3707 . p       - The mesh point
3708 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3709 
3710   Input/Output Parameter:
3711 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3712            if NULL on input, internal storage will be returned, otherwise the provided array is used
3713 
3714   Output Parameter:
3715 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3716 
3717   Level: beginner
3718 
3719   Note:
3720   If using internal storage (points is NULL on input), each call overwrites the last output.
3721 
3722   Fortran Note:
3723   The numPoints argument is not present in the Fortran binding since it is internal to the array.
3724 
3725 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3726 @*/
3727 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3728 {
3729   PetscFunctionBeginHot;
3730   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3731   if (numPoints) PetscValidIntPointer(numPoints, 4);
3732   if (points) PetscValidPointer(points, 5);
3733   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3734   PetscFunctionReturn(0);
3735 }
3736 
3737 /*@C
3738   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3739 
3740   Not Collective
3741 
3742   Input Parameters:
3743 + dm        - The `DMPLEX`
3744 . p         - The mesh point
3745 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3746 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3747 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3748 
3749   Level: beginner
3750 
3751   Note:
3752   If not using internal storage (points is not NULL on input), this call is unnecessary
3753 
3754 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3755 @*/
3756 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3757 {
3758   PetscFunctionBeginHot;
3759   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3760   if (numPoints) *numPoints = 0;
3761   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3762   PetscFunctionReturn(0);
3763 }
3764 
3765 /*@
3766   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3767 
3768   Not Collective
3769 
3770   Input Parameter:
3771 . mesh - The `DMPLEX`
3772 
3773   Output Parameters:
3774 + maxConeSize - The maximum number of in-edges
3775 - maxSupportSize - The maximum number of out-edges
3776 
3777   Level: beginner
3778 
3779 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3780 @*/
3781 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3782 {
3783   DM_Plex *mesh = (DM_Plex *)dm->data;
3784 
3785   PetscFunctionBegin;
3786   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3787   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3788   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3789   PetscFunctionReturn(0);
3790 }
3791 
3792 PetscErrorCode DMSetUp_Plex(DM dm)
3793 {
3794   DM_Plex *mesh = (DM_Plex *)dm->data;
3795   PetscInt size, maxSupportSize;
3796 
3797   PetscFunctionBegin;
3798   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3799   PetscCall(PetscSectionSetUp(mesh->coneSection));
3800   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3801   PetscCall(PetscMalloc1(size, &mesh->cones));
3802   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3803   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3804   if (maxSupportSize) {
3805     PetscCall(PetscSectionSetUp(mesh->supportSection));
3806     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3807     PetscCall(PetscMalloc1(size, &mesh->supports));
3808   }
3809   PetscFunctionReturn(0);
3810 }
3811 
3812 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3813 {
3814   PetscFunctionBegin;
3815   if (subdm) PetscCall(DMClone(dm, subdm));
3816   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3817   if (subdm) (*subdm)->useNatural = dm->useNatural;
3818   if (dm->useNatural && dm->sfMigration) {
3819     PetscSF sfNatural;
3820 
3821     (*subdm)->sfMigration = dm->sfMigration;
3822     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3823     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3824     (*subdm)->sfNatural = sfNatural;
3825   }
3826   PetscFunctionReturn(0);
3827 }
3828 
3829 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3830 {
3831   PetscInt i = 0;
3832 
3833   PetscFunctionBegin;
3834   PetscCall(DMClone(dms[0], superdm));
3835   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3836   (*superdm)->useNatural = PETSC_FALSE;
3837   for (i = 0; i < len; i++) {
3838     if (dms[i]->useNatural && dms[i]->sfMigration) {
3839       PetscSF sfNatural;
3840 
3841       (*superdm)->sfMigration = dms[i]->sfMigration;
3842       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3843       (*superdm)->useNatural = PETSC_TRUE;
3844       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3845       (*superdm)->sfNatural = sfNatural;
3846       break;
3847     }
3848   }
3849   PetscFunctionReturn(0);
3850 }
3851 
3852 /*@
3853   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3854 
3855   Not Collective
3856 
3857   Input Parameter:
3858 . mesh - The `DMPLEX`
3859 
3860   Level: beginner
3861 
3862   Note:
3863   This should be called after all calls to `DMPlexSetCone()`
3864 
3865 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3866 @*/
3867 PetscErrorCode DMPlexSymmetrize(DM dm)
3868 {
3869   DM_Plex  *mesh = (DM_Plex *)dm->data;
3870   PetscInt *offsets;
3871   PetscInt  supportSize;
3872   PetscInt  pStart, pEnd, p;
3873 
3874   PetscFunctionBegin;
3875   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3876   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3877   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3878   /* Calculate support sizes */
3879   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3880   for (p = pStart; p < pEnd; ++p) {
3881     PetscInt dof, off, c;
3882 
3883     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3884     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3885     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3886   }
3887   PetscCall(PetscSectionSetUp(mesh->supportSection));
3888   /* Calculate supports */
3889   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3890   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3891   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3892   for (p = pStart; p < pEnd; ++p) {
3893     PetscInt dof, off, c;
3894 
3895     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3896     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3897     for (c = off; c < off + dof; ++c) {
3898       const PetscInt q = mesh->cones[c];
3899       PetscInt       offS;
3900 
3901       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3902 
3903       mesh->supports[offS + offsets[q]] = p;
3904       ++offsets[q];
3905     }
3906   }
3907   PetscCall(PetscFree(offsets));
3908   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
3909   PetscFunctionReturn(0);
3910 }
3911 
3912 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3913 {
3914   IS stratumIS;
3915 
3916   PetscFunctionBegin;
3917   if (pStart >= pEnd) PetscFunctionReturn(0);
3918   if (PetscDefined(USE_DEBUG)) {
3919     PetscInt  qStart, qEnd, numLevels, level;
3920     PetscBool overlap = PETSC_FALSE;
3921     PetscCall(DMLabelGetNumValues(label, &numLevels));
3922     for (level = 0; level < numLevels; level++) {
3923       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3924       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
3925         overlap = PETSC_TRUE;
3926         break;
3927       }
3928     }
3929     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);
3930   }
3931   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
3932   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3933   PetscCall(ISDestroy(&stratumIS));
3934   PetscFunctionReturn(0);
3935 }
3936 
3937 /*@
3938   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3939   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3940   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3941   the DAG.
3942 
3943   Collective on dm
3944 
3945   Input Parameter:
3946 . mesh - The `DMPLEX`
3947 
3948   Level: beginner
3949 
3950   Notes:
3951   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3952   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3953   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
3954   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3955   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
3956 
3957   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3958   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3959   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
3960   to interpolate only that one (e0), so that
3961 .vb
3962   cone(c0) = {e0, v2}
3963   cone(e0) = {v0, v1}
3964 .ve
3965   If `DMPlexStratify()` is run on this mesh, it will give depths
3966 .vb
3967    depth 0 = {v0, v1, v2}
3968    depth 1 = {e0, c0}
3969 .ve
3970   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3971 
3972   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
3973 
3974 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3975 @*/
3976 PetscErrorCode DMPlexStratify(DM dm)
3977 {
3978   DM_Plex *mesh = (DM_Plex *)dm->data;
3979   DMLabel  label;
3980   PetscInt pStart, pEnd, p;
3981   PetscInt numRoots = 0, numLeaves = 0;
3982 
3983   PetscFunctionBegin;
3984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3985   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
3986 
3987   /* Create depth label */
3988   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3989   PetscCall(DMCreateLabel(dm, "depth"));
3990   PetscCall(DMPlexGetDepthLabel(dm, &label));
3991 
3992   {
3993     /* Initialize roots and count leaves */
3994     PetscInt sMin = PETSC_MAX_INT;
3995     PetscInt sMax = PETSC_MIN_INT;
3996     PetscInt coneSize, supportSize;
3997 
3998     for (p = pStart; p < pEnd; ++p) {
3999       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4000       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4001       if (!coneSize && supportSize) {
4002         sMin = PetscMin(p, sMin);
4003         sMax = PetscMax(p, sMax);
4004         ++numRoots;
4005       } else if (!supportSize && coneSize) {
4006         ++numLeaves;
4007       } else if (!supportSize && !coneSize) {
4008         /* Isolated points */
4009         sMin = PetscMin(p, sMin);
4010         sMax = PetscMax(p, sMax);
4011       }
4012     }
4013     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4014   }
4015 
4016   if (numRoots + numLeaves == (pEnd - pStart)) {
4017     PetscInt sMin = PETSC_MAX_INT;
4018     PetscInt sMax = PETSC_MIN_INT;
4019     PetscInt coneSize, supportSize;
4020 
4021     for (p = pStart; p < pEnd; ++p) {
4022       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4023       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4024       if (!supportSize && coneSize) {
4025         sMin = PetscMin(p, sMin);
4026         sMax = PetscMax(p, sMax);
4027       }
4028     }
4029     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4030   } else {
4031     PetscInt level = 0;
4032     PetscInt qStart, qEnd, q;
4033 
4034     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4035     while (qEnd > qStart) {
4036       PetscInt sMin = PETSC_MAX_INT;
4037       PetscInt sMax = PETSC_MIN_INT;
4038 
4039       for (q = qStart; q < qEnd; ++q) {
4040         const PetscInt *support;
4041         PetscInt        supportSize, s;
4042 
4043         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4044         PetscCall(DMPlexGetSupport(dm, q, &support));
4045         for (s = 0; s < supportSize; ++s) {
4046           sMin = PetscMin(support[s], sMin);
4047           sMax = PetscMax(support[s], sMax);
4048         }
4049       }
4050       PetscCall(DMLabelGetNumValues(label, &level));
4051       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4052       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4053     }
4054   }
4055   { /* just in case there is an empty process */
4056     PetscInt numValues, maxValues = 0, v;
4057 
4058     PetscCall(DMLabelGetNumValues(label, &numValues));
4059     PetscCallMPI(MPI_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4060     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4061   }
4062   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4063   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4064   PetscFunctionReturn(0);
4065 }
4066 
4067 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4068 {
4069   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4070   PetscInt       dim, depth, pheight, coneSize;
4071 
4072   PetscFunctionBeginHot;
4073   PetscCall(DMGetDimension(dm, &dim));
4074   PetscCall(DMPlexGetDepth(dm, &depth));
4075   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4076   pheight = depth - pdepth;
4077   if (depth <= 1) {
4078     switch (pdepth) {
4079     case 0:
4080       ct = DM_POLYTOPE_POINT;
4081       break;
4082     case 1:
4083       switch (coneSize) {
4084       case 2:
4085         ct = DM_POLYTOPE_SEGMENT;
4086         break;
4087       case 3:
4088         ct = DM_POLYTOPE_TRIANGLE;
4089         break;
4090       case 4:
4091         switch (dim) {
4092         case 2:
4093           ct = DM_POLYTOPE_QUADRILATERAL;
4094           break;
4095         case 3:
4096           ct = DM_POLYTOPE_TETRAHEDRON;
4097           break;
4098         default:
4099           break;
4100         }
4101         break;
4102       case 5:
4103         ct = DM_POLYTOPE_PYRAMID;
4104         break;
4105       case 6:
4106         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4107         break;
4108       case 8:
4109         ct = DM_POLYTOPE_HEXAHEDRON;
4110         break;
4111       default:
4112         break;
4113       }
4114     }
4115   } else {
4116     if (pdepth == 0) {
4117       ct = DM_POLYTOPE_POINT;
4118     } else if (pheight == 0) {
4119       switch (dim) {
4120       case 1:
4121         switch (coneSize) {
4122         case 2:
4123           ct = DM_POLYTOPE_SEGMENT;
4124           break;
4125         default:
4126           break;
4127         }
4128         break;
4129       case 2:
4130         switch (coneSize) {
4131         case 3:
4132           ct = DM_POLYTOPE_TRIANGLE;
4133           break;
4134         case 4:
4135           ct = DM_POLYTOPE_QUADRILATERAL;
4136           break;
4137         default:
4138           break;
4139         }
4140         break;
4141       case 3:
4142         switch (coneSize) {
4143         case 4:
4144           ct = DM_POLYTOPE_TETRAHEDRON;
4145           break;
4146         case 5: {
4147           const PetscInt *cone;
4148           PetscInt        faceConeSize;
4149 
4150           PetscCall(DMPlexGetCone(dm, p, &cone));
4151           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4152           switch (faceConeSize) {
4153           case 3:
4154             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4155             break;
4156           case 4:
4157             ct = DM_POLYTOPE_PYRAMID;
4158             break;
4159           }
4160         } break;
4161         case 6:
4162           ct = DM_POLYTOPE_HEXAHEDRON;
4163           break;
4164         default:
4165           break;
4166         }
4167         break;
4168       default:
4169         break;
4170       }
4171     } else if (pheight > 0) {
4172       switch (coneSize) {
4173       case 2:
4174         ct = DM_POLYTOPE_SEGMENT;
4175         break;
4176       case 3:
4177         ct = DM_POLYTOPE_TRIANGLE;
4178         break;
4179       case 4:
4180         ct = DM_POLYTOPE_QUADRILATERAL;
4181         break;
4182       default:
4183         break;
4184       }
4185     }
4186   }
4187   *pt = ct;
4188   PetscFunctionReturn(0);
4189 }
4190 
4191 /*@
4192   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4193 
4194   Collective on dm
4195 
4196   Input Parameter:
4197 . mesh - The `DMPLEX`
4198 
4199   Level: developer
4200 
4201   Note:
4202   This function is normally called automatically when a cell type is requested. It creates an
4203   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4204   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4205 
4206   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4207 
4208 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4209 @*/
4210 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4211 {
4212   DM_Plex *mesh;
4213   DMLabel  ctLabel;
4214   PetscInt pStart, pEnd, p;
4215 
4216   PetscFunctionBegin;
4217   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4218   mesh = (DM_Plex *)dm->data;
4219   PetscCall(DMCreateLabel(dm, "celltype"));
4220   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4221   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4222   for (p = pStart; p < pEnd; ++p) {
4223     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4224     PetscInt       pdepth;
4225 
4226     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4227     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4228     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4229     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4230   }
4231   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4232   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4233   PetscFunctionReturn(0);
4234 }
4235 
4236 /*@C
4237   DMPlexGetJoin - Get an array for the join of the set of points
4238 
4239   Not Collective
4240 
4241   Input Parameters:
4242 + dm - The `DMPLEX` object
4243 . numPoints - The number of input points for the join
4244 - points - The input points
4245 
4246   Output Parameters:
4247 + numCoveredPoints - The number of points in the join
4248 - coveredPoints - The points in the join
4249 
4250   Level: intermediate
4251 
4252   Note:
4253   Currently, this is restricted to a single level join
4254 
4255   Fortran Note:
4256   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4257 
4258 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4259 @*/
4260 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4261 {
4262   DM_Plex  *mesh = (DM_Plex *)dm->data;
4263   PetscInt *join[2];
4264   PetscInt  joinSize, i = 0;
4265   PetscInt  dof, off, p, c, m;
4266   PetscInt  maxSupportSize;
4267 
4268   PetscFunctionBegin;
4269   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4270   PetscValidIntPointer(points, 3);
4271   PetscValidIntPointer(numCoveredPoints, 4);
4272   PetscValidPointer(coveredPoints, 5);
4273   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4274   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4275   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4276   /* Copy in support of first point */
4277   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4278   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4279   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4280   /* Check each successive support */
4281   for (p = 1; p < numPoints; ++p) {
4282     PetscInt newJoinSize = 0;
4283 
4284     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4285     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4286     for (c = 0; c < dof; ++c) {
4287       const PetscInt point = mesh->supports[off + c];
4288 
4289       for (m = 0; m < joinSize; ++m) {
4290         if (point == join[i][m]) {
4291           join[1 - i][newJoinSize++] = point;
4292           break;
4293         }
4294       }
4295     }
4296     joinSize = newJoinSize;
4297     i        = 1 - i;
4298   }
4299   *numCoveredPoints = joinSize;
4300   *coveredPoints    = join[i];
4301   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4302   PetscFunctionReturn(0);
4303 }
4304 
4305 /*@C
4306   DMPlexRestoreJoin - Restore an array for the join 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 join
4313 - points - The input points
4314 
4315   Output Parameters:
4316 + numCoveredPoints - The number of points in the join
4317 - coveredPoints - The points in the join
4318 
4319   Level: intermediate
4320 
4321   Fortran Note:
4322   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4323 
4324 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4325 @*/
4326 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4327 {
4328   PetscFunctionBegin;
4329   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4330   if (points) PetscValidIntPointer(points, 3);
4331   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4332   PetscValidPointer(coveredPoints, 5);
4333   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4334   if (numCoveredPoints) *numCoveredPoints = 0;
4335   PetscFunctionReturn(0);
4336 }
4337 
4338 /*@C
4339   DMPlexGetFullJoin - Get an array for the join of the set of points
4340 
4341   Not Collective
4342 
4343   Input Parameters:
4344 + dm - The `DMPLEX` object
4345 . numPoints - The number of input points for the join
4346 - points - The input points
4347 
4348   Output Parameters:
4349 + numCoveredPoints - The number of points in the join
4350 - coveredPoints - The points in the join
4351 
4352   Level: intermediate
4353 
4354   Fortran Note:
4355   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4356 
4357 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4358 @*/
4359 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4360 {
4361   PetscInt *offsets, **closures;
4362   PetscInt *join[2];
4363   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4364   PetscInt  p, d, c, m, ms;
4365 
4366   PetscFunctionBegin;
4367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4368   PetscValidIntPointer(points, 3);
4369   PetscValidIntPointer(numCoveredPoints, 4);
4370   PetscValidPointer(coveredPoints, 5);
4371 
4372   PetscCall(DMPlexGetDepth(dm, &depth));
4373   PetscCall(PetscCalloc1(numPoints, &closures));
4374   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4375   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4376   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4377   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4378   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4379 
4380   for (p = 0; p < numPoints; ++p) {
4381     PetscInt closureSize;
4382 
4383     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4384 
4385     offsets[p * (depth + 2) + 0] = 0;
4386     for (d = 0; d < depth + 1; ++d) {
4387       PetscInt pStart, pEnd, i;
4388 
4389       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4390       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4391         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4392           offsets[p * (depth + 2) + d + 1] = i;
4393           break;
4394         }
4395       }
4396       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4397     }
4398     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);
4399   }
4400   for (d = 0; d < depth + 1; ++d) {
4401     PetscInt dof;
4402 
4403     /* Copy in support of first point */
4404     dof = offsets[d + 1] - offsets[d];
4405     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4406     /* Check each successive cone */
4407     for (p = 1; p < numPoints && joinSize; ++p) {
4408       PetscInt newJoinSize = 0;
4409 
4410       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4411       for (c = 0; c < dof; ++c) {
4412         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4413 
4414         for (m = 0; m < joinSize; ++m) {
4415           if (point == join[i][m]) {
4416             join[1 - i][newJoinSize++] = point;
4417             break;
4418           }
4419         }
4420       }
4421       joinSize = newJoinSize;
4422       i        = 1 - i;
4423     }
4424     if (joinSize) break;
4425   }
4426   *numCoveredPoints = joinSize;
4427   *coveredPoints    = join[i];
4428   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4429   PetscCall(PetscFree(closures));
4430   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4431   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4432   PetscFunctionReturn(0);
4433 }
4434 
4435 /*@C
4436   DMPlexGetMeet - Get an array for the meet of the set of points
4437 
4438   Not Collective
4439 
4440   Input Parameters:
4441 + dm - The `DMPLEX` object
4442 . numPoints - The number of input points for the meet
4443 - points - The input points
4444 
4445   Output Parameters:
4446 + numCoveredPoints - The number of points in the meet
4447 - coveredPoints - The points in the meet
4448 
4449   Level: intermediate
4450 
4451   Note:
4452   Currently, this is restricted to a single level meet
4453 
4454   Fortran Notes:
4455   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4456 
4457 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4458 @*/
4459 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4460 {
4461   DM_Plex  *mesh = (DM_Plex *)dm->data;
4462   PetscInt *meet[2];
4463   PetscInt  meetSize, i = 0;
4464   PetscInt  dof, off, p, c, m;
4465   PetscInt  maxConeSize;
4466 
4467   PetscFunctionBegin;
4468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4469   PetscValidIntPointer(points, 3);
4470   PetscValidIntPointer(numCoveringPoints, 4);
4471   PetscValidPointer(coveringPoints, 5);
4472   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4473   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4474   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4475   /* Copy in cone of first point */
4476   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4477   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4478   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4479   /* Check each successive cone */
4480   for (p = 1; p < numPoints; ++p) {
4481     PetscInt newMeetSize = 0;
4482 
4483     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4484     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4485     for (c = 0; c < dof; ++c) {
4486       const PetscInt point = mesh->cones[off + c];
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   *numCoveringPoints = meetSize;
4499   *coveringPoints    = meet[i];
4500   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4501   PetscFunctionReturn(0);
4502 }
4503 
4504 /*@C
4505   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4506 
4507   Not Collective
4508 
4509   Input Parameters:
4510 + dm - The `DMPLEX` object
4511 . numPoints - The number of input points for the meet
4512 - points - The input points
4513 
4514   Output Parameters:
4515 + numCoveredPoints - The number of points in the meet
4516 - coveredPoints - The points in the meet
4517 
4518   Level: intermediate
4519 
4520   Fortran Note:
4521   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4522 
4523 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4524 @*/
4525 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4526 {
4527   PetscFunctionBegin;
4528   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4529   if (points) PetscValidIntPointer(points, 3);
4530   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4531   PetscValidPointer(coveredPoints, 5);
4532   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4533   if (numCoveredPoints) *numCoveredPoints = 0;
4534   PetscFunctionReturn(0);
4535 }
4536 
4537 /*@C
4538   DMPlexGetFullMeet - Get an array for the meet of the set of points
4539 
4540   Not Collective
4541 
4542   Input Parameters:
4543 + dm - The `DMPLEX` object
4544 . numPoints - The number of input points for the meet
4545 - points - The input points
4546 
4547   Output Parameters:
4548 + numCoveredPoints - The number of points in the meet
4549 - coveredPoints - The points in the meet
4550 
4551   Level: intermediate
4552 
4553   Fortran Note:
4554   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4555 
4556 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4557 @*/
4558 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4559 {
4560   PetscInt *offsets, **closures;
4561   PetscInt *meet[2];
4562   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4563   PetscInt  p, h, c, m, mc;
4564 
4565   PetscFunctionBegin;
4566   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4567   PetscValidIntPointer(points, 3);
4568   PetscValidIntPointer(numCoveredPoints, 4);
4569   PetscValidPointer(coveredPoints, 5);
4570 
4571   PetscCall(DMPlexGetDepth(dm, &height));
4572   PetscCall(PetscMalloc1(numPoints, &closures));
4573   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4574   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4575   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4576   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4577   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4578 
4579   for (p = 0; p < numPoints; ++p) {
4580     PetscInt closureSize;
4581 
4582     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4583 
4584     offsets[p * (height + 2) + 0] = 0;
4585     for (h = 0; h < height + 1; ++h) {
4586       PetscInt pStart, pEnd, i;
4587 
4588       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4589       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4590         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4591           offsets[p * (height + 2) + h + 1] = i;
4592           break;
4593         }
4594       }
4595       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4596     }
4597     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);
4598   }
4599   for (h = 0; h < height + 1; ++h) {
4600     PetscInt dof;
4601 
4602     /* Copy in cone of first point */
4603     dof = offsets[h + 1] - offsets[h];
4604     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4605     /* Check each successive cone */
4606     for (p = 1; p < numPoints && meetSize; ++p) {
4607       PetscInt newMeetSize = 0;
4608 
4609       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4610       for (c = 0; c < dof; ++c) {
4611         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4612 
4613         for (m = 0; m < meetSize; ++m) {
4614           if (point == meet[i][m]) {
4615             meet[1 - i][newMeetSize++] = point;
4616             break;
4617           }
4618         }
4619       }
4620       meetSize = newMeetSize;
4621       i        = 1 - i;
4622     }
4623     if (meetSize) break;
4624   }
4625   *numCoveredPoints = meetSize;
4626   *coveredPoints    = meet[i];
4627   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4628   PetscCall(PetscFree(closures));
4629   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4630   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4631   PetscFunctionReturn(0);
4632 }
4633 
4634 /*@C
4635   DMPlexEqual - Determine if two `DM` have the same topology
4636 
4637   Not Collective
4638 
4639   Input Parameters:
4640 + dmA - A `DMPLEX` object
4641 - dmB - A `DMPLEX` object
4642 
4643   Output Parameters:
4644 . equal - `PETSC_TRUE` if the topologies are identical
4645 
4646   Level: intermediate
4647 
4648   Note:
4649   We are not solving graph isomorphism, so we do not permute.
4650 
4651 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4652 @*/
4653 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4654 {
4655   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4656 
4657   PetscFunctionBegin;
4658   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4659   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4660   PetscValidBoolPointer(equal, 3);
4661 
4662   *equal = PETSC_FALSE;
4663   PetscCall(DMPlexGetDepth(dmA, &depth));
4664   PetscCall(DMPlexGetDepth(dmB, &depthB));
4665   if (depth != depthB) PetscFunctionReturn(0);
4666   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4667   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4668   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4669   for (p = pStart; p < pEnd; ++p) {
4670     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4671     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4672 
4673     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4674     PetscCall(DMPlexGetCone(dmA, p, &cone));
4675     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4676     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4677     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4678     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4679     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4680     for (c = 0; c < coneSize; ++c) {
4681       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4682       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4683     }
4684     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4685     PetscCall(DMPlexGetSupport(dmA, p, &support));
4686     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4687     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4688     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4689     for (s = 0; s < supportSize; ++s) {
4690       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4691     }
4692   }
4693   *equal = PETSC_TRUE;
4694   PetscFunctionReturn(0);
4695 }
4696 
4697 /*@C
4698   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4699 
4700   Not Collective
4701 
4702   Input Parameters:
4703 + dm         - The `DMPLEX`
4704 . cellDim    - The cell dimension
4705 - numCorners - The number of vertices on a cell
4706 
4707   Output Parameters:
4708 . numFaceVertices - The number of vertices on a face
4709 
4710   Level: developer
4711 
4712   Note:
4713   Of course this can only work for a restricted set of symmetric shapes
4714 
4715 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4716 @*/
4717 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4718 {
4719   MPI_Comm comm;
4720 
4721   PetscFunctionBegin;
4722   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4723   PetscValidIntPointer(numFaceVertices, 4);
4724   switch (cellDim) {
4725   case 0:
4726     *numFaceVertices = 0;
4727     break;
4728   case 1:
4729     *numFaceVertices = 1;
4730     break;
4731   case 2:
4732     switch (numCorners) {
4733     case 3:                 /* triangle */
4734       *numFaceVertices = 2; /* Edge has 2 vertices */
4735       break;
4736     case 4:                 /* quadrilateral */
4737       *numFaceVertices = 2; /* Edge has 2 vertices */
4738       break;
4739     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4740       *numFaceVertices = 3; /* Edge has 3 vertices */
4741       break;
4742     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4743       *numFaceVertices = 3; /* Edge has 3 vertices */
4744       break;
4745     default:
4746       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4747     }
4748     break;
4749   case 3:
4750     switch (numCorners) {
4751     case 4:                 /* tetradehdron */
4752       *numFaceVertices = 3; /* Face has 3 vertices */
4753       break;
4754     case 6:                 /* tet cohesive cells */
4755       *numFaceVertices = 4; /* Face has 4 vertices */
4756       break;
4757     case 8:                 /* hexahedron */
4758       *numFaceVertices = 4; /* Face has 4 vertices */
4759       break;
4760     case 9:                 /* tet cohesive Lagrange cells */
4761       *numFaceVertices = 6; /* Face has 6 vertices */
4762       break;
4763     case 10:                /* quadratic tetrahedron */
4764       *numFaceVertices = 6; /* Face has 6 vertices */
4765       break;
4766     case 12:                /* hex cohesive Lagrange cells */
4767       *numFaceVertices = 6; /* Face has 6 vertices */
4768       break;
4769     case 18:                /* quadratic tet cohesive Lagrange cells */
4770       *numFaceVertices = 6; /* Face has 6 vertices */
4771       break;
4772     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4773       *numFaceVertices = 9; /* Face has 9 vertices */
4774       break;
4775     default:
4776       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4777     }
4778     break;
4779   default:
4780     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4781   }
4782   PetscFunctionReturn(0);
4783 }
4784 
4785 /*@
4786   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4787 
4788   Not Collective
4789 
4790   Input Parameter:
4791 . dm    - The `DMPLEX` object
4792 
4793   Output Parameter:
4794 . depthLabel - The `DMLabel` recording point depth
4795 
4796   Level: developer
4797 
4798 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4799 @*/
4800 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4801 {
4802   PetscFunctionBegin;
4803   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4804   PetscValidPointer(depthLabel, 2);
4805   *depthLabel = dm->depthLabel;
4806   PetscFunctionReturn(0);
4807 }
4808 
4809 /*@
4810   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4811 
4812   Not Collective
4813 
4814   Input Parameter:
4815 . dm    - The `DMPLEX` object
4816 
4817   Output Parameter:
4818 . depth - The number of strata (breadth first levels) in the DAG
4819 
4820   Level: developer
4821 
4822   Notes:
4823   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4824 
4825   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4826 
4827   An empty mesh gives -1.
4828 
4829 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4830 @*/
4831 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4832 {
4833   DMLabel  label;
4834   PetscInt d = 0;
4835 
4836   PetscFunctionBegin;
4837   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4838   PetscValidIntPointer(depth, 2);
4839   PetscCall(DMPlexGetDepthLabel(dm, &label));
4840   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4841   *depth = d - 1;
4842   PetscFunctionReturn(0);
4843 }
4844 
4845 /*@
4846   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4847 
4848   Not Collective
4849 
4850   Input Parameters:
4851 + dm    - The `DMPLEX` object
4852 - depth - The requested depth
4853 
4854   Output Parameters:
4855 + start - The first point at this depth
4856 - end   - One beyond the last point at this depth
4857 
4858   Level: developer
4859 
4860   Notes:
4861   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4862   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
4863   higher dimension, e.g., "edges".
4864 
4865 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4866 @*/
4867 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4868 {
4869   DMLabel  label;
4870   PetscInt pStart, pEnd;
4871 
4872   PetscFunctionBegin;
4873   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4874   if (start) {
4875     PetscValidIntPointer(start, 3);
4876     *start = 0;
4877   }
4878   if (end) {
4879     PetscValidIntPointer(end, 4);
4880     *end = 0;
4881   }
4882   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4883   if (pStart == pEnd) PetscFunctionReturn(0);
4884   if (depth < 0) {
4885     if (start) *start = pStart;
4886     if (end) *end = pEnd;
4887     PetscFunctionReturn(0);
4888   }
4889   PetscCall(DMPlexGetDepthLabel(dm, &label));
4890   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4891   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4892   PetscFunctionReturn(0);
4893 }
4894 
4895 /*@
4896   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4897 
4898   Not Collective
4899 
4900   Input Parameters:
4901 + dm     - The `DMPLEX` object
4902 - height - The requested height
4903 
4904   Output Parameters:
4905 + start - The first point at this height
4906 - end   - One beyond the last point at this height
4907 
4908   Level: developer
4909 
4910   Notes:
4911   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4912   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
4913   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4914 
4915 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4916 @*/
4917 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4918 {
4919   DMLabel  label;
4920   PetscInt depth, pStart, pEnd;
4921 
4922   PetscFunctionBegin;
4923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4924   if (start) {
4925     PetscValidIntPointer(start, 3);
4926     *start = 0;
4927   }
4928   if (end) {
4929     PetscValidIntPointer(end, 4);
4930     *end = 0;
4931   }
4932   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4933   if (pStart == pEnd) PetscFunctionReturn(0);
4934   if (height < 0) {
4935     if (start) *start = pStart;
4936     if (end) *end = pEnd;
4937     PetscFunctionReturn(0);
4938   }
4939   PetscCall(DMPlexGetDepthLabel(dm, &label));
4940   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4941   PetscCall(DMLabelGetNumValues(label, &depth));
4942   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
4943   PetscFunctionReturn(0);
4944 }
4945 
4946 /*@
4947   DMPlexGetPointDepth - Get the depth of a given point
4948 
4949   Not Collective
4950 
4951   Input Parameters:
4952 + dm    - The `DMPLEX` object
4953 - point - The point
4954 
4955   Output Parameter:
4956 . depth - The depth of the point
4957 
4958   Level: intermediate
4959 
4960 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4961 @*/
4962 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4963 {
4964   PetscFunctionBegin;
4965   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4966   PetscValidIntPointer(depth, 3);
4967   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4968   PetscFunctionReturn(0);
4969 }
4970 
4971 /*@
4972   DMPlexGetPointHeight - Get the height of a given point
4973 
4974   Not Collective
4975 
4976   Input Parameters:
4977 + dm    - The `DMPLEX` object
4978 - point - The point
4979 
4980   Output Parameter:
4981 . height - The height of the point
4982 
4983   Level: intermediate
4984 
4985 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4986 @*/
4987 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4988 {
4989   PetscInt n, pDepth;
4990 
4991   PetscFunctionBegin;
4992   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4993   PetscValidIntPointer(height, 3);
4994   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4995   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4996   *height = n - 1 - pDepth; /* DAG depth is n-1 */
4997   PetscFunctionReturn(0);
4998 }
4999 
5000 /*@
5001   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5002 
5003   Not Collective
5004 
5005   Input Parameter:
5006 . dm - The `DMPLEX` object
5007 
5008   Output Parameter:
5009 . celltypeLabel - The `DMLabel` recording cell polytope type
5010 
5011   Level: developer
5012 
5013   Note:
5014   This function will trigger automatica computation of cell types. This can be disabled by calling
5015   `DMCreateLabel`(dm, "celltype") beforehand.
5016 
5017 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5018 @*/
5019 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5020 {
5021   PetscFunctionBegin;
5022   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5023   PetscValidPointer(celltypeLabel, 2);
5024   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5025   *celltypeLabel = dm->celltypeLabel;
5026   PetscFunctionReturn(0);
5027 }
5028 
5029 /*@
5030   DMPlexGetCellType - Get the polytope type of a given cell
5031 
5032   Not Collective
5033 
5034   Input Parameters:
5035 + dm   - The `DMPLEX` object
5036 - cell - The cell
5037 
5038   Output Parameter:
5039 . celltype - The polytope type of the cell
5040 
5041   Level: intermediate
5042 
5043 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5044 @*/
5045 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5046 {
5047   DMLabel  label;
5048   PetscInt ct;
5049 
5050   PetscFunctionBegin;
5051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5052   PetscValidPointer(celltype, 3);
5053   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5054   PetscCall(DMLabelGetValue(label, cell, &ct));
5055   PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5056   *celltype = (DMPolytopeType)ct;
5057   PetscFunctionReturn(0);
5058 }
5059 
5060 /*@
5061   DMPlexSetCellType - Set the polytope type of a given cell
5062 
5063   Not Collective
5064 
5065   Input Parameters:
5066 + dm   - The `DMPLEX` object
5067 . cell - The cell
5068 - celltype - The polytope type of the cell
5069 
5070   Level: advanced
5071 
5072   Note:
5073   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5074   is executed. This function will override the computed type. However, if automatic classification will not succeed
5075   and a user wants to manually specify all types, the classification must be disabled by calling
5076   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5077 
5078 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5079 @*/
5080 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5081 {
5082   DMLabel label;
5083 
5084   PetscFunctionBegin;
5085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5086   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5087   PetscCall(DMLabelSetValue(label, cell, celltype));
5088   PetscFunctionReturn(0);
5089 }
5090 
5091 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5092 {
5093   PetscSection section, s;
5094   Mat          m;
5095   PetscInt     maxHeight;
5096 
5097   PetscFunctionBegin;
5098   PetscCall(DMClone(dm, cdm));
5099   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5100   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5101   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5102   PetscCall(DMSetLocalSection(*cdm, section));
5103   PetscCall(PetscSectionDestroy(&section));
5104   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5105   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5106   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5107   PetscCall(PetscSectionDestroy(&s));
5108   PetscCall(MatDestroy(&m));
5109 
5110   PetscCall(DMSetNumFields(*cdm, 1));
5111   PetscCall(DMCreateDS(*cdm));
5112   PetscFunctionReturn(0);
5113 }
5114 
5115 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5116 {
5117   Vec coordsLocal, cellCoordsLocal;
5118   DM  coordsDM, cellCoordsDM;
5119 
5120   PetscFunctionBegin;
5121   *field = NULL;
5122   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5123   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5124   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5125   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5126   if (coordsLocal && coordsDM) {
5127     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5128     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5129   }
5130   PetscFunctionReturn(0);
5131 }
5132 
5133 /*@C
5134   DMPlexGetConeSection - Return a section which describes the layout of cone data
5135 
5136   Not Collective
5137 
5138   Input Parameters:
5139 . dm        - The `DMPLEX` object
5140 
5141   Output Parameter:
5142 . section - The `PetscSection` object
5143 
5144   Level: developer
5145 
5146 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5147 @*/
5148 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5149 {
5150   DM_Plex *mesh = (DM_Plex *)dm->data;
5151 
5152   PetscFunctionBegin;
5153   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5154   if (section) *section = mesh->coneSection;
5155   PetscFunctionReturn(0);
5156 }
5157 
5158 /*@C
5159   DMPlexGetSupportSection - Return a section which describes the layout of support data
5160 
5161   Not Collective
5162 
5163   Input Parameters:
5164 . dm        - The `DMPLEX` object
5165 
5166   Output Parameter:
5167 . section - The `PetscSection` object
5168 
5169   Level: developer
5170 
5171 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5172 @*/
5173 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5174 {
5175   DM_Plex *mesh = (DM_Plex *)dm->data;
5176 
5177   PetscFunctionBegin;
5178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5179   if (section) *section = mesh->supportSection;
5180   PetscFunctionReturn(0);
5181 }
5182 
5183 /*@C
5184   DMPlexGetCones - Return cone data
5185 
5186   Not Collective
5187 
5188   Input Parameters:
5189 . dm        - The `DMPLEX` object
5190 
5191   Output Parameter:
5192 . cones - The cone for each point
5193 
5194   Level: developer
5195 
5196 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5197 @*/
5198 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5199 {
5200   DM_Plex *mesh = (DM_Plex *)dm->data;
5201 
5202   PetscFunctionBegin;
5203   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5204   if (cones) *cones = mesh->cones;
5205   PetscFunctionReturn(0);
5206 }
5207 
5208 /*@C
5209   DMPlexGetConeOrientations - Return cone orientation data
5210 
5211   Not Collective
5212 
5213   Input Parameters:
5214 . dm        - The `DMPLEX` object
5215 
5216   Output Parameter:
5217 . coneOrientations - The array of cone orientations for all points
5218 
5219   Level: developer
5220 
5221   Notes:
5222   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5223 
5224   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5225 
5226 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5227 @*/
5228 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5229 {
5230   DM_Plex *mesh = (DM_Plex *)dm->data;
5231 
5232   PetscFunctionBegin;
5233   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5234   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5235   PetscFunctionReturn(0);
5236 }
5237 
5238 /******************************** FEM Support **********************************/
5239 
5240 /*
5241  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5242  representing a line in the section.
5243 */
5244 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5245 {
5246   PetscFunctionBeginHot;
5247   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5248   if (line < 0) {
5249     *k  = 0;
5250     *Nc = 0;
5251   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5252     *k = 1;
5253   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5254     /* An order k SEM disc has k-1 dofs on an edge */
5255     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5256     *k = *k / *Nc + 1;
5257   }
5258   PetscFunctionReturn(0);
5259 }
5260 
5261 /*@
5262 
5263   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5264   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5265   section provided (or the section of the DM).
5266 
5267   Input Parameters:
5268 + dm      - The DM
5269 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5270 - section - The PetscSection to reorder, or NULL for the default section
5271 
5272   Example:
5273   A typical interpolated single-quad mesh might order points as
5274 .vb
5275   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5276 
5277   v4 -- e6 -- v3
5278   |           |
5279   e7    c0    e8
5280   |           |
5281   v1 -- e5 -- v2
5282 .ve
5283 
5284   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5285   dofs in the order of points, e.g.,
5286 .vb
5287     c0 -> [0,1,2,3]
5288     v1 -> [4]
5289     ...
5290     e5 -> [8, 9]
5291 .ve
5292 
5293   which corresponds to the dofs
5294 .vb
5295     6   10  11  7
5296     13  2   3   15
5297     12  0   1   14
5298     4   8   9   5
5299 .ve
5300 
5301   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5302 .vb
5303   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5304 .ve
5305 
5306   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5307 .vb
5308    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5309 .ve
5310 
5311   Level: developer
5312 
5313   Note:
5314   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5315   degree of the basis.
5316 
5317 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5318 @*/
5319 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5320 {
5321   DMLabel   label;
5322   PetscInt  dim, depth = -1, eStart = -1, Nf;
5323   PetscBool vertexchart;
5324 
5325   PetscFunctionBegin;
5326   PetscCall(DMGetDimension(dm, &dim));
5327   if (dim < 1) PetscFunctionReturn(0);
5328   if (point < 0) {
5329     PetscInt sStart, sEnd;
5330 
5331     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5332     point = sEnd - sStart ? sStart : point;
5333   }
5334   PetscCall(DMPlexGetDepthLabel(dm, &label));
5335   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5336   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5337   if (depth == 1) {
5338     eStart = point;
5339   } else if (depth == dim) {
5340     const PetscInt *cone;
5341 
5342     PetscCall(DMPlexGetCone(dm, point, &cone));
5343     if (dim == 2) eStart = cone[0];
5344     else if (dim == 3) {
5345       const PetscInt *cone2;
5346       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5347       eStart = cone2[0];
5348     } 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);
5349   } 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);
5350   { /* Determine whether the chart covers all points or just vertices. */
5351     PetscInt pStart, pEnd, cStart, cEnd;
5352     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5353     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5354     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5355     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5356     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5357   }
5358   PetscCall(PetscSectionGetNumFields(section, &Nf));
5359   for (PetscInt d = 1; d <= dim; d++) {
5360     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5361     PetscInt *perm;
5362 
5363     for (f = 0; f < Nf; ++f) {
5364       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5365       size += PetscPowInt(k + 1, d) * Nc;
5366     }
5367     PetscCall(PetscMalloc1(size, &perm));
5368     for (f = 0; f < Nf; ++f) {
5369       switch (d) {
5370       case 1:
5371         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5372         /*
5373          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5374          We want              [ vtx0; edge of length k-1; vtx1 ]
5375          */
5376         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5377         for (i = 0; i < k - 1; i++)
5378           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5379         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5380         foffset = offset;
5381         break;
5382       case 2:
5383         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5384         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5385         /* The SEM order is
5386 
5387          v_lb, {e_b}, v_rb,
5388          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5389          v_lt, reverse {e_t}, v_rt
5390          */
5391         {
5392           const PetscInt of   = 0;
5393           const PetscInt oeb  = of + PetscSqr(k - 1);
5394           const PetscInt oer  = oeb + (k - 1);
5395           const PetscInt oet  = oer + (k - 1);
5396           const PetscInt oel  = oet + (k - 1);
5397           const PetscInt ovlb = oel + (k - 1);
5398           const PetscInt ovrb = ovlb + 1;
5399           const PetscInt ovrt = ovrb + 1;
5400           const PetscInt ovlt = ovrt + 1;
5401           PetscInt       o;
5402 
5403           /* bottom */
5404           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5405           for (o = oeb; o < oer; ++o)
5406             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5407           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5408           /* middle */
5409           for (i = 0; i < k - 1; ++i) {
5410             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5411             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5412               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5413             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5414           }
5415           /* top */
5416           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5417           for (o = oel - 1; o >= oet; --o)
5418             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5419           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5420           foffset = offset;
5421         }
5422         break;
5423       case 3:
5424         /* The original hex closure is
5425 
5426          {c,
5427          f_b, f_t, f_f, f_b, f_r, f_l,
5428          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5429          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5430          */
5431         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5432         /* The SEM order is
5433          Bottom Slice
5434          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5435          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5436          v_blb, {e_bb}, v_brb,
5437 
5438          Middle Slice (j)
5439          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5440          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5441          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5442 
5443          Top Slice
5444          v_tlf, {e_tf}, v_trf,
5445          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5446          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5447          */
5448         {
5449           const PetscInt oc    = 0;
5450           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5451           const PetscInt oft   = ofb + PetscSqr(k - 1);
5452           const PetscInt off   = oft + PetscSqr(k - 1);
5453           const PetscInt ofk   = off + PetscSqr(k - 1);
5454           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5455           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5456           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5457           const PetscInt oebb  = oebl + (k - 1);
5458           const PetscInt oebr  = oebb + (k - 1);
5459           const PetscInt oebf  = oebr + (k - 1);
5460           const PetscInt oetf  = oebf + (k - 1);
5461           const PetscInt oetr  = oetf + (k - 1);
5462           const PetscInt oetb  = oetr + (k - 1);
5463           const PetscInt oetl  = oetb + (k - 1);
5464           const PetscInt oerf  = oetl + (k - 1);
5465           const PetscInt oelf  = oerf + (k - 1);
5466           const PetscInt oelb  = oelf + (k - 1);
5467           const PetscInt oerb  = oelb + (k - 1);
5468           const PetscInt ovblf = oerb + (k - 1);
5469           const PetscInt ovblb = ovblf + 1;
5470           const PetscInt ovbrb = ovblb + 1;
5471           const PetscInt ovbrf = ovbrb + 1;
5472           const PetscInt ovtlf = ovbrf + 1;
5473           const PetscInt ovtrf = ovtlf + 1;
5474           const PetscInt ovtrb = ovtrf + 1;
5475           const PetscInt ovtlb = ovtrb + 1;
5476           PetscInt       o, n;
5477 
5478           /* Bottom Slice */
5479           /*   bottom */
5480           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5481           for (o = oetf - 1; o >= oebf; --o)
5482             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5483           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5484           /*   middle */
5485           for (i = 0; i < k - 1; ++i) {
5486             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5487             for (n = 0; n < k - 1; ++n) {
5488               o = ofb + n * (k - 1) + i;
5489               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5490             }
5491             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5492           }
5493           /*   top */
5494           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5495           for (o = oebb; o < oebr; ++o)
5496             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5497           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5498 
5499           /* Middle Slice */
5500           for (j = 0; j < k - 1; ++j) {
5501             /*   bottom */
5502             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5503             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5504               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5505             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5506             /*   middle */
5507             for (i = 0; i < k - 1; ++i) {
5508               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5509               for (n = 0; n < k - 1; ++n)
5510                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5511               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5512             }
5513             /*   top */
5514             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5515             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5516               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5517             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5518           }
5519 
5520           /* Top Slice */
5521           /*   bottom */
5522           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5523           for (o = oetf; o < oetr; ++o)
5524             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5525           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5526           /*   middle */
5527           for (i = 0; i < k - 1; ++i) {
5528             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5529             for (n = 0; n < k - 1; ++n)
5530               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5531             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5532           }
5533           /*   top */
5534           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5535           for (o = oetl - 1; o >= oetb; --o)
5536             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5537           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5538 
5539           foffset = offset;
5540         }
5541         break;
5542       default:
5543         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5544       }
5545     }
5546     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5547     /* Check permutation */
5548     {
5549       PetscInt *check;
5550 
5551       PetscCall(PetscMalloc1(size, &check));
5552       for (i = 0; i < size; ++i) {
5553         check[i] = -1;
5554         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5555       }
5556       for (i = 0; i < size; ++i) check[perm[i]] = i;
5557       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5558       PetscCall(PetscFree(check));
5559     }
5560     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5561     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5562       PetscInt *loc_perm;
5563       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5564       for (PetscInt i = 0; i < size; i++) {
5565         loc_perm[i]        = perm[i];
5566         loc_perm[size + i] = size + perm[i];
5567       }
5568       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5569     }
5570   }
5571   PetscFunctionReturn(0);
5572 }
5573 
5574 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5575 {
5576   PetscDS  prob;
5577   PetscInt depth, Nf, h;
5578   DMLabel  label;
5579 
5580   PetscFunctionBeginHot;
5581   PetscCall(DMGetDS(dm, &prob));
5582   Nf      = prob->Nf;
5583   label   = dm->depthLabel;
5584   *dspace = NULL;
5585   if (field < Nf) {
5586     PetscObject disc = prob->disc[field];
5587 
5588     if (disc->classid == PETSCFE_CLASSID) {
5589       PetscDualSpace dsp;
5590 
5591       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5592       PetscCall(DMLabelGetNumValues(label, &depth));
5593       PetscCall(DMLabelGetValue(label, point, &h));
5594       h = depth - 1 - h;
5595       if (h) {
5596         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5597       } else {
5598         *dspace = dsp;
5599       }
5600     }
5601   }
5602   PetscFunctionReturn(0);
5603 }
5604 
5605 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5606 {
5607   PetscScalar       *array;
5608   const PetscScalar *vArray;
5609   const PetscInt    *cone, *coneO;
5610   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5611 
5612   PetscFunctionBeginHot;
5613   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5614   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5615   PetscCall(DMPlexGetCone(dm, point, &cone));
5616   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5617   if (!values || !*values) {
5618     if ((point >= pStart) && (point < pEnd)) {
5619       PetscInt dof;
5620 
5621       PetscCall(PetscSectionGetDof(section, point, &dof));
5622       size += dof;
5623     }
5624     for (p = 0; p < numPoints; ++p) {
5625       const PetscInt cp = cone[p];
5626       PetscInt       dof;
5627 
5628       if ((cp < pStart) || (cp >= pEnd)) continue;
5629       PetscCall(PetscSectionGetDof(section, cp, &dof));
5630       size += dof;
5631     }
5632     if (!values) {
5633       if (csize) *csize = size;
5634       PetscFunctionReturn(0);
5635     }
5636     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5637   } else {
5638     array = *values;
5639   }
5640   size = 0;
5641   PetscCall(VecGetArrayRead(v, &vArray));
5642   if ((point >= pStart) && (point < pEnd)) {
5643     PetscInt           dof, off, d;
5644     const PetscScalar *varr;
5645 
5646     PetscCall(PetscSectionGetDof(section, point, &dof));
5647     PetscCall(PetscSectionGetOffset(section, point, &off));
5648     varr = &vArray[off];
5649     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5650     size += dof;
5651   }
5652   for (p = 0; p < numPoints; ++p) {
5653     const PetscInt     cp = cone[p];
5654     PetscInt           o  = coneO[p];
5655     PetscInt           dof, off, d;
5656     const PetscScalar *varr;
5657 
5658     if ((cp < pStart) || (cp >= pEnd)) continue;
5659     PetscCall(PetscSectionGetDof(section, cp, &dof));
5660     PetscCall(PetscSectionGetOffset(section, cp, &off));
5661     varr = &vArray[off];
5662     if (o >= 0) {
5663       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5664     } else {
5665       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5666     }
5667     size += dof;
5668   }
5669   PetscCall(VecRestoreArrayRead(v, &vArray));
5670   if (!*values) {
5671     if (csize) *csize = size;
5672     *values = array;
5673   } else {
5674     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5675     *csize = size;
5676   }
5677   PetscFunctionReturn(0);
5678 }
5679 
5680 /* Compress out points not in the section */
5681 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5682 {
5683   const PetscInt np = *numPoints;
5684   PetscInt       pStart, pEnd, p, q;
5685 
5686   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5687   for (p = 0, q = 0; p < np; ++p) {
5688     const PetscInt r = points[p * 2];
5689     if ((r >= pStart) && (r < pEnd)) {
5690       points[q * 2]     = r;
5691       points[q * 2 + 1] = points[p * 2 + 1];
5692       ++q;
5693     }
5694   }
5695   *numPoints = q;
5696   return 0;
5697 }
5698 
5699 /* Compressed closure does not apply closure permutation */
5700 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5701 {
5702   const PetscInt *cla = NULL;
5703   PetscInt        np, *pts = NULL;
5704 
5705   PetscFunctionBeginHot;
5706   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5707   if (*clPoints) {
5708     PetscInt dof, off;
5709 
5710     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5711     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5712     PetscCall(ISGetIndices(*clPoints, &cla));
5713     np  = dof / 2;
5714     pts = (PetscInt *)&cla[off];
5715   } else {
5716     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5717     PetscCall(CompressPoints_Private(section, &np, pts));
5718   }
5719   *numPoints = np;
5720   *points    = pts;
5721   *clp       = cla;
5722   PetscFunctionReturn(0);
5723 }
5724 
5725 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5726 {
5727   PetscFunctionBeginHot;
5728   if (!*clPoints) {
5729     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5730   } else {
5731     PetscCall(ISRestoreIndices(*clPoints, clp));
5732   }
5733   *numPoints = 0;
5734   *points    = NULL;
5735   *clSec     = NULL;
5736   *clPoints  = NULL;
5737   *clp       = NULL;
5738   PetscFunctionReturn(0);
5739 }
5740 
5741 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5742 {
5743   PetscInt            offset = 0, p;
5744   const PetscInt    **perms  = NULL;
5745   const PetscScalar **flips  = NULL;
5746 
5747   PetscFunctionBeginHot;
5748   *size = 0;
5749   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5750   for (p = 0; p < numPoints; p++) {
5751     const PetscInt     point = points[2 * p];
5752     const PetscInt    *perm  = perms ? perms[p] : NULL;
5753     const PetscScalar *flip  = flips ? flips[p] : NULL;
5754     PetscInt           dof, off, d;
5755     const PetscScalar *varr;
5756 
5757     PetscCall(PetscSectionGetDof(section, point, &dof));
5758     PetscCall(PetscSectionGetOffset(section, point, &off));
5759     varr = &vArray[off];
5760     if (clperm) {
5761       if (perm) {
5762         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5763       } else {
5764         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5765       }
5766       if (flip) {
5767         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5768       }
5769     } else {
5770       if (perm) {
5771         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5772       } else {
5773         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5774       }
5775       if (flip) {
5776         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5777       }
5778     }
5779     offset += dof;
5780   }
5781   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5782   *size = offset;
5783   PetscFunctionReturn(0);
5784 }
5785 
5786 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[])
5787 {
5788   PetscInt offset = 0, f;
5789 
5790   PetscFunctionBeginHot;
5791   *size = 0;
5792   for (f = 0; f < numFields; ++f) {
5793     PetscInt            p;
5794     const PetscInt    **perms = NULL;
5795     const PetscScalar **flips = NULL;
5796 
5797     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5798     for (p = 0; p < numPoints; p++) {
5799       const PetscInt     point = points[2 * p];
5800       PetscInt           fdof, foff, b;
5801       const PetscScalar *varr;
5802       const PetscInt    *perm = perms ? perms[p] : NULL;
5803       const PetscScalar *flip = flips ? flips[p] : NULL;
5804 
5805       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5806       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5807       varr = &vArray[foff];
5808       if (clperm) {
5809         if (perm) {
5810           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5811         } else {
5812           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5813         }
5814         if (flip) {
5815           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5816         }
5817       } else {
5818         if (perm) {
5819           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5820         } else {
5821           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5822         }
5823         if (flip) {
5824           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5825         }
5826       }
5827       offset += fdof;
5828     }
5829     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5830   }
5831   *size = offset;
5832   PetscFunctionReturn(0);
5833 }
5834 
5835 /*@C
5836   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5837 
5838   Not collective
5839 
5840   Input Parameters:
5841 + dm - The `DM`
5842 . section - The section describing the layout in v, or NULL to use the default section
5843 . v - The local vector
5844 - point - The point in the `DM`
5845 
5846   Input/Output Parameters:
5847 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5848 - values - An array to use for the values, or NULL to have it allocated automatically;
5849            if the user provided NULL, it is a borrowed array and should not be freed
5850 
5851   Level: intermediate
5852 
5853   Notes:
5854   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to NULL in the
5855   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
5856   assembly function, and a user may already have allocated storage for this operation.
5857 
5858   A typical use could be
5859 .vb
5860    values = NULL;
5861    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5862    for (cl = 0; cl < clSize; ++cl) {
5863      <Compute on closure>
5864    }
5865    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5866 .ve
5867   or
5868 .vb
5869    PetscMalloc1(clMaxSize, &values);
5870    for (p = pStart; p < pEnd; ++p) {
5871      clSize = clMaxSize;
5872      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5873      for (cl = 0; cl < clSize; ++cl) {
5874        <Compute on closure>
5875      }
5876    }
5877    PetscFree(values);
5878 .ve
5879 
5880   Fortran Note:
5881   The csize argument is not present in the Fortran binding since it is internal to the array.
5882 
5883 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5884 @*/
5885 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5886 {
5887   PetscSection    clSection;
5888   IS              clPoints;
5889   PetscInt       *points = NULL;
5890   const PetscInt *clp, *perm;
5891   PetscInt        depth, numFields, numPoints, asize;
5892 
5893   PetscFunctionBeginHot;
5894   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5895   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5896   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5897   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5898   PetscCall(DMPlexGetDepth(dm, &depth));
5899   PetscCall(PetscSectionGetNumFields(section, &numFields));
5900   if (depth == 1 && numFields < 2) {
5901     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5902     PetscFunctionReturn(0);
5903   }
5904   /* Get points */
5905   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5906   /* Get sizes */
5907   asize = 0;
5908   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
5909     PetscInt dof;
5910     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5911     asize += dof;
5912   }
5913   if (values) {
5914     const PetscScalar *vArray;
5915     PetscInt           size;
5916 
5917     if (*values) {
5918       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);
5919     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5920     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
5921     PetscCall(VecGetArrayRead(v, &vArray));
5922     /* Get values */
5923     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5924     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5925     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5926     /* Cleanup array */
5927     PetscCall(VecRestoreArrayRead(v, &vArray));
5928   }
5929   if (csize) *csize = asize;
5930   /* Cleanup points */
5931   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5932   PetscFunctionReturn(0);
5933 }
5934 
5935 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5936 {
5937   DMLabel            depthLabel;
5938   PetscSection       clSection;
5939   IS                 clPoints;
5940   PetscScalar       *array;
5941   const PetscScalar *vArray;
5942   PetscInt          *points = NULL;
5943   const PetscInt    *clp, *perm = NULL;
5944   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5945 
5946   PetscFunctionBeginHot;
5947   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5948   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5949   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5950   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5951   PetscCall(DMPlexGetDepth(dm, &mdepth));
5952   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5953   PetscCall(PetscSectionGetNumFields(section, &numFields));
5954   if (mdepth == 1 && numFields < 2) {
5955     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5956     PetscFunctionReturn(0);
5957   }
5958   /* Get points */
5959   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5960   for (clsize = 0, p = 0; p < Np; p++) {
5961     PetscInt dof;
5962     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
5963     clsize += dof;
5964   }
5965   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
5966   /* Filter points */
5967   for (p = 0; p < numPoints * 2; p += 2) {
5968     PetscInt dep;
5969 
5970     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5971     if (dep != depth) continue;
5972     points[Np * 2 + 0] = points[p];
5973     points[Np * 2 + 1] = points[p + 1];
5974     ++Np;
5975   }
5976   /* Get array */
5977   if (!values || !*values) {
5978     PetscInt asize = 0, dof;
5979 
5980     for (p = 0; p < Np * 2; p += 2) {
5981       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5982       asize += dof;
5983     }
5984     if (!values) {
5985       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5986       if (csize) *csize = asize;
5987       PetscFunctionReturn(0);
5988     }
5989     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5990   } else {
5991     array = *values;
5992   }
5993   PetscCall(VecGetArrayRead(v, &vArray));
5994   /* Get values */
5995   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5996   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5997   /* Cleanup points */
5998   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5999   /* Cleanup array */
6000   PetscCall(VecRestoreArrayRead(v, &vArray));
6001   if (!*values) {
6002     if (csize) *csize = size;
6003     *values = array;
6004   } else {
6005     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6006     *csize = size;
6007   }
6008   PetscFunctionReturn(0);
6009 }
6010 
6011 /*@C
6012   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6013 
6014   Not collective
6015 
6016   Input Parameters:
6017 + dm - The `DM`
6018 . section - The section describing the layout in v, or NULL to use the default section
6019 . v - The local vector
6020 . point - The point in the `DM`
6021 . csize - The number of values in the closure, or NULL
6022 - values - The array of values, which is a borrowed array and should not be freed
6023 
6024   Level: intermediate
6025 
6026   Note:
6027   The array values are discarded and not copied back into v. In order to copy values back to v, use `DMPlexVecSetClosure()`
6028 
6029   Fortran Note:
6030   The csize argument is not present in the Fortran binding since it is internal to the array.
6031 
6032 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6033 @*/
6034 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6035 {
6036   PetscInt size = 0;
6037 
6038   PetscFunctionBegin;
6039   /* Should work without recalculating size */
6040   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6041   *values = NULL;
6042   PetscFunctionReturn(0);
6043 }
6044 
6045 static inline void add(PetscScalar *x, PetscScalar y)
6046 {
6047   *x += y;
6048 }
6049 static inline void insert(PetscScalar *x, PetscScalar y)
6050 {
6051   *x = y;
6052 }
6053 
6054 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[])
6055 {
6056   PetscInt        cdof;  /* The number of constraints on this point */
6057   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6058   PetscScalar    *a;
6059   PetscInt        off, cind = 0, k;
6060 
6061   PetscFunctionBegin;
6062   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6063   PetscCall(PetscSectionGetOffset(section, point, &off));
6064   a = &array[off];
6065   if (!cdof || setBC) {
6066     if (clperm) {
6067       if (perm) {
6068         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6069       } else {
6070         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6071       }
6072     } else {
6073       if (perm) {
6074         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6075       } else {
6076         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6077       }
6078     }
6079   } else {
6080     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6081     if (clperm) {
6082       if (perm) {
6083         for (k = 0; k < dof; ++k) {
6084           if ((cind < cdof) && (k == cdofs[cind])) {
6085             ++cind;
6086             continue;
6087           }
6088           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6089         }
6090       } else {
6091         for (k = 0; k < dof; ++k) {
6092           if ((cind < cdof) && (k == cdofs[cind])) {
6093             ++cind;
6094             continue;
6095           }
6096           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6097         }
6098       }
6099     } else {
6100       if (perm) {
6101         for (k = 0; k < dof; ++k) {
6102           if ((cind < cdof) && (k == cdofs[cind])) {
6103             ++cind;
6104             continue;
6105           }
6106           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6107         }
6108       } else {
6109         for (k = 0; k < dof; ++k) {
6110           if ((cind < cdof) && (k == cdofs[cind])) {
6111             ++cind;
6112             continue;
6113           }
6114           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6115         }
6116       }
6117     }
6118   }
6119   PetscFunctionReturn(0);
6120 }
6121 
6122 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[])
6123 {
6124   PetscInt        cdof;  /* The number of constraints on this point */
6125   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6126   PetscScalar    *a;
6127   PetscInt        off, cind = 0, k;
6128 
6129   PetscFunctionBegin;
6130   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6131   PetscCall(PetscSectionGetOffset(section, point, &off));
6132   a = &array[off];
6133   if (cdof) {
6134     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6135     if (clperm) {
6136       if (perm) {
6137         for (k = 0; k < dof; ++k) {
6138           if ((cind < cdof) && (k == cdofs[cind])) {
6139             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6140             cind++;
6141           }
6142         }
6143       } else {
6144         for (k = 0; k < dof; ++k) {
6145           if ((cind < cdof) && (k == cdofs[cind])) {
6146             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6147             cind++;
6148           }
6149         }
6150       }
6151     } else {
6152       if (perm) {
6153         for (k = 0; k < dof; ++k) {
6154           if ((cind < cdof) && (k == cdofs[cind])) {
6155             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6156             cind++;
6157           }
6158         }
6159       } else {
6160         for (k = 0; k < dof; ++k) {
6161           if ((cind < cdof) && (k == cdofs[cind])) {
6162             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6163             cind++;
6164           }
6165         }
6166       }
6167     }
6168   }
6169   PetscFunctionReturn(0);
6170 }
6171 
6172 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[])
6173 {
6174   PetscScalar    *a;
6175   PetscInt        fdof, foff, fcdof, foffset = *offset;
6176   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6177   PetscInt        cind = 0, b;
6178 
6179   PetscFunctionBegin;
6180   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6181   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6182   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6183   a = &array[foff];
6184   if (!fcdof || setBC) {
6185     if (clperm) {
6186       if (perm) {
6187         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6188       } else {
6189         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6190       }
6191     } else {
6192       if (perm) {
6193         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6194       } else {
6195         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6196       }
6197     }
6198   } else {
6199     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6200     if (clperm) {
6201       if (perm) {
6202         for (b = 0; b < fdof; b++) {
6203           if ((cind < fcdof) && (b == fcdofs[cind])) {
6204             ++cind;
6205             continue;
6206           }
6207           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6208         }
6209       } else {
6210         for (b = 0; b < fdof; b++) {
6211           if ((cind < fcdof) && (b == fcdofs[cind])) {
6212             ++cind;
6213             continue;
6214           }
6215           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6216         }
6217       }
6218     } else {
6219       if (perm) {
6220         for (b = 0; b < fdof; b++) {
6221           if ((cind < fcdof) && (b == fcdofs[cind])) {
6222             ++cind;
6223             continue;
6224           }
6225           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6226         }
6227       } else {
6228         for (b = 0; b < fdof; b++) {
6229           if ((cind < fcdof) && (b == fcdofs[cind])) {
6230             ++cind;
6231             continue;
6232           }
6233           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6234         }
6235       }
6236     }
6237   }
6238   *offset += fdof;
6239   PetscFunctionReturn(0);
6240 }
6241 
6242 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[])
6243 {
6244   PetscScalar    *a;
6245   PetscInt        fdof, foff, fcdof, foffset = *offset;
6246   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6247   PetscInt        Nc, cind = 0, ncind = 0, b;
6248   PetscBool       ncSet, fcSet;
6249 
6250   PetscFunctionBegin;
6251   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6252   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6253   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6254   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6255   a = &array[foff];
6256   if (fcdof) {
6257     /* We just override fcdof and fcdofs with Ncc and comps */
6258     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6259     if (clperm) {
6260       if (perm) {
6261         if (comps) {
6262           for (b = 0; b < fdof; b++) {
6263             ncSet = fcSet = PETSC_FALSE;
6264             if (b % Nc == comps[ncind]) {
6265               ncind = (ncind + 1) % Ncc;
6266               ncSet = PETSC_TRUE;
6267             }
6268             if ((cind < fcdof) && (b == fcdofs[cind])) {
6269               ++cind;
6270               fcSet = PETSC_TRUE;
6271             }
6272             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6273           }
6274         } else {
6275           for (b = 0; b < fdof; b++) {
6276             if ((cind < fcdof) && (b == fcdofs[cind])) {
6277               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6278               ++cind;
6279             }
6280           }
6281         }
6282       } else {
6283         if (comps) {
6284           for (b = 0; b < fdof; b++) {
6285             ncSet = fcSet = PETSC_FALSE;
6286             if (b % Nc == comps[ncind]) {
6287               ncind = (ncind + 1) % Ncc;
6288               ncSet = PETSC_TRUE;
6289             }
6290             if ((cind < fcdof) && (b == fcdofs[cind])) {
6291               ++cind;
6292               fcSet = PETSC_TRUE;
6293             }
6294             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6295           }
6296         } else {
6297           for (b = 0; b < fdof; b++) {
6298             if ((cind < fcdof) && (b == fcdofs[cind])) {
6299               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6300               ++cind;
6301             }
6302           }
6303         }
6304       }
6305     } else {
6306       if (perm) {
6307         if (comps) {
6308           for (b = 0; b < fdof; b++) {
6309             ncSet = fcSet = PETSC_FALSE;
6310             if (b % Nc == comps[ncind]) {
6311               ncind = (ncind + 1) % Ncc;
6312               ncSet = PETSC_TRUE;
6313             }
6314             if ((cind < fcdof) && (b == fcdofs[cind])) {
6315               ++cind;
6316               fcSet = PETSC_TRUE;
6317             }
6318             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6319           }
6320         } else {
6321           for (b = 0; b < fdof; b++) {
6322             if ((cind < fcdof) && (b == fcdofs[cind])) {
6323               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6324               ++cind;
6325             }
6326           }
6327         }
6328       } else {
6329         if (comps) {
6330           for (b = 0; b < fdof; b++) {
6331             ncSet = fcSet = PETSC_FALSE;
6332             if (b % Nc == comps[ncind]) {
6333               ncind = (ncind + 1) % Ncc;
6334               ncSet = PETSC_TRUE;
6335             }
6336             if ((cind < fcdof) && (b == fcdofs[cind])) {
6337               ++cind;
6338               fcSet = PETSC_TRUE;
6339             }
6340             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6341           }
6342         } else {
6343           for (b = 0; b < fdof; b++) {
6344             if ((cind < fcdof) && (b == fcdofs[cind])) {
6345               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6346               ++cind;
6347             }
6348           }
6349         }
6350       }
6351     }
6352   }
6353   *offset += fdof;
6354   PetscFunctionReturn(0);
6355 }
6356 
6357 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6358 {
6359   PetscScalar    *array;
6360   const PetscInt *cone, *coneO;
6361   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6362 
6363   PetscFunctionBeginHot;
6364   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6365   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6366   PetscCall(DMPlexGetCone(dm, point, &cone));
6367   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6368   PetscCall(VecGetArray(v, &array));
6369   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6370     const PetscInt cp = !p ? point : cone[p - 1];
6371     const PetscInt o  = !p ? 0 : coneO[p - 1];
6372 
6373     if ((cp < pStart) || (cp >= pEnd)) {
6374       dof = 0;
6375       continue;
6376     }
6377     PetscCall(PetscSectionGetDof(section, cp, &dof));
6378     /* ADD_VALUES */
6379     {
6380       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6381       PetscScalar    *a;
6382       PetscInt        cdof, coff, cind = 0, k;
6383 
6384       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6385       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6386       a = &array[coff];
6387       if (!cdof) {
6388         if (o >= 0) {
6389           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6390         } else {
6391           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6392         }
6393       } else {
6394         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6395         if (o >= 0) {
6396           for (k = 0; k < dof; ++k) {
6397             if ((cind < cdof) && (k == cdofs[cind])) {
6398               ++cind;
6399               continue;
6400             }
6401             a[k] += values[off + k];
6402           }
6403         } else {
6404           for (k = 0; k < dof; ++k) {
6405             if ((cind < cdof) && (k == cdofs[cind])) {
6406               ++cind;
6407               continue;
6408             }
6409             a[k] += values[off + dof - k - 1];
6410           }
6411         }
6412       }
6413     }
6414   }
6415   PetscCall(VecRestoreArray(v, &array));
6416   PetscFunctionReturn(0);
6417 }
6418 
6419 /*@C
6420   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6421 
6422   Not collective
6423 
6424   Input Parameters:
6425 + dm - The `DM`
6426 . section - The section describing the layout in v, or NULL to use the default section
6427 . v - The local vector
6428 . point - The point in the DM
6429 . values - The array of values
6430 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6431          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6432 
6433   Level: intermediate
6434 
6435 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6436 @*/
6437 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6438 {
6439   PetscSection    clSection;
6440   IS              clPoints;
6441   PetscScalar    *array;
6442   PetscInt       *points = NULL;
6443   const PetscInt *clp, *clperm = NULL;
6444   PetscInt        depth, numFields, numPoints, p, clsize;
6445 
6446   PetscFunctionBeginHot;
6447   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6448   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6449   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6450   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6451   PetscCall(DMPlexGetDepth(dm, &depth));
6452   PetscCall(PetscSectionGetNumFields(section, &numFields));
6453   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6454     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6455     PetscFunctionReturn(0);
6456   }
6457   /* Get points */
6458   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6459   for (clsize = 0, p = 0; p < numPoints; p++) {
6460     PetscInt dof;
6461     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6462     clsize += dof;
6463   }
6464   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6465   /* Get array */
6466   PetscCall(VecGetArray(v, &array));
6467   /* Get values */
6468   if (numFields > 0) {
6469     PetscInt offset = 0, f;
6470     for (f = 0; f < numFields; ++f) {
6471       const PetscInt    **perms = NULL;
6472       const PetscScalar **flips = NULL;
6473 
6474       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6475       switch (mode) {
6476       case INSERT_VALUES:
6477         for (p = 0; p < numPoints; p++) {
6478           const PetscInt     point = points[2 * p];
6479           const PetscInt    *perm  = perms ? perms[p] : NULL;
6480           const PetscScalar *flip  = flips ? flips[p] : NULL;
6481           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6482         }
6483         break;
6484       case INSERT_ALL_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           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6490         }
6491         break;
6492       case INSERT_BC_VALUES:
6493         for (p = 0; p < numPoints; p++) {
6494           const PetscInt     point = points[2 * p];
6495           const PetscInt    *perm  = perms ? perms[p] : NULL;
6496           const PetscScalar *flip  = flips ? flips[p] : NULL;
6497           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6498         }
6499         break;
6500       case ADD_VALUES:
6501         for (p = 0; p < numPoints; p++) {
6502           const PetscInt     point = points[2 * p];
6503           const PetscInt    *perm  = perms ? perms[p] : NULL;
6504           const PetscScalar *flip  = flips ? flips[p] : NULL;
6505           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6506         }
6507         break;
6508       case ADD_ALL_VALUES:
6509         for (p = 0; p < numPoints; p++) {
6510           const PetscInt     point = points[2 * p];
6511           const PetscInt    *perm  = perms ? perms[p] : NULL;
6512           const PetscScalar *flip  = flips ? flips[p] : NULL;
6513           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6514         }
6515         break;
6516       case ADD_BC_VALUES:
6517         for (p = 0; p < numPoints; p++) {
6518           const PetscInt     point = points[2 * p];
6519           const PetscInt    *perm  = perms ? perms[p] : NULL;
6520           const PetscScalar *flip  = flips ? flips[p] : NULL;
6521           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6522         }
6523         break;
6524       default:
6525         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6526       }
6527       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6528     }
6529   } else {
6530     PetscInt            dof, off;
6531     const PetscInt    **perms = NULL;
6532     const PetscScalar **flips = NULL;
6533 
6534     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6535     switch (mode) {
6536     case INSERT_VALUES:
6537       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6538         const PetscInt     point = points[2 * p];
6539         const PetscInt    *perm  = perms ? perms[p] : NULL;
6540         const PetscScalar *flip  = flips ? flips[p] : NULL;
6541         PetscCall(PetscSectionGetDof(section, point, &dof));
6542         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6543       }
6544       break;
6545     case INSERT_ALL_VALUES:
6546       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6547         const PetscInt     point = points[2 * p];
6548         const PetscInt    *perm  = perms ? perms[p] : NULL;
6549         const PetscScalar *flip  = flips ? flips[p] : NULL;
6550         PetscCall(PetscSectionGetDof(section, point, &dof));
6551         updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array);
6552       }
6553       break;
6554     case INSERT_BC_VALUES:
6555       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6556         const PetscInt     point = points[2 * p];
6557         const PetscInt    *perm  = perms ? perms[p] : NULL;
6558         const PetscScalar *flip  = flips ? flips[p] : NULL;
6559         PetscCall(PetscSectionGetDof(section, point, &dof));
6560         updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array);
6561       }
6562       break;
6563     case ADD_VALUES:
6564       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6565         const PetscInt     point = points[2 * p];
6566         const PetscInt    *perm  = perms ? perms[p] : NULL;
6567         const PetscScalar *flip  = flips ? flips[p] : NULL;
6568         PetscCall(PetscSectionGetDof(section, point, &dof));
6569         updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array);
6570       }
6571       break;
6572     case ADD_ALL_VALUES:
6573       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6574         const PetscInt     point = points[2 * p];
6575         const PetscInt    *perm  = perms ? perms[p] : NULL;
6576         const PetscScalar *flip  = flips ? flips[p] : NULL;
6577         PetscCall(PetscSectionGetDof(section, point, &dof));
6578         updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array);
6579       }
6580       break;
6581     case ADD_BC_VALUES:
6582       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6583         const PetscInt     point = points[2 * p];
6584         const PetscInt    *perm  = perms ? perms[p] : NULL;
6585         const PetscScalar *flip  = flips ? flips[p] : NULL;
6586         PetscCall(PetscSectionGetDof(section, point, &dof));
6587         updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array);
6588       }
6589       break;
6590     default:
6591       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6592     }
6593     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6594   }
6595   /* Cleanup points */
6596   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6597   /* Cleanup array */
6598   PetscCall(VecRestoreArray(v, &array));
6599   PetscFunctionReturn(0);
6600 }
6601 
6602 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6603 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6604 {
6605   PetscFunctionBegin;
6606   *contains = PETSC_TRUE;
6607   if (label) {
6608     PetscInt fdof;
6609 
6610     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6611     if (!*contains) {
6612       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6613       *offset += fdof;
6614       PetscFunctionReturn(0);
6615     }
6616   }
6617   PetscFunctionReturn(0);
6618 }
6619 
6620 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6621 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)
6622 {
6623   PetscSection    clSection;
6624   IS              clPoints;
6625   PetscScalar    *array;
6626   PetscInt       *points = NULL;
6627   const PetscInt *clp;
6628   PetscInt        numFields, numPoints, p;
6629   PetscInt        offset = 0, f;
6630 
6631   PetscFunctionBeginHot;
6632   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6633   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6634   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6635   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6636   PetscCall(PetscSectionGetNumFields(section, &numFields));
6637   /* Get points */
6638   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6639   /* Get array */
6640   PetscCall(VecGetArray(v, &array));
6641   /* Get values */
6642   for (f = 0; f < numFields; ++f) {
6643     const PetscInt    **perms = NULL;
6644     const PetscScalar **flips = NULL;
6645     PetscBool           contains;
6646 
6647     if (!fieldActive[f]) {
6648       for (p = 0; p < numPoints * 2; p += 2) {
6649         PetscInt fdof;
6650         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6651         offset += fdof;
6652       }
6653       continue;
6654     }
6655     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6656     switch (mode) {
6657     case INSERT_VALUES:
6658       for (p = 0; p < numPoints; p++) {
6659         const PetscInt     point = points[2 * p];
6660         const PetscInt    *perm  = perms ? perms[p] : NULL;
6661         const PetscScalar *flip  = flips ? flips[p] : NULL;
6662         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6663         if (!contains) continue;
6664         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6665       }
6666       break;
6667     case INSERT_ALL_VALUES:
6668       for (p = 0; p < numPoints; p++) {
6669         const PetscInt     point = points[2 * p];
6670         const PetscInt    *perm  = perms ? perms[p] : NULL;
6671         const PetscScalar *flip  = flips ? flips[p] : NULL;
6672         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6673         if (!contains) continue;
6674         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6675       }
6676       break;
6677     case INSERT_BC_VALUES:
6678       for (p = 0; p < numPoints; p++) {
6679         const PetscInt     point = points[2 * p];
6680         const PetscInt    *perm  = perms ? perms[p] : NULL;
6681         const PetscScalar *flip  = flips ? flips[p] : NULL;
6682         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6683         if (!contains) continue;
6684         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6685       }
6686       break;
6687     case ADD_VALUES:
6688       for (p = 0; p < numPoints; p++) {
6689         const PetscInt     point = points[2 * p];
6690         const PetscInt    *perm  = perms ? perms[p] : NULL;
6691         const PetscScalar *flip  = flips ? flips[p] : NULL;
6692         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6693         if (!contains) continue;
6694         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6695       }
6696       break;
6697     case ADD_ALL_VALUES:
6698       for (p = 0; p < numPoints; p++) {
6699         const PetscInt     point = points[2 * p];
6700         const PetscInt    *perm  = perms ? perms[p] : NULL;
6701         const PetscScalar *flip  = flips ? flips[p] : NULL;
6702         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6703         if (!contains) continue;
6704         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6705       }
6706       break;
6707     default:
6708       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6709     }
6710     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6711   }
6712   /* Cleanup points */
6713   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6714   /* Cleanup array */
6715   PetscCall(VecRestoreArray(v, &array));
6716   PetscFunctionReturn(0);
6717 }
6718 
6719 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6720 {
6721   PetscMPIInt rank;
6722   PetscInt    i, j;
6723 
6724   PetscFunctionBegin;
6725   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6726   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6727   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6728   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6729   numCIndices = numCIndices ? numCIndices : numRIndices;
6730   if (!values) PetscFunctionReturn(0);
6731   for (i = 0; i < numRIndices; i++) {
6732     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6733     for (j = 0; j < numCIndices; j++) {
6734 #if defined(PETSC_USE_COMPLEX)
6735       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6736 #else
6737       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6738 #endif
6739     }
6740     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6741   }
6742   PetscFunctionReturn(0);
6743 }
6744 
6745 /*
6746   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6747 
6748   Input Parameters:
6749 + section - The section for this data layout
6750 . islocal - Is the section (and thus indices being requested) local or global?
6751 . point   - The point contributing dofs with these indices
6752 . off     - The global offset of this point
6753 . loff    - The local offset of each field
6754 . setBC   - The flag determining whether to include indices of boundary values
6755 . perm    - A permutation of the dofs on this point, or NULL
6756 - indperm - A permutation of the entire indices array, or NULL
6757 
6758   Output Parameter:
6759 . indices - Indices for dofs on this point
6760 
6761   Level: developer
6762 
6763   Note: The indices could be local or global, depending on the value of 'off'.
6764 */
6765 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6766 {
6767   PetscInt        dof;   /* The number of unknowns on this point */
6768   PetscInt        cdof;  /* The number of constraints on this point */
6769   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6770   PetscInt        cind = 0, k;
6771 
6772   PetscFunctionBegin;
6773   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6774   PetscCall(PetscSectionGetDof(section, point, &dof));
6775   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6776   if (!cdof || setBC) {
6777     for (k = 0; k < dof; ++k) {
6778       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6779       const PetscInt ind    = indperm ? indperm[preind] : preind;
6780 
6781       indices[ind] = off + k;
6782     }
6783   } else {
6784     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6785     for (k = 0; k < dof; ++k) {
6786       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6787       const PetscInt ind    = indperm ? indperm[preind] : preind;
6788 
6789       if ((cind < cdof) && (k == cdofs[cind])) {
6790         /* Insert check for returning constrained indices */
6791         indices[ind] = -(off + k + 1);
6792         ++cind;
6793       } else {
6794         indices[ind] = off + k - (islocal ? 0 : cind);
6795       }
6796     }
6797   }
6798   *loff += dof;
6799   PetscFunctionReturn(0);
6800 }
6801 
6802 /*
6803  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6804 
6805  Input Parameters:
6806 + section - a section (global or local)
6807 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6808 . point - point within section
6809 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6810 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6811 . setBC - identify constrained (boundary condition) points via involution.
6812 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6813 . permsoff - offset
6814 - indperm - index permutation
6815 
6816  Output Parameter:
6817 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6818 . indices - array to hold indices (as defined by section) of each dof associated with point
6819 
6820  Notes:
6821  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6822  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6823  in the local vector.
6824 
6825  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6826  significant).  It is invalid to call with a global section and setBC=true.
6827 
6828  Developer Note:
6829  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6830  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6831  offset could be obtained from the section instead of passing it explicitly as we do now.
6832 
6833  Example:
6834  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6835  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6836  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6837  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.
6838 
6839  Level: developer
6840 */
6841 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[])
6842 {
6843   PetscInt numFields, foff, f;
6844 
6845   PetscFunctionBegin;
6846   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6847   PetscCall(PetscSectionGetNumFields(section, &numFields));
6848   for (f = 0, foff = 0; f < numFields; ++f) {
6849     PetscInt        fdof, cfdof;
6850     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6851     PetscInt        cind = 0, b;
6852     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6853 
6854     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6855     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6856     if (!cfdof || setBC) {
6857       for (b = 0; b < fdof; ++b) {
6858         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6859         const PetscInt ind    = indperm ? indperm[preind] : preind;
6860 
6861         indices[ind] = off + foff + b;
6862       }
6863     } else {
6864       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6865       for (b = 0; b < fdof; ++b) {
6866         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6867         const PetscInt ind    = indperm ? indperm[preind] : preind;
6868 
6869         if ((cind < cfdof) && (b == fcdofs[cind])) {
6870           indices[ind] = -(off + foff + b + 1);
6871           ++cind;
6872         } else {
6873           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6874         }
6875       }
6876     }
6877     foff += (setBC || islocal ? fdof : (fdof - cfdof));
6878     foffs[f] += fdof;
6879   }
6880   PetscFunctionReturn(0);
6881 }
6882 
6883 /*
6884   This version believes the globalSection offsets for each field, rather than just the point offset
6885 
6886  . foffs - The offset into 'indices' for each field, since it is segregated by field
6887 
6888  Notes:
6889  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6890  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6891 */
6892 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6893 {
6894   PetscInt numFields, foff, f;
6895 
6896   PetscFunctionBegin;
6897   PetscCall(PetscSectionGetNumFields(section, &numFields));
6898   for (f = 0; f < numFields; ++f) {
6899     PetscInt        fdof, cfdof;
6900     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6901     PetscInt        cind = 0, b;
6902     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6903 
6904     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6905     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6906     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6907     if (!cfdof) {
6908       for (b = 0; b < fdof; ++b) {
6909         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6910         const PetscInt ind    = indperm ? indperm[preind] : preind;
6911 
6912         indices[ind] = foff + b;
6913       }
6914     } else {
6915       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6916       for (b = 0; b < fdof; ++b) {
6917         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6918         const PetscInt ind    = indperm ? indperm[preind] : preind;
6919 
6920         if ((cind < cfdof) && (b == fcdofs[cind])) {
6921           indices[ind] = -(foff + b + 1);
6922           ++cind;
6923         } else {
6924           indices[ind] = foff + b - cind;
6925         }
6926       }
6927     }
6928     foffs[f] += fdof;
6929   }
6930   PetscFunctionReturn(0);
6931 }
6932 
6933 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)
6934 {
6935   Mat             cMat;
6936   PetscSection    aSec, cSec;
6937   IS              aIS;
6938   PetscInt        aStart = -1, aEnd = -1;
6939   const PetscInt *anchors;
6940   PetscInt        numFields, f, p, q, newP = 0;
6941   PetscInt        newNumPoints = 0, newNumIndices = 0;
6942   PetscInt       *newPoints, *indices, *newIndices;
6943   PetscInt        maxAnchor, maxDof;
6944   PetscInt        newOffsets[32];
6945   PetscInt       *pointMatOffsets[32];
6946   PetscInt       *newPointOffsets[32];
6947   PetscScalar    *pointMat[32];
6948   PetscScalar    *newValues      = NULL, *tmpValues;
6949   PetscBool       anyConstrained = PETSC_FALSE;
6950 
6951   PetscFunctionBegin;
6952   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6953   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6954   PetscCall(PetscSectionGetNumFields(section, &numFields));
6955 
6956   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
6957   /* if there are point-to-point constraints */
6958   if (aSec) {
6959     PetscCall(PetscArrayzero(newOffsets, 32));
6960     PetscCall(ISGetIndices(aIS, &anchors));
6961     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
6962     /* figure out how many points are going to be in the new element matrix
6963      * (we allow double counting, because it's all just going to be summed
6964      * into the global matrix anyway) */
6965     for (p = 0; p < 2 * numPoints; p += 2) {
6966       PetscInt b    = points[p];
6967       PetscInt bDof = 0, bSecDof;
6968 
6969       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6970       if (!bSecDof) continue;
6971       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6972       if (bDof) {
6973         /* this point is constrained */
6974         /* it is going to be replaced by its anchors */
6975         PetscInt bOff, q;
6976 
6977         anyConstrained = PETSC_TRUE;
6978         newNumPoints += bDof;
6979         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6980         for (q = 0; q < bDof; q++) {
6981           PetscInt a = anchors[bOff + q];
6982           PetscInt aDof;
6983 
6984           PetscCall(PetscSectionGetDof(section, a, &aDof));
6985           newNumIndices += aDof;
6986           for (f = 0; f < numFields; ++f) {
6987             PetscInt fDof;
6988 
6989             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6990             newOffsets[f + 1] += fDof;
6991           }
6992         }
6993       } else {
6994         /* this point is not constrained */
6995         newNumPoints++;
6996         newNumIndices += bSecDof;
6997         for (f = 0; f < numFields; ++f) {
6998           PetscInt fDof;
6999 
7000           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7001           newOffsets[f + 1] += fDof;
7002         }
7003       }
7004     }
7005   }
7006   if (!anyConstrained) {
7007     if (outNumPoints) *outNumPoints = 0;
7008     if (outNumIndices) *outNumIndices = 0;
7009     if (outPoints) *outPoints = NULL;
7010     if (outValues) *outValues = NULL;
7011     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7012     PetscFunctionReturn(0);
7013   }
7014 
7015   if (outNumPoints) *outNumPoints = newNumPoints;
7016   if (outNumIndices) *outNumIndices = newNumIndices;
7017 
7018   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7019 
7020   if (!outPoints && !outValues) {
7021     if (offsets) {
7022       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7023     }
7024     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7025     PetscFunctionReturn(0);
7026   }
7027 
7028   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7029 
7030   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7031 
7032   /* workspaces */
7033   if (numFields) {
7034     for (f = 0; f < numFields; f++) {
7035       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7036       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7037     }
7038   } else {
7039     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7040     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7041   }
7042 
7043   /* get workspaces for the point-to-point matrices */
7044   if (numFields) {
7045     PetscInt totalOffset, totalMatOffset;
7046 
7047     for (p = 0; p < numPoints; p++) {
7048       PetscInt b    = points[2 * p];
7049       PetscInt bDof = 0, bSecDof;
7050 
7051       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7052       if (!bSecDof) {
7053         for (f = 0; f < numFields; f++) {
7054           newPointOffsets[f][p + 1] = 0;
7055           pointMatOffsets[f][p + 1] = 0;
7056         }
7057         continue;
7058       }
7059       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7060       if (bDof) {
7061         for (f = 0; f < numFields; f++) {
7062           PetscInt fDof, q, bOff, allFDof = 0;
7063 
7064           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7065           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7066           for (q = 0; q < bDof; q++) {
7067             PetscInt a = anchors[bOff + q];
7068             PetscInt aFDof;
7069 
7070             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7071             allFDof += aFDof;
7072           }
7073           newPointOffsets[f][p + 1] = allFDof;
7074           pointMatOffsets[f][p + 1] = fDof * allFDof;
7075         }
7076       } else {
7077         for (f = 0; f < numFields; f++) {
7078           PetscInt fDof;
7079 
7080           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7081           newPointOffsets[f][p + 1] = fDof;
7082           pointMatOffsets[f][p + 1] = 0;
7083         }
7084       }
7085     }
7086     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7087       newPointOffsets[f][0] = totalOffset;
7088       pointMatOffsets[f][0] = totalMatOffset;
7089       for (p = 0; p < numPoints; p++) {
7090         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7091         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7092       }
7093       totalOffset    = newPointOffsets[f][numPoints];
7094       totalMatOffset = pointMatOffsets[f][numPoints];
7095       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7096     }
7097   } else {
7098     for (p = 0; p < numPoints; p++) {
7099       PetscInt b    = points[2 * p];
7100       PetscInt bDof = 0, bSecDof;
7101 
7102       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7103       if (!bSecDof) {
7104         newPointOffsets[0][p + 1] = 0;
7105         pointMatOffsets[0][p + 1] = 0;
7106         continue;
7107       }
7108       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7109       if (bDof) {
7110         PetscInt bOff, q, allDof = 0;
7111 
7112         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7113         for (q = 0; q < bDof; q++) {
7114           PetscInt a = anchors[bOff + q], aDof;
7115 
7116           PetscCall(PetscSectionGetDof(section, a, &aDof));
7117           allDof += aDof;
7118         }
7119         newPointOffsets[0][p + 1] = allDof;
7120         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7121       } else {
7122         newPointOffsets[0][p + 1] = bSecDof;
7123         pointMatOffsets[0][p + 1] = 0;
7124       }
7125     }
7126     newPointOffsets[0][0] = 0;
7127     pointMatOffsets[0][0] = 0;
7128     for (p = 0; p < numPoints; p++) {
7129       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7130       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7131     }
7132     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7133   }
7134 
7135   /* output arrays */
7136   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7137 
7138   /* get the point-to-point matrices; construct newPoints */
7139   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7140   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7141   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7142   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7143   if (numFields) {
7144     for (p = 0, newP = 0; p < numPoints; p++) {
7145       PetscInt b    = points[2 * p];
7146       PetscInt o    = points[2 * p + 1];
7147       PetscInt bDof = 0, bSecDof;
7148 
7149       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7150       if (!bSecDof) continue;
7151       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7152       if (bDof) {
7153         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7154 
7155         fStart[0] = 0;
7156         fEnd[0]   = 0;
7157         for (f = 0; f < numFields; f++) {
7158           PetscInt fDof;
7159 
7160           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7161           fStart[f + 1] = fStart[f] + fDof;
7162           fEnd[f + 1]   = fStart[f + 1];
7163         }
7164         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7165         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7166 
7167         fAnchorStart[0] = 0;
7168         fAnchorEnd[0]   = 0;
7169         for (f = 0; f < numFields; f++) {
7170           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7171 
7172           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7173           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7174         }
7175         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7176         for (q = 0; q < bDof; q++) {
7177           PetscInt a = anchors[bOff + q], aOff;
7178 
7179           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7180           newPoints[2 * (newP + q)]     = a;
7181           newPoints[2 * (newP + q) + 1] = 0;
7182           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7183           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7184         }
7185         newP += bDof;
7186 
7187         if (outValues) {
7188           /* get the point-to-point submatrix */
7189           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]));
7190         }
7191       } else {
7192         newPoints[2 * newP]     = b;
7193         newPoints[2 * newP + 1] = o;
7194         newP++;
7195       }
7196     }
7197   } else {
7198     for (p = 0; p < numPoints; p++) {
7199       PetscInt b    = points[2 * p];
7200       PetscInt o    = points[2 * p + 1];
7201       PetscInt bDof = 0, bSecDof;
7202 
7203       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7204       if (!bSecDof) continue;
7205       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7206       if (bDof) {
7207         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7208 
7209         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7210         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7211 
7212         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7213         for (q = 0; q < bDof; q++) {
7214           PetscInt a = anchors[bOff + q], aOff;
7215 
7216           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7217 
7218           newPoints[2 * (newP + q)]     = a;
7219           newPoints[2 * (newP + q) + 1] = 0;
7220           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7221           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7222         }
7223         newP += bDof;
7224 
7225         /* get the point-to-point submatrix */
7226         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7227       } else {
7228         newPoints[2 * newP]     = b;
7229         newPoints[2 * newP + 1] = o;
7230         newP++;
7231       }
7232     }
7233   }
7234 
7235   if (outValues) {
7236     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7237     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7238     /* multiply constraints on the right */
7239     if (numFields) {
7240       for (f = 0; f < numFields; f++) {
7241         PetscInt oldOff = offsets[f];
7242 
7243         for (p = 0; p < numPoints; p++) {
7244           PetscInt cStart = newPointOffsets[f][p];
7245           PetscInt b      = points[2 * p];
7246           PetscInt c, r, k;
7247           PetscInt dof;
7248 
7249           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7250           if (!dof) continue;
7251           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7252             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7253             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7254 
7255             for (r = 0; r < numIndices; r++) {
7256               for (c = 0; c < nCols; c++) {
7257                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7258               }
7259             }
7260           } else {
7261             /* copy this column as is */
7262             for (r = 0; r < numIndices; r++) {
7263               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7264             }
7265           }
7266           oldOff += dof;
7267         }
7268       }
7269     } else {
7270       PetscInt oldOff = 0;
7271       for (p = 0; p < numPoints; p++) {
7272         PetscInt cStart = newPointOffsets[0][p];
7273         PetscInt b      = points[2 * p];
7274         PetscInt c, r, k;
7275         PetscInt dof;
7276 
7277         PetscCall(PetscSectionGetDof(section, b, &dof));
7278         if (!dof) continue;
7279         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7280           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7281           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7282 
7283           for (r = 0; r < numIndices; r++) {
7284             for (c = 0; c < nCols; c++) {
7285               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7286             }
7287           }
7288         } else {
7289           /* copy this column as is */
7290           for (r = 0; r < numIndices; r++) {
7291             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7292           }
7293         }
7294         oldOff += dof;
7295       }
7296     }
7297 
7298     if (multiplyLeft) {
7299       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7300       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7301       /* multiply constraints transpose on the left */
7302       if (numFields) {
7303         for (f = 0; f < numFields; f++) {
7304           PetscInt oldOff = offsets[f];
7305 
7306           for (p = 0; p < numPoints; p++) {
7307             PetscInt rStart = newPointOffsets[f][p];
7308             PetscInt b      = points[2 * p];
7309             PetscInt c, r, k;
7310             PetscInt dof;
7311 
7312             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7313             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7314               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7315               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7316 
7317               for (r = 0; r < nRows; r++) {
7318                 for (c = 0; c < newNumIndices; c++) {
7319                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7320                 }
7321               }
7322             } else {
7323               /* copy this row as is */
7324               for (r = 0; r < dof; r++) {
7325                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7326               }
7327             }
7328             oldOff += dof;
7329           }
7330         }
7331       } else {
7332         PetscInt oldOff = 0;
7333 
7334         for (p = 0; p < numPoints; p++) {
7335           PetscInt rStart = newPointOffsets[0][p];
7336           PetscInt b      = points[2 * p];
7337           PetscInt c, r, k;
7338           PetscInt dof;
7339 
7340           PetscCall(PetscSectionGetDof(section, b, &dof));
7341           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7342             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7343             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7344 
7345             for (r = 0; r < nRows; r++) {
7346               for (c = 0; c < newNumIndices; c++) {
7347                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7348               }
7349             }
7350           } else {
7351             /* copy this row as is */
7352             for (r = 0; r < dof; r++) {
7353               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7354             }
7355           }
7356           oldOff += dof;
7357         }
7358       }
7359 
7360       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7361     } else {
7362       newValues = tmpValues;
7363     }
7364   }
7365 
7366   /* clean up */
7367   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7368   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7369 
7370   if (numFields) {
7371     for (f = 0; f < numFields; f++) {
7372       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7373       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7374       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7375     }
7376   } else {
7377     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7378     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7379     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7380   }
7381   PetscCall(ISRestoreIndices(aIS, &anchors));
7382 
7383   /* output */
7384   if (outPoints) {
7385     *outPoints = newPoints;
7386   } else {
7387     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7388   }
7389   if (outValues) *outValues = newValues;
7390   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7391   PetscFunctionReturn(0);
7392 }
7393 
7394 /*@C
7395   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7396 
7397   Not collective
7398 
7399   Input Parameters:
7400 + dm         - The `DM`
7401 . section    - The `PetscSection` describing the points (a local section)
7402 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7403 . point      - The point defining the closure
7404 - useClPerm  - Use the closure point permutation if available
7405 
7406   Output Parameters:
7407 + numIndices - The number of dof indices in the closure of point with the input sections
7408 . indices    - The dof indices
7409 . outOffsets - Array to write the field offsets into, or NULL
7410 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7411 
7412   Level: advanced
7413 
7414   Notes:
7415   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7416 
7417   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7418   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7419   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7420   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7421   indices (with the above semantics) are implied.
7422 
7423 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7424           `PetscSection`, `DMGetGlobalSection()`
7425 @*/
7426 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7427 {
7428   /* Closure ordering */
7429   PetscSection    clSection;
7430   IS              clPoints;
7431   const PetscInt *clp;
7432   PetscInt       *points;
7433   const PetscInt *clperm = NULL;
7434   /* Dof permutation and sign flips */
7435   const PetscInt    **perms[32] = {NULL};
7436   const PetscScalar **flips[32] = {NULL};
7437   PetscScalar        *valCopy   = NULL;
7438   /* Hanging node constraints */
7439   PetscInt    *pointsC = NULL;
7440   PetscScalar *valuesC = NULL;
7441   PetscInt     NclC, NiC;
7442 
7443   PetscInt *idx;
7444   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7445   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7446 
7447   PetscFunctionBeginHot;
7448   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7449   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7450   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7451   if (numIndices) PetscValidIntPointer(numIndices, 6);
7452   if (indices) PetscValidPointer(indices, 7);
7453   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7454   if (values) PetscValidPointer(values, 9);
7455   PetscCall(PetscSectionGetNumFields(section, &Nf));
7456   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7457   PetscCall(PetscArrayzero(offsets, 32));
7458   /* 1) Get points in closure */
7459   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7460   if (useClPerm) {
7461     PetscInt depth, clsize;
7462     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7463     for (clsize = 0, p = 0; p < Ncl; p++) {
7464       PetscInt dof;
7465       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7466       clsize += dof;
7467     }
7468     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7469   }
7470   /* 2) Get number of indices on these points and field offsets from section */
7471   for (p = 0; p < Ncl * 2; p += 2) {
7472     PetscInt dof, fdof;
7473 
7474     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7475     for (f = 0; f < Nf; ++f) {
7476       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7477       offsets[f + 1] += fdof;
7478     }
7479     Ni += dof;
7480   }
7481   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7482   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7483   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7484   for (f = 0; f < PetscMax(1, Nf); ++f) {
7485     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7486     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7487     /* may need to apply sign changes to the element matrix */
7488     if (values && flips[f]) {
7489       PetscInt foffset = offsets[f];
7490 
7491       for (p = 0; p < Ncl; ++p) {
7492         PetscInt           pnt  = points[2 * p], fdof;
7493         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7494 
7495         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7496         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7497         if (flip) {
7498           PetscInt i, j, k;
7499 
7500           if (!valCopy) {
7501             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7502             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7503             *values = valCopy;
7504           }
7505           for (i = 0; i < fdof; ++i) {
7506             PetscScalar fval = flip[i];
7507 
7508             for (k = 0; k < Ni; ++k) {
7509               valCopy[Ni * (foffset + i) + k] *= fval;
7510               valCopy[Ni * k + (foffset + i)] *= fval;
7511             }
7512           }
7513         }
7514         foffset += fdof;
7515       }
7516     }
7517   }
7518   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7519   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7520   if (NclC) {
7521     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7522     for (f = 0; f < PetscMax(1, Nf); ++f) {
7523       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7524       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7525     }
7526     for (f = 0; f < PetscMax(1, Nf); ++f) {
7527       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7528       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7529     }
7530     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7531     Ncl    = NclC;
7532     Ni     = NiC;
7533     points = pointsC;
7534     if (values) *values = valuesC;
7535   }
7536   /* 5) Calculate indices */
7537   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7538   if (Nf) {
7539     PetscInt  idxOff;
7540     PetscBool useFieldOffsets;
7541 
7542     if (outOffsets) {
7543       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7544     }
7545     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7546     if (useFieldOffsets) {
7547       for (p = 0; p < Ncl; ++p) {
7548         const PetscInt pnt = points[p * 2];
7549 
7550         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7551       }
7552     } else {
7553       for (p = 0; p < Ncl; ++p) {
7554         const PetscInt pnt = points[p * 2];
7555 
7556         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7557         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7558          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7559          * global section. */
7560         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7561       }
7562     }
7563   } else {
7564     PetscInt off = 0, idxOff;
7565 
7566     for (p = 0; p < Ncl; ++p) {
7567       const PetscInt  pnt  = points[p * 2];
7568       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7569 
7570       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7571       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7572        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7573       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7574     }
7575   }
7576   /* 6) Cleanup */
7577   for (f = 0; f < PetscMax(1, Nf); ++f) {
7578     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7579     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7580   }
7581   if (NclC) {
7582     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7583   } else {
7584     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7585   }
7586 
7587   if (numIndices) *numIndices = Ni;
7588   if (indices) *indices = idx;
7589   PetscFunctionReturn(0);
7590 }
7591 
7592 /*@C
7593   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7594 
7595   Not collective
7596 
7597   Input Parameters:
7598 + dm         - The `DM`
7599 . section    - The `PetscSection` describing the points (a local section)
7600 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7601 . point      - The point defining the closure
7602 - useClPerm  - Use the closure point permutation if available
7603 
7604   Output Parameters:
7605 + numIndices - The number of dof indices in the closure of point with the input sections
7606 . indices    - The dof indices
7607 . outOffsets - Array to write the field offsets into, or NULL
7608 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7609 
7610   Level: advanced
7611 
7612   Notes:
7613   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7614 
7615   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7616   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7617   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7618   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7619   indices (with the above semantics) are implied.
7620 
7621 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7622 @*/
7623 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7624 {
7625   PetscFunctionBegin;
7626   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7627   PetscValidPointer(indices, 7);
7628   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7629   PetscFunctionReturn(0);
7630 }
7631 
7632 /*@C
7633   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7634 
7635   Not collective
7636 
7637   Input Parameters:
7638 + dm - The `DM`
7639 . section - The section describing the layout in v, or NULL to use the default section
7640 . globalSection - The section describing the layout in v, or NULL to use the default global section
7641 . A - The matrix
7642 . point - The point in the `DM`
7643 . values - The array of values
7644 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7645 
7646   Level: intermediate
7647 
7648 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7649 @*/
7650 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7651 {
7652   DM_Plex           *mesh = (DM_Plex *)dm->data;
7653   PetscInt          *indices;
7654   PetscInt           numIndices;
7655   const PetscScalar *valuesOrig = values;
7656   PetscErrorCode     ierr;
7657 
7658   PetscFunctionBegin;
7659   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7660   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7661   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7662   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7663   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7664   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7665 
7666   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7667 
7668   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7669   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7670   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7671   if (ierr) {
7672     PetscMPIInt rank;
7673 
7674     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7675     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7676     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7677     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7678     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7679     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7680   }
7681   if (mesh->printFEM > 1) {
7682     PetscInt i;
7683     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7684     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7685     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7686   }
7687 
7688   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7689   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7690   PetscFunctionReturn(0);
7691 }
7692 
7693 /*@C
7694   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7695 
7696   Not collective
7697 
7698   Input Parameters:
7699 + dmRow - The `DM` for the row fields
7700 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7701 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7702 . dmCol - The `DM` for the column fields
7703 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7704 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7705 . A - The matrix
7706 . point - The point in the `DM`
7707 . values - The array of values
7708 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7709 
7710   Level: intermediate
7711 
7712 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7713 @*/
7714 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7715 {
7716   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7717   PetscInt          *indicesRow, *indicesCol;
7718   PetscInt           numIndicesRow, numIndicesCol;
7719   const PetscScalar *valuesOrig = values;
7720   PetscErrorCode     ierr;
7721 
7722   PetscFunctionBegin;
7723   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7724   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7725   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7726   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7727   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7728   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7729   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7730   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7731   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7732   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7733   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7734 
7735   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7736   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7737 
7738   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7739   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7740   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7741   if (ierr) {
7742     PetscMPIInt rank;
7743 
7744     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7745     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7746     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7747     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7748     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7749     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7750   }
7751 
7752   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7753   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7754   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7755   PetscFunctionReturn(0);
7756 }
7757 
7758 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7759 {
7760   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7761   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7762   PetscInt       *cpoints = NULL;
7763   PetscInt       *findices, *cindices;
7764   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7765   PetscInt        foffsets[32], coffsets[32];
7766   DMPolytopeType  ct;
7767   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7768   PetscErrorCode  ierr;
7769 
7770   PetscFunctionBegin;
7771   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7772   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7773   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7774   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7775   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7776   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7777   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7778   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7779   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7780   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7781   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7782   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7783   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7784   PetscCall(PetscArrayzero(foffsets, 32));
7785   PetscCall(PetscArrayzero(coffsets, 32));
7786   /* Column indices */
7787   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7788   maxFPoints = numCPoints;
7789   /* Compress out points not in the section */
7790   /*   TODO: Squeeze out points with 0 dof as well */
7791   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7792   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7793     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7794       cpoints[q * 2]     = cpoints[p];
7795       cpoints[q * 2 + 1] = cpoints[p + 1];
7796       ++q;
7797     }
7798   }
7799   numCPoints = q;
7800   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7801     PetscInt fdof;
7802 
7803     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7804     if (!dof) continue;
7805     for (f = 0; f < numFields; ++f) {
7806       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7807       coffsets[f + 1] += fdof;
7808     }
7809     numCIndices += dof;
7810   }
7811   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7812   /* Row indices */
7813   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7814   {
7815     DMPlexTransform tr;
7816     DMPolytopeType *rct;
7817     PetscInt       *rsize, *rcone, *rornt, Nt;
7818 
7819     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7820     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7821     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7822     numSubcells = rsize[Nt - 1];
7823     PetscCall(DMPlexTransformDestroy(&tr));
7824   }
7825   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7826   for (r = 0, q = 0; r < numSubcells; ++r) {
7827     /* TODO Map from coarse to fine cells */
7828     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7829     /* Compress out points not in the section */
7830     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7831     for (p = 0; p < numFPoints * 2; p += 2) {
7832       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7833         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7834         if (!dof) continue;
7835         for (s = 0; s < q; ++s)
7836           if (fpoints[p] == ftotpoints[s * 2]) break;
7837         if (s < q) continue;
7838         ftotpoints[q * 2]     = fpoints[p];
7839         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7840         ++q;
7841       }
7842     }
7843     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7844   }
7845   numFPoints = q;
7846   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7847     PetscInt fdof;
7848 
7849     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7850     if (!dof) continue;
7851     for (f = 0; f < numFields; ++f) {
7852       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7853       foffsets[f + 1] += fdof;
7854     }
7855     numFIndices += dof;
7856   }
7857   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7858 
7859   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7860   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7861   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7862   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7863   if (numFields) {
7864     const PetscInt **permsF[32] = {NULL};
7865     const PetscInt **permsC[32] = {NULL};
7866 
7867     for (f = 0; f < numFields; f++) {
7868       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7869       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7870     }
7871     for (p = 0; p < numFPoints; p++) {
7872       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7873       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7874     }
7875     for (p = 0; p < numCPoints; p++) {
7876       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7877       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7878     }
7879     for (f = 0; f < numFields; f++) {
7880       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7881       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7882     }
7883   } else {
7884     const PetscInt **permsF = NULL;
7885     const PetscInt **permsC = NULL;
7886 
7887     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7888     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7889     for (p = 0, off = 0; p < numFPoints; p++) {
7890       const PetscInt *perm = permsF ? permsF[p] : NULL;
7891 
7892       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7893       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7894     }
7895     for (p = 0, off = 0; p < numCPoints; p++) {
7896       const PetscInt *perm = permsC ? permsC[p] : NULL;
7897 
7898       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7899       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7900     }
7901     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7902     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7903   }
7904   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7905   /* TODO: flips */
7906   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7907   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7908   if (ierr) {
7909     PetscMPIInt rank;
7910 
7911     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7912     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7913     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7914     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7915     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7916   }
7917   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
7918   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7919   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7920   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7921   PetscFunctionReturn(0);
7922 }
7923 
7924 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7925 {
7926   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7927   PetscInt       *cpoints = NULL;
7928   PetscInt        foffsets[32], coffsets[32];
7929   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7930   DMPolytopeType  ct;
7931   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7932 
7933   PetscFunctionBegin;
7934   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7935   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7936   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7937   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7938   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7939   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7940   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7941   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7942   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7943   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7944   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7945   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7946   PetscCall(PetscArrayzero(foffsets, 32));
7947   PetscCall(PetscArrayzero(coffsets, 32));
7948   /* Column indices */
7949   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7950   maxFPoints = numCPoints;
7951   /* Compress out points not in the section */
7952   /*   TODO: Squeeze out points with 0 dof as well */
7953   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7954   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7955     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7956       cpoints[q * 2]     = cpoints[p];
7957       cpoints[q * 2 + 1] = cpoints[p + 1];
7958       ++q;
7959     }
7960   }
7961   numCPoints = q;
7962   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7963     PetscInt fdof;
7964 
7965     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7966     if (!dof) continue;
7967     for (f = 0; f < numFields; ++f) {
7968       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7969       coffsets[f + 1] += fdof;
7970     }
7971     numCIndices += dof;
7972   }
7973   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7974   /* Row indices */
7975   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7976   {
7977     DMPlexTransform tr;
7978     DMPolytopeType *rct;
7979     PetscInt       *rsize, *rcone, *rornt, Nt;
7980 
7981     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7982     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7983     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7984     numSubcells = rsize[Nt - 1];
7985     PetscCall(DMPlexTransformDestroy(&tr));
7986   }
7987   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7988   for (r = 0, q = 0; r < numSubcells; ++r) {
7989     /* TODO Map from coarse to fine cells */
7990     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7991     /* Compress out points not in the section */
7992     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7993     for (p = 0; p < numFPoints * 2; p += 2) {
7994       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7995         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7996         if (!dof) continue;
7997         for (s = 0; s < q; ++s)
7998           if (fpoints[p] == ftotpoints[s * 2]) break;
7999         if (s < q) continue;
8000         ftotpoints[q * 2]     = fpoints[p];
8001         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8002         ++q;
8003       }
8004     }
8005     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8006   }
8007   numFPoints = q;
8008   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8009     PetscInt fdof;
8010 
8011     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8012     if (!dof) continue;
8013     for (f = 0; f < numFields; ++f) {
8014       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8015       foffsets[f + 1] += fdof;
8016     }
8017     numFIndices += dof;
8018   }
8019   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8020 
8021   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8022   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8023   if (numFields) {
8024     const PetscInt **permsF[32] = {NULL};
8025     const PetscInt **permsC[32] = {NULL};
8026 
8027     for (f = 0; f < numFields; f++) {
8028       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8029       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8030     }
8031     for (p = 0; p < numFPoints; p++) {
8032       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8033       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8034     }
8035     for (p = 0; p < numCPoints; p++) {
8036       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8037       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8038     }
8039     for (f = 0; f < numFields; f++) {
8040       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8041       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8042     }
8043   } else {
8044     const PetscInt **permsF = NULL;
8045     const PetscInt **permsC = NULL;
8046 
8047     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8048     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8049     for (p = 0, off = 0; p < numFPoints; p++) {
8050       const PetscInt *perm = permsF ? permsF[p] : NULL;
8051 
8052       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8053       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8054     }
8055     for (p = 0, off = 0; p < numCPoints; p++) {
8056       const PetscInt *perm = permsC ? permsC[p] : NULL;
8057 
8058       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8059       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8060     }
8061     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8062     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8063   }
8064   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8065   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8066   PetscFunctionReturn(0);
8067 }
8068 
8069 /*@C
8070   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8071 
8072   Input Parameter:
8073 . dm   - The `DMPLEX` object
8074 
8075   Output Parameter:
8076 . cellHeight - The height of a cell
8077 
8078   Level: developer
8079 
8080 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`,  `DMPlexSetVTKCellHeight()`
8081 @*/
8082 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8083 {
8084   DM_Plex *mesh = (DM_Plex *)dm->data;
8085 
8086   PetscFunctionBegin;
8087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8088   PetscValidIntPointer(cellHeight, 2);
8089   *cellHeight = mesh->vtkCellHeight;
8090   PetscFunctionReturn(0);
8091 }
8092 
8093 /*@C
8094   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8095 
8096   Input Parameters:
8097 + dm   - The `DMPLEX` object
8098 - cellHeight - The height of a cell
8099 
8100   Level: developer
8101 
8102 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8103 @*/
8104 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8105 {
8106   DM_Plex *mesh = (DM_Plex *)dm->data;
8107 
8108   PetscFunctionBegin;
8109   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8110   mesh->vtkCellHeight = cellHeight;
8111   PetscFunctionReturn(0);
8112 }
8113 
8114 /*@
8115   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8116 
8117   Input Parameter:
8118 . dm - The `DMPLEX` object
8119 
8120   Output Parameters:
8121 + gcStart - The first ghost cell, or NULL
8122 - gcEnd   - The upper bound on ghost cells, or NULL
8123 
8124   Level: advanced
8125 
8126 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8127 @*/
8128 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8129 {
8130   DMLabel ctLabel;
8131 
8132   PetscFunctionBegin;
8133   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8134   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8135   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8136   // Reset label for fast lookup
8137   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8138   PetscFunctionReturn(0);
8139 }
8140 
8141 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8142 {
8143   PetscSection section, globalSection;
8144   PetscInt    *numbers, p;
8145 
8146   PetscFunctionBegin;
8147   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8148   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8149   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8150   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8151   PetscCall(PetscSectionSetUp(section));
8152   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8153   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8154   for (p = pStart; p < pEnd; ++p) {
8155     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8156     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8157     else numbers[p - pStart] += shift;
8158   }
8159   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8160   if (globalSize) {
8161     PetscLayout layout;
8162     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8163     PetscCall(PetscLayoutGetSize(layout, globalSize));
8164     PetscCall(PetscLayoutDestroy(&layout));
8165   }
8166   PetscCall(PetscSectionDestroy(&section));
8167   PetscCall(PetscSectionDestroy(&globalSection));
8168   PetscFunctionReturn(0);
8169 }
8170 
8171 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8172 {
8173   PetscInt cellHeight, cStart, cEnd;
8174 
8175   PetscFunctionBegin;
8176   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8177   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8178   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8179   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8180   PetscFunctionReturn(0);
8181 }
8182 
8183 /*@
8184   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8185 
8186   Input Parameter:
8187 . dm   - The `DMPLEX` object
8188 
8189   Output Parameter:
8190 . globalCellNumbers - Global cell numbers for all cells on this process
8191 
8192   Level: developer
8193 
8194 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8195 @*/
8196 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8197 {
8198   DM_Plex *mesh = (DM_Plex *)dm->data;
8199 
8200   PetscFunctionBegin;
8201   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8202   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8203   *globalCellNumbers = mesh->globalCellNumbers;
8204   PetscFunctionReturn(0);
8205 }
8206 
8207 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8208 {
8209   PetscInt vStart, vEnd;
8210 
8211   PetscFunctionBegin;
8212   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8213   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8214   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8215   PetscFunctionReturn(0);
8216 }
8217 
8218 /*@
8219   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8220 
8221   Input Parameter:
8222 . dm   - The `DMPLEX` object
8223 
8224   Output Parameter:
8225 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8226 
8227   Level: developer
8228 
8229 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8230 @*/
8231 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8232 {
8233   DM_Plex *mesh = (DM_Plex *)dm->data;
8234 
8235   PetscFunctionBegin;
8236   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8237   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8238   *globalVertexNumbers = mesh->globalVertexNumbers;
8239   PetscFunctionReturn(0);
8240 }
8241 
8242 /*@
8243   DMPlexCreatePointNumbering - Create a global numbering for all points.
8244 
8245   Collective on dm
8246 
8247   Input Parameter:
8248 . dm   - The `DMPLEX` object
8249 
8250   Output Parameter:
8251 . globalPointNumbers - Global numbers for all points on this process
8252 
8253   Level: developer
8254 
8255   Notes:
8256   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8257   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8258   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8259   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8260 
8261   The partitioned mesh is
8262 ```
8263  (2)--0--(3)--1--(4)    (1)--0--(2)
8264 ```
8265   and its global numbering is
8266 ```
8267   (3)--0--(4)--1--(5)--2--(6)
8268 ```
8269   Then the global numbering is provided as
8270 ```
8271 [0] Number of indices in set 5
8272 [0] 0 0
8273 [0] 1 1
8274 [0] 2 3
8275 [0] 3 4
8276 [0] 4 -6
8277 [1] Number of indices in set 3
8278 [1] 0 2
8279 [1] 1 5
8280 [1] 2 6
8281 ```
8282 
8283 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8284 @*/
8285 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8286 {
8287   IS        nums[4];
8288   PetscInt  depths[4], gdepths[4], starts[4];
8289   PetscInt  depth, d, shift = 0;
8290   PetscBool empty = PETSC_FALSE;
8291 
8292   PetscFunctionBegin;
8293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8294   PetscCall(DMPlexGetDepth(dm, &depth));
8295   // For unstratified meshes use dim instead of depth
8296   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8297   // If any stratum is empty, we must mark all empty
8298   for (d = 0; d <= depth; ++d) {
8299     PetscInt end;
8300 
8301     depths[d] = depth - d;
8302     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8303     if (!(starts[d] - end)) empty = PETSC_TRUE;
8304   }
8305   if (empty)
8306     for (d = 0; d <= depth; ++d) {
8307       depths[d] = -1;
8308       starts[d] = -1;
8309     }
8310   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8311   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8312   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]);
8313   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8314   for (d = 0; d <= depth; ++d) {
8315     PetscInt pStart, pEnd, gsize;
8316 
8317     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8318     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8319     shift += gsize;
8320   }
8321   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8322   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8323   PetscFunctionReturn(0);
8324 }
8325 
8326 /*@
8327   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8328 
8329   Input Parameter:
8330 . dm - The `DMPLEX` object
8331 
8332   Output Parameter:
8333 . ranks - The rank field
8334 
8335   Options Database Key:
8336 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8337 
8338   Level: intermediate
8339 
8340 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8341 @*/
8342 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8343 {
8344   DM             rdm;
8345   PetscFE        fe;
8346   PetscScalar   *r;
8347   PetscMPIInt    rank;
8348   DMPolytopeType ct;
8349   PetscInt       dim, cStart, cEnd, c;
8350   PetscBool      simplex;
8351 
8352   PetscFunctionBeginUser;
8353   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8354   PetscValidPointer(ranks, 2);
8355   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8356   PetscCall(DMClone(dm, &rdm));
8357   PetscCall(DMGetDimension(rdm, &dim));
8358   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8359   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8360   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8361   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8362   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8363   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8364   PetscCall(PetscFEDestroy(&fe));
8365   PetscCall(DMCreateDS(rdm));
8366   PetscCall(DMCreateGlobalVector(rdm, ranks));
8367   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8368   PetscCall(VecGetArray(*ranks, &r));
8369   for (c = cStart; c < cEnd; ++c) {
8370     PetscScalar *lr;
8371 
8372     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8373     if (lr) *lr = rank;
8374   }
8375   PetscCall(VecRestoreArray(*ranks, &r));
8376   PetscCall(DMDestroy(&rdm));
8377   PetscFunctionReturn(0);
8378 }
8379 
8380 /*@
8381   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8382 
8383   Input Parameters:
8384 + dm    - The DMPlex
8385 - label - The DMLabel
8386 
8387   Output Parameter:
8388 . val - The label value field
8389 
8390   Options Database Keys:
8391 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8392 
8393   Level: intermediate
8394 
8395 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8396 @*/
8397 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8398 {
8399   DM           rdm;
8400   PetscFE      fe;
8401   PetscScalar *v;
8402   PetscInt     dim, cStart, cEnd, c;
8403 
8404   PetscFunctionBeginUser;
8405   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8406   PetscValidPointer(label, 2);
8407   PetscValidPointer(val, 3);
8408   PetscCall(DMClone(dm, &rdm));
8409   PetscCall(DMGetDimension(rdm, &dim));
8410   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8411   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8412   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8413   PetscCall(PetscFEDestroy(&fe));
8414   PetscCall(DMCreateDS(rdm));
8415   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8416   PetscCall(DMCreateGlobalVector(rdm, val));
8417   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8418   PetscCall(VecGetArray(*val, &v));
8419   for (c = cStart; c < cEnd; ++c) {
8420     PetscScalar *lv;
8421     PetscInt     cval;
8422 
8423     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8424     PetscCall(DMLabelGetValue(label, c, &cval));
8425     *lv = cval;
8426   }
8427   PetscCall(VecRestoreArray(*val, &v));
8428   PetscCall(DMDestroy(&rdm));
8429   PetscFunctionReturn(0);
8430 }
8431 
8432 /*@
8433   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8434 
8435   Input Parameter:
8436 . dm - The `DMPLEX` object
8437 
8438   Level: developer
8439 
8440   Notes:
8441   This is a useful diagnostic when creating meshes programmatically.
8442 
8443   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8444 
8445 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8446 @*/
8447 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8448 {
8449   PetscSection    coneSection, supportSection;
8450   const PetscInt *cone, *support;
8451   PetscInt        coneSize, c, supportSize, s;
8452   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8453   PetscBool       storagecheck = PETSC_TRUE;
8454 
8455   PetscFunctionBegin;
8456   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8457   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8458   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8459   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8460   /* Check that point p is found in the support of its cone points, and vice versa */
8461   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8462   for (p = pStart; p < pEnd; ++p) {
8463     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8464     PetscCall(DMPlexGetCone(dm, p, &cone));
8465     for (c = 0; c < coneSize; ++c) {
8466       PetscBool dup = PETSC_FALSE;
8467       PetscInt  d;
8468       for (d = c - 1; d >= 0; --d) {
8469         if (cone[c] == cone[d]) {
8470           dup = PETSC_TRUE;
8471           break;
8472         }
8473       }
8474       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8475       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8476       for (s = 0; s < supportSize; ++s) {
8477         if (support[s] == p) break;
8478       }
8479       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8480         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8481         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8482         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8483         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8484         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8485         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8486         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]);
8487         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8488       }
8489     }
8490     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8491     if (p != pp) {
8492       storagecheck = PETSC_FALSE;
8493       continue;
8494     }
8495     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8496     PetscCall(DMPlexGetSupport(dm, p, &support));
8497     for (s = 0; s < supportSize; ++s) {
8498       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8499       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8500       for (c = 0; c < coneSize; ++c) {
8501         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8502         if (cone[c] != pp) {
8503           c = 0;
8504           break;
8505         }
8506         if (cone[c] == p) break;
8507       }
8508       if (c >= coneSize) {
8509         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8510         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8511         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8512         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8513         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8514         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8515         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8516       }
8517     }
8518   }
8519   if (storagecheck) {
8520     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8521     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8522     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8523   }
8524   PetscFunctionReturn(0);
8525 }
8526 
8527 /*
8528   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.
8529 */
8530 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8531 {
8532   DMPolytopeType  cct;
8533   PetscInt        ptpoints[4];
8534   const PetscInt *cone, *ccone, *ptcone;
8535   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8536 
8537   PetscFunctionBegin;
8538   *unsplit = 0;
8539   switch (ct) {
8540   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8541     ptpoints[npt++] = c;
8542     break;
8543   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8544     PetscCall(DMPlexGetCone(dm, c, &cone));
8545     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8546     for (cp = 0; cp < coneSize; ++cp) {
8547       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8548       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8549     }
8550     break;
8551   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8552   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8553     PetscCall(DMPlexGetCone(dm, c, &cone));
8554     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8555     for (cp = 0; cp < coneSize; ++cp) {
8556       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8557       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8558       for (ccp = 0; ccp < cconeSize; ++ccp) {
8559         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8560         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8561           PetscInt p;
8562           for (p = 0; p < npt; ++p)
8563             if (ptpoints[p] == ccone[ccp]) break;
8564           if (p == npt) ptpoints[npt++] = ccone[ccp];
8565         }
8566       }
8567     }
8568     break;
8569   default:
8570     break;
8571   }
8572   for (pt = 0; pt < npt; ++pt) {
8573     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8574     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8575   }
8576   PetscFunctionReturn(0);
8577 }
8578 
8579 /*@
8580   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8581 
8582   Input Parameters:
8583 + dm - The `DMPLEX` object
8584 - cellHeight - Normally 0
8585 
8586   Level: developer
8587 
8588   Notes:
8589   This is a useful diagnostic when creating meshes programmatically.
8590   Currently applicable only to homogeneous simplex or tensor meshes.
8591 
8592   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8593 
8594 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8595 @*/
8596 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8597 {
8598   DMPlexInterpolatedFlag interp;
8599   DMPolytopeType         ct;
8600   PetscInt               vStart, vEnd, cStart, cEnd, c;
8601 
8602   PetscFunctionBegin;
8603   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8604   PetscCall(DMPlexIsInterpolated(dm, &interp));
8605   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8606   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8607   for (c = cStart; c < cEnd; ++c) {
8608     PetscInt *closure = NULL;
8609     PetscInt  coneSize, closureSize, cl, Nv = 0;
8610 
8611     PetscCall(DMPlexGetCellType(dm, c, &ct));
8612     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8613     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8614     if (interp == DMPLEX_INTERPOLATED_FULL) {
8615       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8616       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));
8617     }
8618     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8619     for (cl = 0; cl < closureSize * 2; cl += 2) {
8620       const PetscInt p = closure[cl];
8621       if ((p >= vStart) && (p < vEnd)) ++Nv;
8622     }
8623     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8624     /* Special Case: Tensor faces with identified vertices */
8625     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8626       PetscInt unsplit;
8627 
8628       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8629       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8630     }
8631     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));
8632   }
8633   PetscFunctionReturn(0);
8634 }
8635 
8636 /*@
8637   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8638 
8639   Collective on dm
8640 
8641   Input Parameters:
8642 + dm - The `DMPLEX` object
8643 - cellHeight - Normally 0
8644 
8645   Level: developer
8646 
8647   Notes:
8648   This is a useful diagnostic when creating meshes programmatically.
8649   This routine is only relevant for meshes that are fully interpolated across all ranks.
8650   It will error out if a partially interpolated mesh is given on some rank.
8651   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8652 
8653   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8654 
8655 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8656 @*/
8657 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8658 {
8659   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8660   DMPlexInterpolatedFlag interpEnum;
8661 
8662   PetscFunctionBegin;
8663   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8664   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8665   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8666   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8667     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8668     PetscFunctionReturn(0);
8669   }
8670 
8671   PetscCall(DMGetDimension(dm, &dim));
8672   PetscCall(DMPlexGetDepth(dm, &depth));
8673   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8674   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8675     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8676     for (c = cStart; c < cEnd; ++c) {
8677       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8678       const DMPolytopeType *faceTypes;
8679       DMPolytopeType        ct;
8680       PetscInt              numFaces, coneSize, f;
8681       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8682 
8683       PetscCall(DMPlexGetCellType(dm, c, &ct));
8684       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8685       if (unsplit) continue;
8686       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8687       PetscCall(DMPlexGetCone(dm, c, &cone));
8688       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8689       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8690       for (cl = 0; cl < closureSize * 2; cl += 2) {
8691         const PetscInt p = closure[cl];
8692         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8693       }
8694       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8695       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);
8696       for (f = 0; f < numFaces; ++f) {
8697         DMPolytopeType fct;
8698         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8699 
8700         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8701         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8702         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8703           const PetscInt p = fclosure[cl];
8704           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8705         }
8706         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]);
8707         for (v = 0; v < fnumCorners; ++v) {
8708           if (fclosure[v] != faces[fOff + v]) {
8709             PetscInt v1;
8710 
8711             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8712             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8713             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8714             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8715             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8716             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]);
8717           }
8718         }
8719         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8720         fOff += faceSizes[f];
8721       }
8722       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8723       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8724     }
8725   }
8726   PetscFunctionReturn(0);
8727 }
8728 
8729 /*@
8730   DMPlexCheckGeometry - Check the geometry of mesh cells
8731 
8732   Input Parameter:
8733 . dm - The `DMPLEX` object
8734 
8735   Level: developer
8736 
8737   Notes:
8738   This is a useful diagnostic when creating meshes programmatically.
8739 
8740   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8741 
8742 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8743 @*/
8744 PetscErrorCode DMPlexCheckGeometry(DM dm)
8745 {
8746   Vec       coordinates;
8747   PetscReal detJ, J[9], refVol = 1.0;
8748   PetscReal vol;
8749   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8750 
8751   PetscFunctionBegin;
8752   PetscCall(DMGetDimension(dm, &dim));
8753   PetscCall(DMGetCoordinateDim(dm, &dE));
8754   if (dim != dE) PetscFunctionReturn(0);
8755   PetscCall(DMPlexGetDepth(dm, &depth));
8756   for (d = 0; d < dim; ++d) refVol *= 2.0;
8757   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8758   /* Make sure local coordinates are created, because that step is collective */
8759   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8760   for (c = cStart; c < cEnd; ++c) {
8761     DMPolytopeType ct;
8762     PetscInt       unsplit;
8763     PetscBool      ignoreZeroVol = PETSC_FALSE;
8764 
8765     PetscCall(DMPlexGetCellType(dm, c, &ct));
8766     switch (ct) {
8767     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8768     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8769     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8770       ignoreZeroVol = PETSC_TRUE;
8771       break;
8772     default:
8773       break;
8774     }
8775     switch (ct) {
8776     case DM_POLYTOPE_TRI_PRISM:
8777     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8778     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8779     case DM_POLYTOPE_PYRAMID:
8780       continue;
8781     default:
8782       break;
8783     }
8784     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8785     if (unsplit) continue;
8786     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8787     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);
8788     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8789     /* This should work with periodicity since DG coordinates should be used */
8790     if (depth > 1) {
8791       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8792       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);
8793       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8794     }
8795   }
8796   PetscFunctionReturn(0);
8797 }
8798 
8799 /*@
8800   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8801 
8802   Collective on dm
8803 
8804   Input Parameters:
8805 + dm - The `DMPLEX` object
8806 . pointSF - The `PetscSF`, or NULL for `PointSF` attached to `DM`
8807 - allowExtraRoots - Flag to allow extra points not present in the `DM`
8808 
8809   Level: developer
8810 
8811   Notes:
8812   This is mainly intended for debugging/testing purposes.
8813 
8814   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8815 
8816   Extra roots can come from priodic cuts, where additional points appear on the boundary
8817 
8818 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
8819 @*/
8820 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
8821 {
8822   PetscInt           l, nleaves, nroots, overlap;
8823   const PetscInt    *locals;
8824   const PetscSFNode *remotes;
8825   PetscBool          distributed;
8826   MPI_Comm           comm;
8827   PetscMPIInt        rank;
8828 
8829   PetscFunctionBegin;
8830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8831   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8832   else pointSF = dm->sf;
8833   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8834   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8835   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8836   {
8837     PetscMPIInt mpiFlag;
8838 
8839     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
8840     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
8841   }
8842   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8843   PetscCall(DMPlexIsDistributed(dm, &distributed));
8844   if (!distributed) {
8845     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);
8846     PetscFunctionReturn(0);
8847   }
8848   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);
8849   PetscCall(DMPlexGetOverlap(dm, &overlap));
8850 
8851   /* Check SF graph is compatible with DMPlex chart */
8852   {
8853     PetscInt pStart, pEnd, maxLeaf;
8854 
8855     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8856     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8857     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
8858     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8859   }
8860 
8861   /* Check Point SF has no local points referenced */
8862   for (l = 0; l < nleaves; l++) {
8863     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);
8864   }
8865 
8866   /* Check there are no cells in interface */
8867   if (!overlap) {
8868     PetscInt cellHeight, cStart, cEnd;
8869 
8870     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8871     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8872     for (l = 0; l < nleaves; ++l) {
8873       const PetscInt point = locals ? locals[l] : l;
8874 
8875       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8876     }
8877   }
8878 
8879   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8880   {
8881     const PetscInt *rootdegree;
8882 
8883     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8884     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8885     for (l = 0; l < nleaves; ++l) {
8886       const PetscInt  point = locals ? locals[l] : l;
8887       const PetscInt *cone;
8888       PetscInt        coneSize, c, idx;
8889 
8890       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8891       PetscCall(DMPlexGetCone(dm, point, &cone));
8892       for (c = 0; c < coneSize; ++c) {
8893         if (!rootdegree[cone[c]]) {
8894           if (locals) {
8895             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8896           } else {
8897             idx = (cone[c] < nleaves) ? cone[c] : -1;
8898           }
8899           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8900         }
8901       }
8902     }
8903   }
8904   PetscFunctionReturn(0);
8905 }
8906 
8907 /*@
8908   DMPlexCheck - Perform various checks of Plex sanity
8909 
8910   Input Parameter:
8911 . dm - The `DMPLEX` object
8912 
8913   Level: developer
8914 
8915   Notes:
8916   This is a useful diagnostic when creating meshes programmatically.
8917 
8918   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8919 
8920   Currently does not include DMPlexCheckCellShape().
8921 
8922 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, DMCreate(), DMSetFromOptions()
8923 @*/
8924 PetscErrorCode DMPlexCheck(DM dm)
8925 {
8926   PetscInt cellHeight;
8927 
8928   PetscFunctionBegin;
8929   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8930   PetscCall(DMPlexCheckSymmetry(dm));
8931   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8932   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8933   PetscCall(DMPlexCheckGeometry(dm));
8934   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
8935   PetscCall(DMPlexCheckInterfaceCones(dm));
8936   PetscFunctionReturn(0);
8937 }
8938 
8939 typedef struct cell_stats {
8940   PetscReal min, max, sum, squaresum;
8941   PetscInt  count;
8942 } cell_stats_t;
8943 
8944 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
8945 {
8946   PetscInt i, N = *len;
8947 
8948   for (i = 0; i < N; i++) {
8949     cell_stats_t *A = (cell_stats_t *)a;
8950     cell_stats_t *B = (cell_stats_t *)b;
8951 
8952     B->min = PetscMin(A->min, B->min);
8953     B->max = PetscMax(A->max, B->max);
8954     B->sum += A->sum;
8955     B->squaresum += A->squaresum;
8956     B->count += A->count;
8957   }
8958 }
8959 
8960 /*@
8961   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8962 
8963   Collective on dm
8964 
8965   Input Parameters:
8966 + dm        - The `DMPLEX` object
8967 . output    - If true, statistics will be displayed on stdout
8968 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
8969 
8970   Level: developer
8971 
8972   Notes:
8973   This is mainly intended for debugging/testing purposes.
8974 
8975   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8976 
8977 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8978 @*/
8979 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8980 {
8981   DM           dmCoarse;
8982   cell_stats_t stats, globalStats;
8983   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
8984   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8985   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8986   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8987   PetscMPIInt  rank, size;
8988 
8989   PetscFunctionBegin;
8990   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8991   stats.min = PETSC_MAX_REAL;
8992   stats.max = PETSC_MIN_REAL;
8993   stats.sum = stats.squaresum = 0.;
8994   stats.count                 = 0;
8995 
8996   PetscCallMPI(MPI_Comm_size(comm, &size));
8997   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8998   PetscCall(DMGetCoordinateDim(dm, &cdim));
8999   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9000   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9001   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9002   for (c = cStart; c < cEnd; c++) {
9003     PetscInt  i;
9004     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9005 
9006     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9007     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9008     for (i = 0; i < PetscSqr(cdim); ++i) {
9009       frobJ += J[i] * J[i];
9010       frobInvJ += invJ[i] * invJ[i];
9011     }
9012     cond2 = frobJ * frobInvJ;
9013     cond  = PetscSqrtReal(cond2);
9014 
9015     stats.min = PetscMin(stats.min, cond);
9016     stats.max = PetscMax(stats.max, cond);
9017     stats.sum += cond;
9018     stats.squaresum += cond2;
9019     stats.count++;
9020     if (output && cond > limit) {
9021       PetscSection coordSection;
9022       Vec          coordsLocal;
9023       PetscScalar *coords = NULL;
9024       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9025 
9026       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9027       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9028       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9029       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9030       for (i = 0; i < Nv / cdim; ++i) {
9031         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9032         for (d = 0; d < cdim; ++d) {
9033           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9034           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9035         }
9036         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9037       }
9038       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9039       for (cl = 0; cl < clSize * 2; cl += 2) {
9040         const PetscInt edge = closure[cl];
9041 
9042         if ((edge >= eStart) && (edge < eEnd)) {
9043           PetscReal len;
9044 
9045           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9046           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9047         }
9048       }
9049       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9050       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9051     }
9052   }
9053   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9054 
9055   if (size > 1) {
9056     PetscMPIInt  blockLengths[2] = {4, 1};
9057     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9058     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9059     MPI_Op       statReduce;
9060 
9061     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9062     PetscCallMPI(MPI_Type_commit(&statType));
9063     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9064     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9065     PetscCallMPI(MPI_Op_free(&statReduce));
9066     PetscCallMPI(MPI_Type_free(&statType));
9067   } else {
9068     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9069   }
9070   if (rank == 0) {
9071     count = globalStats.count;
9072     min   = globalStats.min;
9073     max   = globalStats.max;
9074     mean  = globalStats.sum / globalStats.count;
9075     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9076   }
9077 
9078   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));
9079   PetscCall(PetscFree2(J, invJ));
9080 
9081   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9082   if (dmCoarse) {
9083     PetscBool isplex;
9084 
9085     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9086     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9087   }
9088   PetscFunctionReturn(0);
9089 }
9090 
9091 /*@
9092   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9093   orthogonal quality below given tolerance.
9094 
9095   Collective on dm
9096 
9097   Input Parameters:
9098 + dm   - The `DMPLEX` object
9099 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9100 - atol - [0, 1] Absolute tolerance for tagging cells.
9101 
9102   Output Parameters:
9103 + OrthQual      - Vec containing orthogonal quality per cell
9104 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9105 
9106   Options Database Keys:
9107 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9108 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9109 
9110   Level: intermediate
9111 
9112   Notes:
9113   Orthogonal quality is given by the following formula:
9114 
9115   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9116 
9117   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
9118   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9119   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9120   calculating the cosine of the angle between these vectors.
9121 
9122   Orthogonal quality ranges from 1 (best) to 0 (worst).
9123 
9124   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9125   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9126 
9127   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9128 
9129 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9130 @*/
9131 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9132 {
9133   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9134   PetscInt              *idx;
9135   PetscScalar           *oqVals;
9136   const PetscScalar     *cellGeomArr, *faceGeomArr;
9137   PetscReal             *ci, *fi, *Ai;
9138   MPI_Comm               comm;
9139   Vec                    cellgeom, facegeom;
9140   DM                     dmFace, dmCell;
9141   IS                     glob;
9142   ISLocalToGlobalMapping ltog;
9143   PetscViewer            vwr;
9144 
9145   PetscFunctionBegin;
9146   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9147   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9148   PetscValidPointer(OrthQual, 4);
9149   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9150   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9151   PetscCall(DMGetDimension(dm, &nc));
9152   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9153   {
9154     DMPlexInterpolatedFlag interpFlag;
9155 
9156     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9157     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9158       PetscMPIInt rank;
9159 
9160       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9161       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9162     }
9163   }
9164   if (OrthQualLabel) {
9165     PetscValidPointer(OrthQualLabel, 5);
9166     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9167     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9168   } else {
9169     *OrthQualLabel = NULL;
9170   }
9171   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9172   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9173   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9174   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9175   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9176   PetscCall(VecCreate(comm, OrthQual));
9177   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9178   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9179   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9180   PetscCall(VecSetUp(*OrthQual));
9181   PetscCall(ISDestroy(&glob));
9182   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9183   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9184   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9185   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9186   PetscCall(VecGetDM(cellgeom, &dmCell));
9187   PetscCall(VecGetDM(facegeom, &dmFace));
9188   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9189   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9190     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9191     PetscInt         cellarr[2], *adj = NULL;
9192     PetscScalar     *cArr, *fArr;
9193     PetscReal        minvalc = 1.0, minvalf = 1.0;
9194     PetscFVCellGeom *cg;
9195 
9196     idx[cellIter] = cell - cStart;
9197     cellarr[0]    = cell;
9198     /* Make indexing into cellGeom easier */
9199     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9200     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9201     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9202     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9203     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9204       PetscInt         i;
9205       const PetscInt   neigh  = adj[cellneigh];
9206       PetscReal        normci = 0, normfi = 0, normai = 0;
9207       PetscFVCellGeom *cgneigh;
9208       PetscFVFaceGeom *fg;
9209 
9210       /* Don't count ourselves in the neighbor list */
9211       if (neigh == cell) continue;
9212       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9213       cellarr[1] = neigh;
9214       {
9215         PetscInt        numcovpts;
9216         const PetscInt *covpts;
9217 
9218         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9219         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9220         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9221       }
9222 
9223       /* Compute c_i, f_i and their norms */
9224       for (i = 0; i < nc; i++) {
9225         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9226         fi[i] = fg->centroid[i] - cg->centroid[i];
9227         Ai[i] = fg->normal[i];
9228         normci += PetscPowReal(ci[i], 2);
9229         normfi += PetscPowReal(fi[i], 2);
9230         normai += PetscPowReal(Ai[i], 2);
9231       }
9232       normci = PetscSqrtReal(normci);
9233       normfi = PetscSqrtReal(normfi);
9234       normai = PetscSqrtReal(normai);
9235 
9236       /* Normalize and compute for each face-cell-normal pair */
9237       for (i = 0; i < nc; i++) {
9238         ci[i] = ci[i] / normci;
9239         fi[i] = fi[i] / normfi;
9240         Ai[i] = Ai[i] / normai;
9241         /* PetscAbs because I don't know if normals are guaranteed to point out */
9242         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9243         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9244       }
9245       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9246       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9247     }
9248     PetscCall(PetscFree(adj));
9249     PetscCall(PetscFree2(cArr, fArr));
9250     /* Defer to cell if they're equal */
9251     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9252     if (OrthQualLabel) {
9253       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9254     }
9255   }
9256   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9257   PetscCall(VecAssemblyBegin(*OrthQual));
9258   PetscCall(VecAssemblyEnd(*OrthQual));
9259   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9260   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9261   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9262   if (OrthQualLabel) {
9263     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9264   }
9265   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9266   PetscCall(PetscViewerDestroy(&vwr));
9267   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9268   PetscFunctionReturn(0);
9269 }
9270 
9271 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9272  * interpolator construction */
9273 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9274 {
9275   PetscSection section, newSection, gsection;
9276   PetscSF      sf;
9277   PetscBool    hasConstraints, ghasConstraints;
9278 
9279   PetscFunctionBegin;
9280   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9281   PetscValidPointer(odm, 2);
9282   PetscCall(DMGetLocalSection(dm, &section));
9283   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9284   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9285   if (!ghasConstraints) {
9286     PetscCall(PetscObjectReference((PetscObject)dm));
9287     *odm = dm;
9288     PetscFunctionReturn(0);
9289   }
9290   PetscCall(DMClone(dm, odm));
9291   PetscCall(DMCopyFields(dm, *odm));
9292   PetscCall(DMGetLocalSection(*odm, &newSection));
9293   PetscCall(DMGetPointSF(*odm, &sf));
9294   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9295   PetscCall(DMSetGlobalSection(*odm, gsection));
9296   PetscCall(PetscSectionDestroy(&gsection));
9297   PetscFunctionReturn(0);
9298 }
9299 
9300 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9301 {
9302   DM        dmco, dmfo;
9303   Mat       interpo;
9304   Vec       rscale;
9305   Vec       cglobalo, clocal;
9306   Vec       fglobal, fglobalo, flocal;
9307   PetscBool regular;
9308 
9309   PetscFunctionBegin;
9310   PetscCall(DMGetFullDM(dmc, &dmco));
9311   PetscCall(DMGetFullDM(dmf, &dmfo));
9312   PetscCall(DMSetCoarseDM(dmfo, dmco));
9313   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9314   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9315   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9316   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9317   PetscCall(DMCreateLocalVector(dmc, &clocal));
9318   PetscCall(VecSet(cglobalo, 0.));
9319   PetscCall(VecSet(clocal, 0.));
9320   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9321   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9322   PetscCall(DMCreateLocalVector(dmf, &flocal));
9323   PetscCall(VecSet(fglobal, 0.));
9324   PetscCall(VecSet(fglobalo, 0.));
9325   PetscCall(VecSet(flocal, 0.));
9326   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9327   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9328   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9329   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9330   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9331   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9332   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9333   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9334   *shift = fglobal;
9335   PetscCall(VecDestroy(&flocal));
9336   PetscCall(VecDestroy(&fglobalo));
9337   PetscCall(VecDestroy(&clocal));
9338   PetscCall(VecDestroy(&cglobalo));
9339   PetscCall(VecDestroy(&rscale));
9340   PetscCall(MatDestroy(&interpo));
9341   PetscCall(DMDestroy(&dmfo));
9342   PetscCall(DMDestroy(&dmco));
9343   PetscFunctionReturn(0);
9344 }
9345 
9346 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9347 {
9348   PetscObject shifto;
9349   Vec         shift;
9350 
9351   PetscFunctionBegin;
9352   if (!interp) {
9353     Vec rscale;
9354 
9355     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9356     PetscCall(VecDestroy(&rscale));
9357   } else {
9358     PetscCall(PetscObjectReference((PetscObject)interp));
9359   }
9360   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9361   if (!shifto) {
9362     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9363     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9364     shifto = (PetscObject)shift;
9365     PetscCall(VecDestroy(&shift));
9366   }
9367   shift = (Vec)shifto;
9368   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9369   PetscCall(VecAXPY(fineSol, 1.0, shift));
9370   PetscCall(MatDestroy(&interp));
9371   PetscFunctionReturn(0);
9372 }
9373 
9374 /* Pointwise interpolation
9375      Just code FEM for now
9376      u^f = I u^c
9377      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9378      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9379      I_{ij} = psi^f_i phi^c_j
9380 */
9381 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9382 {
9383   PetscSection gsc, gsf;
9384   PetscInt     m, n;
9385   void        *ctx;
9386   DM           cdm;
9387   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9388 
9389   PetscFunctionBegin;
9390   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9391   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9392   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9393   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9394 
9395   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9396   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9397   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9398   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9399   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9400 
9401   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9402   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9403   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9404   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9405   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9406   if (scaling) {
9407     /* Use naive scaling */
9408     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9409   }
9410   PetscFunctionReturn(0);
9411 }
9412 
9413 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9414 {
9415   VecScatter ctx;
9416 
9417   PetscFunctionBegin;
9418   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9419   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9420   PetscCall(VecScatterDestroy(&ctx));
9421   PetscFunctionReturn(0);
9422 }
9423 
9424 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[])
9425 {
9426   const PetscInt Nc = uOff[1] - uOff[0];
9427   PetscInt       c;
9428   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9429 }
9430 
9431 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9432 {
9433   DM           dmc;
9434   PetscDS      ds;
9435   Vec          ones, locmass;
9436   IS           cellIS;
9437   PetscFormKey key;
9438   PetscInt     depth;
9439 
9440   PetscFunctionBegin;
9441   PetscCall(DMClone(dm, &dmc));
9442   PetscCall(DMCopyDisc(dm, dmc));
9443   PetscCall(DMGetDS(dmc, &ds));
9444   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9445   PetscCall(DMCreateGlobalVector(dmc, mass));
9446   PetscCall(DMGetLocalVector(dmc, &ones));
9447   PetscCall(DMGetLocalVector(dmc, &locmass));
9448   PetscCall(DMPlexGetDepth(dmc, &depth));
9449   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9450   PetscCall(VecSet(locmass, 0.0));
9451   PetscCall(VecSet(ones, 1.0));
9452   key.label = NULL;
9453   key.value = 0;
9454   key.field = 0;
9455   key.part  = 0;
9456   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9457   PetscCall(ISDestroy(&cellIS));
9458   PetscCall(VecSet(*mass, 0.0));
9459   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9460   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9461   PetscCall(DMRestoreLocalVector(dmc, &ones));
9462   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9463   PetscCall(DMDestroy(&dmc));
9464   PetscFunctionReturn(0);
9465 }
9466 
9467 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9468 {
9469   PetscSection gsc, gsf;
9470   PetscInt     m, n;
9471   void        *ctx;
9472   DM           cdm;
9473   PetscBool    regular;
9474 
9475   PetscFunctionBegin;
9476   if (dmFine == dmCoarse) {
9477     DM            dmc;
9478     PetscDS       ds;
9479     PetscWeakForm wf;
9480     Vec           u;
9481     IS            cellIS;
9482     PetscFormKey  key;
9483     PetscInt      depth;
9484 
9485     PetscCall(DMClone(dmFine, &dmc));
9486     PetscCall(DMCopyDisc(dmFine, dmc));
9487     PetscCall(DMGetDS(dmc, &ds));
9488     PetscCall(PetscDSGetWeakForm(ds, &wf));
9489     PetscCall(PetscWeakFormClear(wf));
9490     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9491     PetscCall(DMCreateMatrix(dmc, mass));
9492     PetscCall(DMGetLocalVector(dmc, &u));
9493     PetscCall(DMPlexGetDepth(dmc, &depth));
9494     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9495     PetscCall(MatZeroEntries(*mass));
9496     key.label = NULL;
9497     key.value = 0;
9498     key.field = 0;
9499     key.part  = 0;
9500     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9501     PetscCall(ISDestroy(&cellIS));
9502     PetscCall(DMRestoreLocalVector(dmc, &u));
9503     PetscCall(DMDestroy(&dmc));
9504   } else {
9505     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9506     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9507     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9508     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9509 
9510     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9511     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9512     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9513     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9514 
9515     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9516     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9517     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9518     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9519   }
9520   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9521   PetscFunctionReturn(0);
9522 }
9523 
9524 /*@
9525   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9526 
9527   Input Parameter:
9528 . dm - The `DMPLEX` object
9529 
9530   Output Parameter:
9531 . regular - The flag
9532 
9533   Level: intermediate
9534 
9535 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9536 @*/
9537 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9538 {
9539   PetscFunctionBegin;
9540   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9541   PetscValidBoolPointer(regular, 2);
9542   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9543   PetscFunctionReturn(0);
9544 }
9545 
9546 /*@
9547   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9548 
9549   Input Parameters:
9550 + dm - The `DMPLEX` object
9551 - regular - The flag
9552 
9553   Level: intermediate
9554 
9555 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9556 @*/
9557 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9558 {
9559   PetscFunctionBegin;
9560   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9561   ((DM_Plex *)dm->data)->regularRefinement = regular;
9562   PetscFunctionReturn(0);
9563 }
9564 
9565 /*@
9566   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9567   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9568 
9569   Not Collective
9570 
9571   Input Parameter:
9572 . dm - The `DMPLEX` object
9573 
9574   Output Parameters:
9575 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9576 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9577 
9578   Level: intermediate
9579 
9580 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9581 @*/
9582 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9583 {
9584   DM_Plex *plex = (DM_Plex *)dm->data;
9585 
9586   PetscFunctionBegin;
9587   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9588   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9589   if (anchorSection) *anchorSection = plex->anchorSection;
9590   if (anchorIS) *anchorIS = plex->anchorIS;
9591   PetscFunctionReturn(0);
9592 }
9593 
9594 /*@
9595   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9596   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9597   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9598 
9599   Collective on dm
9600 
9601   Input Parameters:
9602 + dm - The `DMPLEX` object
9603 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9604                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9605 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9606 
9607   Level: intermediate
9608 
9609   Notes:
9610   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9611   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9612 
9613   The reference counts of anchorSection and anchorIS are incremented.
9614 
9615 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9616 @*/
9617 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9618 {
9619   DM_Plex    *plex = (DM_Plex *)dm->data;
9620   PetscMPIInt result;
9621 
9622   PetscFunctionBegin;
9623   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9624   if (anchorSection) {
9625     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9626     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9627     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9628   }
9629   if (anchorIS) {
9630     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9631     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9632     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9633   }
9634 
9635   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9636   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9637   plex->anchorSection = anchorSection;
9638 
9639   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9640   PetscCall(ISDestroy(&plex->anchorIS));
9641   plex->anchorIS = anchorIS;
9642 
9643   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9644     PetscInt        size, a, pStart, pEnd;
9645     const PetscInt *anchors;
9646 
9647     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9648     PetscCall(ISGetLocalSize(anchorIS, &size));
9649     PetscCall(ISGetIndices(anchorIS, &anchors));
9650     for (a = 0; a < size; a++) {
9651       PetscInt p;
9652 
9653       p = anchors[a];
9654       if (p >= pStart && p < pEnd) {
9655         PetscInt dof;
9656 
9657         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9658         if (dof) {
9659           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9660           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9661         }
9662       }
9663     }
9664     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9665   }
9666   /* reset the generic constraints */
9667   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9668   PetscFunctionReturn(0);
9669 }
9670 
9671 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9672 {
9673   PetscSection anchorSection;
9674   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9675 
9676   PetscFunctionBegin;
9677   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9678   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9679   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9680   PetscCall(PetscSectionGetNumFields(section, &numFields));
9681   if (numFields) {
9682     PetscInt f;
9683     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9684 
9685     for (f = 0; f < numFields; f++) {
9686       PetscInt numComp;
9687 
9688       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9689       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9690     }
9691   }
9692   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9693   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9694   pStart = PetscMax(pStart, sStart);
9695   pEnd   = PetscMin(pEnd, sEnd);
9696   pEnd   = PetscMax(pStart, pEnd);
9697   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9698   for (p = pStart; p < pEnd; p++) {
9699     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9700     if (dof) {
9701       PetscCall(PetscSectionGetDof(section, p, &dof));
9702       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9703       for (f = 0; f < numFields; f++) {
9704         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9705         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9706       }
9707     }
9708   }
9709   PetscCall(PetscSectionSetUp(*cSec));
9710   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9711   PetscFunctionReturn(0);
9712 }
9713 
9714 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9715 {
9716   PetscSection    aSec;
9717   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9718   const PetscInt *anchors;
9719   PetscInt        numFields, f;
9720   IS              aIS;
9721   MatType         mtype;
9722   PetscBool       iscuda, iskokkos;
9723 
9724   PetscFunctionBegin;
9725   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9726   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9727   PetscCall(PetscSectionGetStorageSize(section, &n));
9728   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9729   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9730   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9731   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9732   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9733   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9734   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9735   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9736   else mtype = MATSEQAIJ;
9737   PetscCall(MatSetType(*cMat, mtype));
9738   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9739   PetscCall(ISGetIndices(aIS, &anchors));
9740   /* cSec will be a subset of aSec and section */
9741   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9742   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9743   PetscCall(PetscMalloc1(m + 1, &i));
9744   i[0] = 0;
9745   PetscCall(PetscSectionGetNumFields(section, &numFields));
9746   for (p = pStart; p < pEnd; p++) {
9747     PetscInt rDof, rOff, r;
9748 
9749     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9750     if (!rDof) continue;
9751     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9752     if (numFields) {
9753       for (f = 0; f < numFields; f++) {
9754         annz = 0;
9755         for (r = 0; r < rDof; r++) {
9756           a = anchors[rOff + r];
9757           if (a < sStart || a >= sEnd) continue;
9758           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9759           annz += aDof;
9760         }
9761         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9762         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9763         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9764       }
9765     } else {
9766       annz = 0;
9767       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9768       for (q = 0; q < dof; q++) {
9769         a = anchors[rOff + q];
9770         if (a < sStart || a >= sEnd) continue;
9771         PetscCall(PetscSectionGetDof(section, a, &aDof));
9772         annz += aDof;
9773       }
9774       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9775       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9776       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9777     }
9778   }
9779   nnz = i[m];
9780   PetscCall(PetscMalloc1(nnz, &j));
9781   offset = 0;
9782   for (p = pStart; p < pEnd; p++) {
9783     if (numFields) {
9784       for (f = 0; f < numFields; f++) {
9785         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9786         for (q = 0; q < dof; q++) {
9787           PetscInt rDof, rOff, r;
9788           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9789           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9790           for (r = 0; r < rDof; r++) {
9791             PetscInt s;
9792 
9793             a = anchors[rOff + r];
9794             if (a < sStart || a >= sEnd) continue;
9795             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9796             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9797             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9798           }
9799         }
9800       }
9801     } else {
9802       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9803       for (q = 0; q < dof; q++) {
9804         PetscInt rDof, rOff, r;
9805         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9806         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9807         for (r = 0; r < rDof; r++) {
9808           PetscInt s;
9809 
9810           a = anchors[rOff + r];
9811           if (a < sStart || a >= sEnd) continue;
9812           PetscCall(PetscSectionGetDof(section, a, &aDof));
9813           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9814           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9815         }
9816       }
9817     }
9818   }
9819   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9820   PetscCall(PetscFree(i));
9821   PetscCall(PetscFree(j));
9822   PetscCall(ISRestoreIndices(aIS, &anchors));
9823   PetscFunctionReturn(0);
9824 }
9825 
9826 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9827 {
9828   DM_Plex     *plex = (DM_Plex *)dm->data;
9829   PetscSection anchorSection, section, cSec;
9830   Mat          cMat;
9831 
9832   PetscFunctionBegin;
9833   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9834   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9835   if (anchorSection) {
9836     PetscInt Nf;
9837 
9838     PetscCall(DMGetLocalSection(dm, &section));
9839     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
9840     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
9841     PetscCall(DMGetNumFields(dm, &Nf));
9842     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
9843     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
9844     PetscCall(PetscSectionDestroy(&cSec));
9845     PetscCall(MatDestroy(&cMat));
9846   }
9847   PetscFunctionReturn(0);
9848 }
9849 
9850 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9851 {
9852   IS           subis;
9853   PetscSection section, subsection;
9854 
9855   PetscFunctionBegin;
9856   PetscCall(DMGetLocalSection(dm, &section));
9857   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9858   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9859   /* Create subdomain */
9860   PetscCall(DMPlexFilter(dm, label, value, subdm));
9861   /* Create submodel */
9862   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9863   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9864   PetscCall(DMSetLocalSection(*subdm, subsection));
9865   PetscCall(PetscSectionDestroy(&subsection));
9866   PetscCall(DMCopyDisc(dm, *subdm));
9867   /* Create map from submodel to global model */
9868   if (is) {
9869     PetscSection    sectionGlobal, subsectionGlobal;
9870     IS              spIS;
9871     const PetscInt *spmap;
9872     PetscInt       *subIndices;
9873     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9874     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9875 
9876     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9877     PetscCall(ISGetIndices(spIS, &spmap));
9878     PetscCall(PetscSectionGetNumFields(section, &Nf));
9879     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9880     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9881     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9882     for (p = pStart; p < pEnd; ++p) {
9883       PetscInt gdof, pSubSize = 0;
9884 
9885       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9886       if (gdof > 0) {
9887         for (f = 0; f < Nf; ++f) {
9888           PetscInt fdof, fcdof;
9889 
9890           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9891           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9892           pSubSize += fdof - fcdof;
9893         }
9894         subSize += pSubSize;
9895         if (pSubSize) {
9896           if (bs < 0) {
9897             bs = pSubSize;
9898           } else if (bs != pSubSize) {
9899             /* Layout does not admit a pointwise block size */
9900             bs = 1;
9901           }
9902         }
9903       }
9904     }
9905     /* Must have same blocksize on all procs (some might have no points) */
9906     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
9907     bsLocal[1] = bs;
9908     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
9909     if (bsMinMax[0] != bsMinMax[1]) {
9910       bs = 1;
9911     } else {
9912       bs = bsMinMax[0];
9913     }
9914     PetscCall(PetscMalloc1(subSize, &subIndices));
9915     for (p = pStart; p < pEnd; ++p) {
9916       PetscInt gdof, goff;
9917 
9918       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9919       if (gdof > 0) {
9920         const PetscInt point = spmap[p];
9921 
9922         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9923         for (f = 0; f < Nf; ++f) {
9924           PetscInt fdof, fcdof, fc, f2, poff = 0;
9925 
9926           /* Can get rid of this loop by storing field information in the global section */
9927           for (f2 = 0; f2 < f; ++f2) {
9928             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9929             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9930             poff += fdof - fcdof;
9931           }
9932           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9933           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9934           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
9935         }
9936       }
9937     }
9938     PetscCall(ISRestoreIndices(spIS, &spmap));
9939     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9940     if (bs > 1) {
9941       /* We need to check that the block size does not come from non-contiguous fields */
9942       PetscInt i, j, set = 1;
9943       for (i = 0; i < subSize; i += bs) {
9944         for (j = 0; j < bs; ++j) {
9945           if (subIndices[i + j] != subIndices[i] + j) {
9946             set = 0;
9947             break;
9948           }
9949         }
9950       }
9951       if (set) PetscCall(ISSetBlockSize(*is, bs));
9952     }
9953     /* Attach nullspace */
9954     for (f = 0; f < Nf; ++f) {
9955       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9956       if ((*subdm)->nullspaceConstructors[f]) break;
9957     }
9958     if (f < Nf) {
9959       MatNullSpace nullSpace;
9960       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9961 
9962       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
9963       PetscCall(MatNullSpaceDestroy(&nullSpace));
9964     }
9965   }
9966   PetscFunctionReturn(0);
9967 }
9968 
9969 /*@
9970   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9971 
9972   Input Parameters:
9973 + dm - The `DM`
9974 - dummy - unused argument
9975 
9976   Options Database Key:
9977 . -dm_plex_monitor_throughput - Activate the monitor
9978 
9979   Level: developer
9980 
9981 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
9982 @*/
9983 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9984 {
9985 #if defined(PETSC_USE_LOG)
9986   PetscStageLog      stageLog;
9987   PetscLogEvent      event;
9988   PetscLogStage      stage;
9989   PetscEventPerfInfo eventInfo;
9990   PetscReal          cellRate, flopRate;
9991   PetscInt           cStart, cEnd, Nf, N;
9992   const char        *name;
9993 #endif
9994 
9995   PetscFunctionBegin;
9996   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9997 #if defined(PETSC_USE_LOG)
9998   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
9999   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10000   PetscCall(DMGetNumFields(dm, &Nf));
10001   PetscCall(PetscLogGetStageLog(&stageLog));
10002   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10003   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10004   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10005   N        = (cEnd - cStart) * Nf * eventInfo.count;
10006   flopRate = eventInfo.flops / eventInfo.time;
10007   cellRate = N / eventInfo.time;
10008   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)));
10009 #else
10010   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10011 #endif
10012   PetscFunctionReturn(0);
10013 }
10014