xref: /petsc/src/dm/impls/plex/plexgeometry.c (revision fff43d004b5765114ee01f7516a1c18a84c8e78e)
1 #include <petsc/private/dmpleximpl.h>  /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/petscfeimpl.h> /*I      "petscfe.h"       I*/
3 #include <petscblaslapack.h>
4 #include <petsctime.h>
5 
6 const char *const DMPlexCoordMaps[] = {"none", "shear", "flare", "annulus", "shell", "unknown", "DMPlexCoordMap", "DM_COORD_MAP_", NULL};
7 
8 /*@
9   DMPlexFindVertices - Try to find DAG points based on their coordinates.
10 
11   Not Collective (provided `DMGetCoordinatesLocalSetUp()` has been already called)
12 
13   Input Parameters:
14 + dm          - The `DMPLEX` object
15 . coordinates - The `Vec` of coordinates of the sought points
16 - eps         - The tolerance or `PETSC_DEFAULT`
17 
18   Output Parameter:
19 . points - The `IS` of found DAG points or -1
20 
21   Level: intermediate
22 
23   Notes:
24   The length of `Vec` coordinates must be npoints * dim where dim is the spatial dimension returned by `DMGetCoordinateDim()` and npoints is the number of sought points.
25 
26   The output `IS` is living on `PETSC_COMM_SELF` and its length is npoints.
27   Each rank does the search independently.
28   If this rank's local `DMPLEX` portion contains the DAG point corresponding to the i-th tuple of coordinates, the i-th entry of the output `IS` is set to that DAG point, otherwise to -1.
29 
30   The output `IS` must be destroyed by user.
31 
32   The tolerance is interpreted as the maximum Euclidean (L2) distance of the sought point from the specified coordinates.
33 
34   Complexity of this function is currently O(mn) with m number of vertices to find and n number of vertices in the local mesh. This could probably be improved if needed.
35 
36 .seealso: `DMPLEX`, `DMPlexCreate()`, `DMGetCoordinatesLocal()`
37 @*/
38 PetscErrorCode DMPlexFindVertices(DM dm, Vec coordinates, PetscReal eps, IS *points)
39 {
40   PetscInt           c, cdim, i, j, o, p, vStart, vEnd;
41   PetscInt           npoints;
42   const PetscScalar *coord;
43   Vec                allCoordsVec;
44   const PetscScalar *allCoords;
45   PetscInt          *dagPoints;
46 
47   PetscFunctionBegin;
48   if (eps < 0) eps = PETSC_SQRT_MACHINE_EPSILON;
49   PetscCall(DMGetCoordinateDim(dm, &cdim));
50   {
51     PetscInt n;
52 
53     PetscCall(VecGetLocalSize(coordinates, &n));
54     PetscCheck(n % cdim == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Given coordinates Vec has local length %" PetscInt_FMT " not divisible by coordinate dimension %" PetscInt_FMT " of given DM", n, cdim);
55     npoints = n / cdim;
56   }
57   PetscCall(DMGetCoordinatesLocal(dm, &allCoordsVec));
58   PetscCall(VecGetArrayRead(allCoordsVec, &allCoords));
59   PetscCall(VecGetArrayRead(coordinates, &coord));
60   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
61   if (PetscDefined(USE_DEBUG)) {
62     /* check coordinate section is consistent with DM dimension */
63     PetscSection cs;
64     PetscInt     ndof;
65 
66     PetscCall(DMGetCoordinateSection(dm, &cs));
67     for (p = vStart; p < vEnd; p++) {
68       PetscCall(PetscSectionGetDof(cs, p, &ndof));
69       PetscCheck(ndof == cdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "point %" PetscInt_FMT ": ndof = %" PetscInt_FMT " != %" PetscInt_FMT " = cdim", p, ndof, cdim);
70     }
71   }
72   PetscCall(PetscMalloc1(npoints, &dagPoints));
73   if (eps == 0.0) {
74     for (i = 0, j = 0; i < npoints; i++, j += cdim) {
75       dagPoints[i] = -1;
76       for (p = vStart, o = 0; p < vEnd; p++, o += cdim) {
77         for (c = 0; c < cdim; c++) {
78           if (coord[j + c] != allCoords[o + c]) break;
79         }
80         if (c == cdim) {
81           dagPoints[i] = p;
82           break;
83         }
84       }
85     }
86   } else {
87     for (i = 0, j = 0; i < npoints; i++, j += cdim) {
88       PetscReal norm;
89 
90       dagPoints[i] = -1;
91       for (p = vStart, o = 0; p < vEnd; p++, o += cdim) {
92         norm = 0.0;
93         for (c = 0; c < cdim; c++) norm += PetscRealPart(PetscSqr(coord[j + c] - allCoords[o + c]));
94         norm = PetscSqrtReal(norm);
95         if (norm <= eps) {
96           dagPoints[i] = p;
97           break;
98         }
99       }
100     }
101   }
102   PetscCall(VecRestoreArrayRead(allCoordsVec, &allCoords));
103   PetscCall(VecRestoreArrayRead(coordinates, &coord));
104   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, npoints, dagPoints, PETSC_OWN_POINTER, points));
105   PetscFunctionReturn(PETSC_SUCCESS);
106 }
107 
108 #if 0
109 static PetscErrorCode DMPlexGetLineIntersection_2D_Internal(const PetscReal segmentA[], const PetscReal segmentB[], PetscReal intersection[], PetscBool *hasIntersection)
110 {
111   const PetscReal p0_x  = segmentA[0 * 2 + 0];
112   const PetscReal p0_y  = segmentA[0 * 2 + 1];
113   const PetscReal p1_x  = segmentA[1 * 2 + 0];
114   const PetscReal p1_y  = segmentA[1 * 2 + 1];
115   const PetscReal p2_x  = segmentB[0 * 2 + 0];
116   const PetscReal p2_y  = segmentB[0 * 2 + 1];
117   const PetscReal p3_x  = segmentB[1 * 2 + 0];
118   const PetscReal p3_y  = segmentB[1 * 2 + 1];
119   const PetscReal s1_x  = p1_x - p0_x;
120   const PetscReal s1_y  = p1_y - p0_y;
121   const PetscReal s2_x  = p3_x - p2_x;
122   const PetscReal s2_y  = p3_y - p2_y;
123   const PetscReal denom = (-s2_x * s1_y + s1_x * s2_y);
124 
125   PetscFunctionBegin;
126   *hasIntersection = PETSC_FALSE;
127   /* Non-parallel lines */
128   if (denom != 0.0) {
129     const PetscReal s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / denom;
130     const PetscReal t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / denom;
131 
132     if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
133       *hasIntersection = PETSC_TRUE;
134       if (intersection) {
135         intersection[0] = p0_x + (t * s1_x);
136         intersection[1] = p0_y + (t * s1_y);
137       }
138     }
139   }
140   PetscFunctionReturn(PETSC_SUCCESS);
141 }
142 
143 /* The plane is segmentB x segmentC: https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection */
144 static PetscErrorCode DMPlexGetLinePlaneIntersection_3D_Internal(const PetscReal segmentA[], const PetscReal segmentB[], const PetscReal segmentC[], PetscReal intersection[], PetscBool *hasIntersection)
145 {
146   const PetscReal p0_x  = segmentA[0 * 3 + 0];
147   const PetscReal p0_y  = segmentA[0 * 3 + 1];
148   const PetscReal p0_z  = segmentA[0 * 3 + 2];
149   const PetscReal p1_x  = segmentA[1 * 3 + 0];
150   const PetscReal p1_y  = segmentA[1 * 3 + 1];
151   const PetscReal p1_z  = segmentA[1 * 3 + 2];
152   const PetscReal q0_x  = segmentB[0 * 3 + 0];
153   const PetscReal q0_y  = segmentB[0 * 3 + 1];
154   const PetscReal q0_z  = segmentB[0 * 3 + 2];
155   const PetscReal q1_x  = segmentB[1 * 3 + 0];
156   const PetscReal q1_y  = segmentB[1 * 3 + 1];
157   const PetscReal q1_z  = segmentB[1 * 3 + 2];
158   const PetscReal r0_x  = segmentC[0 * 3 + 0];
159   const PetscReal r0_y  = segmentC[0 * 3 + 1];
160   const PetscReal r0_z  = segmentC[0 * 3 + 2];
161   const PetscReal r1_x  = segmentC[1 * 3 + 0];
162   const PetscReal r1_y  = segmentC[1 * 3 + 1];
163   const PetscReal r1_z  = segmentC[1 * 3 + 2];
164   const PetscReal s0_x  = p1_x - p0_x;
165   const PetscReal s0_y  = p1_y - p0_y;
166   const PetscReal s0_z  = p1_z - p0_z;
167   const PetscReal s1_x  = q1_x - q0_x;
168   const PetscReal s1_y  = q1_y - q0_y;
169   const PetscReal s1_z  = q1_z - q0_z;
170   const PetscReal s2_x  = r1_x - r0_x;
171   const PetscReal s2_y  = r1_y - r0_y;
172   const PetscReal s2_z  = r1_z - r0_z;
173   const PetscReal s3_x  = s1_y * s2_z - s1_z * s2_y; /* s1 x s2 */
174   const PetscReal s3_y  = s1_z * s2_x - s1_x * s2_z;
175   const PetscReal s3_z  = s1_x * s2_y - s1_y * s2_x;
176   const PetscReal s4_x  = s0_y * s2_z - s0_z * s2_y; /* s0 x s2 */
177   const PetscReal s4_y  = s0_z * s2_x - s0_x * s2_z;
178   const PetscReal s4_z  = s0_x * s2_y - s0_y * s2_x;
179   const PetscReal s5_x  = s1_y * s0_z - s1_z * s0_y; /* s1 x s0 */
180   const PetscReal s5_y  = s1_z * s0_x - s1_x * s0_z;
181   const PetscReal s5_z  = s1_x * s0_y - s1_y * s0_x;
182   const PetscReal denom = -(s0_x * s3_x + s0_y * s3_y + s0_z * s3_z); /* -s0 . (s1 x s2) */
183 
184   PetscFunctionBegin;
185   *hasIntersection = PETSC_FALSE;
186   /* Line not parallel to plane */
187   if (denom != 0.0) {
188     const PetscReal t = (s3_x * (p0_x - q0_x) + s3_y * (p0_y - q0_y) + s3_z * (p0_z - q0_z)) / denom;
189     const PetscReal u = (s4_x * (p0_x - q0_x) + s4_y * (p0_y - q0_y) + s4_z * (p0_z - q0_z)) / denom;
190     const PetscReal v = (s5_x * (p0_x - q0_x) + s5_y * (p0_y - q0_y) + s5_z * (p0_z - q0_z)) / denom;
191 
192     if (t >= 0 && t <= 1 && u >= 0 && u <= 1 && v >= 0 && v <= 1) {
193       *hasIntersection = PETSC_TRUE;
194       if (intersection) {
195         intersection[0] = p0_x + (t * s0_x);
196         intersection[1] = p0_y + (t * s0_y);
197         intersection[2] = p0_z + (t * s0_z);
198       }
199     }
200   }
201   PetscFunctionReturn(PETSC_SUCCESS);
202 }
203 #endif
204 
205 static PetscErrorCode DMPlexGetPlaneSimplexIntersection_Coords_Internal(DM dm, PetscInt dim, PetscInt cdim, const PetscScalar coords[], const PetscReal p[], const PetscReal normal[], PetscBool *pos, PetscInt *Nint, PetscReal intPoints[])
206 {
207   PetscReal d[4]; // distance of vertices to the plane
208   PetscReal dp;   // distance from origin to the plane
209   PetscInt  n = 0;
210 
211   PetscFunctionBegin;
212   if (pos) *pos = PETSC_FALSE;
213   if (Nint) *Nint = 0;
214   if (PetscDefined(USE_DEBUG)) {
215     PetscReal mag = DMPlex_NormD_Internal(cdim, normal);
216     PetscCheck(PetscAbsReal(mag - (PetscReal)1.0) < PETSC_SMALL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Normal vector is not normalized: %g", (double)mag);
217   }
218 
219   dp = DMPlex_DotRealD_Internal(cdim, normal, p);
220   for (PetscInt v = 0; v < dim + 1; ++v) {
221     // d[v] is positive, zero, or negative if vertex i is above, on, or below the plane
222 #if defined(PETSC_USE_COMPLEX)
223     PetscReal c[4];
224     for (PetscInt i = 0; i < cdim; ++i) c[i] = PetscRealPart(coords[v * cdim + i]);
225     d[v] = DMPlex_DotRealD_Internal(cdim, normal, c);
226 #else
227     d[v] = DMPlex_DotRealD_Internal(cdim, normal, &coords[v * cdim]);
228 #endif
229     d[v] -= dp;
230   }
231 
232   // If all d are positive or negative, no intersection
233   {
234     PetscInt v;
235     for (v = 0; v < dim + 1; ++v)
236       if (d[v] >= 0.) break;
237     if (v == dim + 1) PetscFunctionReturn(PETSC_SUCCESS);
238     for (v = 0; v < dim + 1; ++v)
239       if (d[v] <= 0.) break;
240     if (v == dim + 1) {
241       if (pos) *pos = PETSC_TRUE;
242       PetscFunctionReturn(PETSC_SUCCESS);
243     }
244   }
245 
246   for (PetscInt v = 0; v < dim + 1; ++v) {
247     // Points with zero distance are automatically added to the list.
248     if (PetscAbsReal(d[v]) < PETSC_MACHINE_EPSILON) {
249       for (PetscInt i = 0; i < cdim; ++i) intPoints[n * cdim + i] = PetscRealPart(coords[v * cdim + i]);
250       ++n;
251     } else {
252       // For each point with nonzero distance, seek another point with opposite sign
253       // and higher index, and compute the intersection of the line between those
254       // points and the plane.
255       for (PetscInt w = v + 1; w < dim + 1; ++w) {
256         if (d[v] * d[w] < 0.) {
257           PetscReal inv_dist = 1. / (d[v] - d[w]);
258           for (PetscInt i = 0; i < cdim; ++i) intPoints[n * cdim + i] = (d[v] * PetscRealPart(coords[w * cdim + i]) - d[w] * PetscRealPart(coords[v * cdim + i])) * inv_dist;
259           ++n;
260         }
261       }
262     }
263   }
264   // TODO order output points if there are 4
265   *Nint = n;
266   PetscFunctionReturn(PETSC_SUCCESS);
267 }
268 
269 static PetscErrorCode DMPlexGetPlaneSimplexIntersection_Internal(DM dm, PetscInt dim, PetscInt c, const PetscReal p[], const PetscReal normal[], PetscBool *pos, PetscInt *Nint, PetscReal intPoints[])
270 {
271   const PetscScalar *array;
272   PetscScalar       *coords = NULL;
273   PetscInt           numCoords;
274   PetscBool          isDG;
275   PetscInt           cdim;
276 
277   PetscFunctionBegin;
278   PetscCall(DMGetCoordinateDim(dm, &cdim));
279   PetscCheck(cdim == dim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM has coordinates in %" PetscInt_FMT "D instead of %" PetscInt_FMT "D", cdim, dim);
280   PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
281   PetscCheck(numCoords == dim * (dim + 1), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Tetrahedron should have %" PetscInt_FMT " coordinates, not %" PetscInt_FMT, dim * (dim + 1), numCoords);
282   PetscCall(PetscArrayzero(intPoints, dim * (dim + 1)));
283 
284   PetscCall(DMPlexGetPlaneSimplexIntersection_Coords_Internal(dm, dim, cdim, coords, p, normal, pos, Nint, intPoints));
285 
286   PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
287   PetscFunctionReturn(PETSC_SUCCESS);
288 }
289 
290 static PetscErrorCode DMPlexGetPlaneQuadIntersection_Internal(DM dm, PetscInt dim, PetscInt c, const PetscReal p[], const PetscReal normal[], PetscBool *pos, PetscInt *Nint, PetscReal intPoints[])
291 {
292   const PetscScalar *array;
293   PetscScalar       *coords = NULL;
294   PetscInt           numCoords;
295   PetscBool          isDG;
296   PetscInt           cdim;
297   PetscScalar        tcoords[6] = {0., 0., 0., 0., 0., 0.};
298   const PetscInt     vertsA[3]  = {0, 1, 3};
299   const PetscInt     vertsB[3]  = {1, 2, 3};
300   PetscInt           NintA, NintB;
301 
302   PetscFunctionBegin;
303   PetscCall(DMGetCoordinateDim(dm, &cdim));
304   PetscCheck(cdim == dim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM has coordinates in %" PetscInt_FMT "D instead of %" PetscInt_FMT "D", cdim, dim);
305   PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
306   PetscCheck(numCoords == dim * 4, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Quadrilateral should have %" PetscInt_FMT " coordinates, not %" PetscInt_FMT, dim * 4, numCoords);
307   PetscCall(PetscArrayzero(intPoints, dim * 4));
308 
309   for (PetscInt v = 0; v < 3; ++v)
310     for (PetscInt d = 0; d < cdim; ++d) tcoords[v * cdim + d] = coords[vertsA[v] * cdim + d];
311   PetscCall(DMPlexGetPlaneSimplexIntersection_Coords_Internal(dm, dim, cdim, tcoords, p, normal, pos, &NintA, intPoints));
312   for (PetscInt v = 0; v < 3; ++v)
313     for (PetscInt d = 0; d < cdim; ++d) tcoords[v * cdim + d] = coords[vertsB[v] * cdim + d];
314   PetscCall(DMPlexGetPlaneSimplexIntersection_Coords_Internal(dm, dim, cdim, tcoords, p, normal, pos, &NintB, &intPoints[NintA * cdim]));
315   *Nint = NintA + NintB;
316 
317   PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
318   PetscFunctionReturn(PETSC_SUCCESS);
319 }
320 
321 static PetscErrorCode DMPlexGetPlaneHexIntersection_Internal(DM dm, PetscInt dim, PetscInt c, const PetscReal p[], const PetscReal normal[], PetscBool *pos, PetscInt *Nint, PetscReal intPoints[])
322 {
323   const PetscScalar *array;
324   PetscScalar       *coords = NULL;
325   PetscInt           numCoords;
326   PetscBool          isDG;
327   PetscInt           cdim;
328   PetscScalar        tcoords[12] = {0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.};
329   // We split using the (2, 4) main diagonal, so all tets contain those vertices
330   const PetscInt vertsA[4] = {0, 1, 2, 4};
331   const PetscInt vertsB[4] = {0, 2, 3, 4};
332   const PetscInt vertsC[4] = {1, 7, 2, 4};
333   const PetscInt vertsD[4] = {2, 7, 6, 4};
334   const PetscInt vertsE[4] = {3, 5, 4, 2};
335   const PetscInt vertsF[4] = {4, 5, 6, 2};
336   PetscInt       NintA, NintB, NintC, NintD, NintE, NintF, Nsum = 0;
337 
338   PetscFunctionBegin;
339   PetscCall(DMGetCoordinateDim(dm, &cdim));
340   PetscCheck(cdim == dim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM has coordinates in %" PetscInt_FMT "D instead of %" PetscInt_FMT "D", cdim, dim);
341   PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
342   PetscCheck(numCoords == dim * 8, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Hexahedron should have %" PetscInt_FMT " coordinates, not %" PetscInt_FMT, dim * 8, numCoords);
343   PetscCall(PetscArrayzero(intPoints, dim * 18));
344 
345   for (PetscInt v = 0; v < 4; ++v)
346     for (PetscInt d = 0; d < cdim; ++d) tcoords[v * cdim + d] = coords[vertsA[v] * cdim + d];
347   PetscCall(DMPlexGetPlaneSimplexIntersection_Coords_Internal(dm, dim, cdim, tcoords, p, normal, pos, &NintA, &intPoints[Nsum * cdim]));
348   Nsum += NintA;
349   for (PetscInt v = 0; v < 4; ++v)
350     for (PetscInt d = 0; d < cdim; ++d) tcoords[v * cdim + d] = coords[vertsB[v] * cdim + d];
351   PetscCall(DMPlexGetPlaneSimplexIntersection_Coords_Internal(dm, dim, cdim, tcoords, p, normal, pos, &NintB, &intPoints[Nsum * cdim]));
352   Nsum += NintB;
353   for (PetscInt v = 0; v < 4; ++v)
354     for (PetscInt d = 0; d < cdim; ++d) tcoords[v * cdim + d] = coords[vertsC[v] * cdim + d];
355   PetscCall(DMPlexGetPlaneSimplexIntersection_Coords_Internal(dm, dim, cdim, tcoords, p, normal, pos, &NintC, &intPoints[Nsum * cdim]));
356   Nsum += NintC;
357   for (PetscInt v = 0; v < 4; ++v)
358     for (PetscInt d = 0; d < cdim; ++d) tcoords[v * cdim + d] = coords[vertsD[v] * cdim + d];
359   PetscCall(DMPlexGetPlaneSimplexIntersection_Coords_Internal(dm, dim, cdim, tcoords, p, normal, pos, &NintD, &intPoints[Nsum * cdim]));
360   Nsum += NintD;
361   for (PetscInt v = 0; v < 4; ++v)
362     for (PetscInt d = 0; d < cdim; ++d) tcoords[v * cdim + d] = coords[vertsE[v] * cdim + d];
363   PetscCall(DMPlexGetPlaneSimplexIntersection_Coords_Internal(dm, dim, cdim, tcoords, p, normal, pos, &NintE, &intPoints[Nsum * cdim]));
364   Nsum += NintE;
365   for (PetscInt v = 0; v < 4; ++v)
366     for (PetscInt d = 0; d < cdim; ++d) tcoords[v * cdim + d] = coords[vertsF[v] * cdim + d];
367   PetscCall(DMPlexGetPlaneSimplexIntersection_Coords_Internal(dm, dim, cdim, tcoords, p, normal, pos, &NintF, &intPoints[Nsum * cdim]));
368   Nsum += NintF;
369   *Nint = Nsum;
370 
371   PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
372   PetscFunctionReturn(PETSC_SUCCESS);
373 }
374 
375 /*
376   DMPlexGetPlaneCellIntersection_Internal - Finds the intersection of a plane with a cell
377 
378   Not collective
379 
380   Input Parameters:
381 + dm     - the DM
382 . c      - the mesh point
383 . p      - a point on the plane.
384 - normal - a normal vector to the plane, must be normalized
385 
386   Output Parameters:
387 . pos       - `PETSC_TRUE` is the cell is on the positive side of the plane, `PETSC_FALSE` on the negative side
388 + Nint      - the number of intersection points, in [0, 4]
389 - intPoints - the coordinates of the intersection points, should be length at least 12
390 
391   Note: The `pos` argument is only meaningful if the number of intersections is 0. The algorithmic idea comes from https://github.com/chrisk314/tet-plane-intersection.
392 
393   Level: developer
394 
395 .seealso:
396 @*/
397 static PetscErrorCode DMPlexGetPlaneCellIntersection_Internal(DM dm, PetscInt c, const PetscReal p[], const PetscReal normal[], PetscBool *pos, PetscInt *Nint, PetscReal intPoints[])
398 {
399   DMPolytopeType ct;
400 
401   PetscFunctionBegin;
402   PetscCall(DMPlexGetCellType(dm, c, &ct));
403   switch (ct) {
404   case DM_POLYTOPE_SEGMENT:
405   case DM_POLYTOPE_TRIANGLE:
406   case DM_POLYTOPE_TETRAHEDRON:
407     PetscCall(DMPlexGetPlaneSimplexIntersection_Internal(dm, DMPolytopeTypeGetDim(ct), c, p, normal, pos, Nint, intPoints));
408     break;
409   case DM_POLYTOPE_QUADRILATERAL:
410     PetscCall(DMPlexGetPlaneQuadIntersection_Internal(dm, DMPolytopeTypeGetDim(ct), c, p, normal, pos, Nint, intPoints));
411     break;
412   case DM_POLYTOPE_HEXAHEDRON:
413     PetscCall(DMPlexGetPlaneHexIntersection_Internal(dm, DMPolytopeTypeGetDim(ct), c, p, normal, pos, Nint, intPoints));
414     break;
415   default:
416     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No plane intersection for cell %" PetscInt_FMT " with type %s", c, DMPolytopeTypes[ct]);
417   }
418   PetscFunctionReturn(PETSC_SUCCESS);
419 }
420 
421 static PetscErrorCode DMPlexLocatePoint_Simplex_1D_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscInt *cell)
422 {
423   const PetscReal eps = PETSC_SQRT_MACHINE_EPSILON;
424   const PetscReal x   = PetscRealPart(point[0]);
425   PetscReal       v0, J, invJ, detJ;
426   PetscReal       xi;
427 
428   PetscFunctionBegin;
429   PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, &v0, &J, &invJ, &detJ));
430   xi = invJ * (x - v0);
431 
432   if ((xi >= -eps) && (xi <= 2. + eps)) *cell = c;
433   else *cell = DMLOCATEPOINT_POINT_NOT_FOUND;
434   PetscFunctionReturn(PETSC_SUCCESS);
435 }
436 
437 static PetscErrorCode DMPlexLocatePoint_Simplex_2D_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscInt *cell)
438 {
439   const PetscInt  embedDim = 2;
440   const PetscReal eps      = PETSC_SQRT_MACHINE_EPSILON;
441   PetscReal       x        = PetscRealPart(point[0]);
442   PetscReal       y        = PetscRealPart(point[1]);
443   PetscReal       v0[2], J[4], invJ[4], detJ;
444   PetscReal       xi, eta;
445 
446   PetscFunctionBegin;
447   PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v0, J, invJ, &detJ));
448   xi  = invJ[0 * embedDim + 0] * (x - v0[0]) + invJ[0 * embedDim + 1] * (y - v0[1]);
449   eta = invJ[1 * embedDim + 0] * (x - v0[0]) + invJ[1 * embedDim + 1] * (y - v0[1]);
450 
451   if ((xi >= -eps) && (eta >= -eps) && (xi + eta <= 2.0 + eps)) *cell = c;
452   else *cell = DMLOCATEPOINT_POINT_NOT_FOUND;
453   PetscFunctionReturn(PETSC_SUCCESS);
454 }
455 
456 static PetscErrorCode DMPlexClosestPoint_Simplex_2D_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscReal cpoint[])
457 {
458   const PetscInt embedDim = 2;
459   PetscReal      x        = PetscRealPart(point[0]);
460   PetscReal      y        = PetscRealPart(point[1]);
461   PetscReal      v0[2], J[4], invJ[4], detJ;
462   PetscReal      xi, eta, r;
463 
464   PetscFunctionBegin;
465   PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v0, J, invJ, &detJ));
466   xi  = invJ[0 * embedDim + 0] * (x - v0[0]) + invJ[0 * embedDim + 1] * (y - v0[1]);
467   eta = invJ[1 * embedDim + 0] * (x - v0[0]) + invJ[1 * embedDim + 1] * (y - v0[1]);
468 
469   xi  = PetscMax(xi, 0.0);
470   eta = PetscMax(eta, 0.0);
471   if (xi + eta > 2.0) {
472     r = (xi + eta) / 2.0;
473     xi /= r;
474     eta /= r;
475   }
476   cpoint[0] = J[0 * embedDim + 0] * xi + J[0 * embedDim + 1] * eta + v0[0];
477   cpoint[1] = J[1 * embedDim + 0] * xi + J[1 * embedDim + 1] * eta + v0[1];
478   PetscFunctionReturn(PETSC_SUCCESS);
479 }
480 
481 // This is the ray-casting, or even-odd algorithm: https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule
482 static PetscErrorCode DMPlexLocatePoint_Quad_2D_Linear_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscInt *cell)
483 {
484   const PetscScalar *array;
485   PetscScalar       *coords    = NULL;
486   const PetscInt     faces[8]  = {0, 1, 1, 2, 2, 3, 3, 0};
487   PetscReal          x         = PetscRealPart(point[0]);
488   PetscReal          y         = PetscRealPart(point[1]);
489   PetscInt           crossings = 0, numCoords, f;
490   PetscBool          isDG;
491 
492   PetscFunctionBegin;
493   PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
494   PetscCheck(numCoords == 8, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Quadrilateral should have 8 coordinates, not %" PetscInt_FMT, numCoords);
495   for (f = 0; f < 4; ++f) {
496     PetscReal x_i = PetscRealPart(coords[faces[2 * f + 0] * 2 + 0]);
497     PetscReal y_i = PetscRealPart(coords[faces[2 * f + 0] * 2 + 1]);
498     PetscReal x_j = PetscRealPart(coords[faces[2 * f + 1] * 2 + 0]);
499     PetscReal y_j = PetscRealPart(coords[faces[2 * f + 1] * 2 + 1]);
500 
501     if ((x == x_j) && (y == y_j)) {
502       // point is a corner
503       crossings = 1;
504       break;
505     }
506     if ((y_j > y) != (y_i > y)) {
507       PetscReal slope = (x - x_j) * (y_i - y_j) - (x_i - x_j) * (y - y_j);
508       if (slope == 0) {
509         // point is a corner
510         crossings = 1;
511         break;
512       }
513       if ((slope < 0) != (y_i < y_j)) ++crossings;
514     }
515   }
516   if (crossings % 2) *cell = c;
517   else *cell = DMLOCATEPOINT_POINT_NOT_FOUND;
518   PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
519   PetscFunctionReturn(PETSC_SUCCESS);
520 }
521 
522 static PetscErrorCode DMPlexLocatePoint_Quad_2D_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscInt *cell)
523 {
524   DM           cdm;
525   PetscInt     degree, dimR, dimC;
526   PetscFE      fe;
527   PetscClassId id;
528   PetscSpace   sp;
529   PetscReal    pointR[3], ref[3], error;
530   Vec          coords;
531   PetscBool    found = PETSC_FALSE;
532 
533   PetscFunctionBegin;
534   PetscCall(DMGetDimension(dm, &dimR));
535   PetscCall(DMGetCoordinateDM(dm, &cdm));
536   PetscCall(DMGetDimension(cdm, &dimC));
537   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
538   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
539   if (id != PETSCFE_CLASSID) degree = 1;
540   else {
541     PetscCall(PetscFEGetBasisSpace(fe, &sp));
542     PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
543   }
544   if (degree == 1) {
545     /* Use simple location method for linear elements*/
546     PetscCall(DMPlexLocatePoint_Quad_2D_Linear_Internal(dm, point, c, cell));
547     PetscFunctionReturn(PETSC_SUCCESS);
548   }
549   /* Otherwise, we have to solve for the real to reference coordinates */
550   PetscCall(DMGetCoordinatesLocal(dm, &coords));
551   error = PETSC_SQRT_MACHINE_EPSILON;
552   for (PetscInt d = 0; d < dimC; d++) pointR[d] = PetscRealPart(point[d]);
553   PetscCall(DMPlexCoordinatesToReference_FE(cdm, fe, c, 1, pointR, ref, coords, dimC, dimR, 10, &error));
554   if (error < PETSC_SQRT_MACHINE_EPSILON) found = PETSC_TRUE;
555   if ((ref[0] > 1.0 + PETSC_SMALL) || (ref[0] < -1.0 - PETSC_SMALL) || (ref[1] > 1.0 + PETSC_SMALL) || (ref[1] < -1.0 - PETSC_SMALL)) found = PETSC_FALSE;
556   if (PetscDefined(USE_DEBUG) && found) {
557     PetscReal real[3], inverseError = 0, normPoint = DMPlex_NormD_Internal(dimC, pointR);
558 
559     normPoint = normPoint > PETSC_SMALL ? normPoint : 1.0;
560     PetscCall(DMPlexReferenceToCoordinates_FE(cdm, fe, c, 1, ref, real, coords, dimC, dimR));
561     inverseError = DMPlex_DistRealD_Internal(dimC, real, pointR);
562     if (inverseError > PETSC_SQRT_MACHINE_EPSILON * normPoint) found = PETSC_FALSE;
563     if (!found) PetscCall(PetscInfo(dm, "Point (%g, %g, %g) != Mapped Ref Coords (%g, %g, %g) with error %g\n", (double)pointR[0], (double)pointR[1], (double)pointR[2], (double)real[0], (double)real[1], (double)real[2], (double)inverseError));
564   }
565   if (found) *cell = c;
566   else *cell = DMLOCATEPOINT_POINT_NOT_FOUND;
567   PetscFunctionReturn(PETSC_SUCCESS);
568 }
569 
570 static PetscErrorCode DMPlexLocatePoint_Simplex_3D_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscInt *cell)
571 {
572   const PetscInt  embedDim = 3;
573   const PetscReal eps      = PETSC_SQRT_MACHINE_EPSILON;
574   PetscReal       v0[3], J[9], invJ[9], detJ;
575   PetscReal       x = PetscRealPart(point[0]);
576   PetscReal       y = PetscRealPart(point[1]);
577   PetscReal       z = PetscRealPart(point[2]);
578   PetscReal       xi, eta, zeta;
579 
580   PetscFunctionBegin;
581   PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v0, J, invJ, &detJ));
582   xi   = invJ[0 * embedDim + 0] * (x - v0[0]) + invJ[0 * embedDim + 1] * (y - v0[1]) + invJ[0 * embedDim + 2] * (z - v0[2]);
583   eta  = invJ[1 * embedDim + 0] * (x - v0[0]) + invJ[1 * embedDim + 1] * (y - v0[1]) + invJ[1 * embedDim + 2] * (z - v0[2]);
584   zeta = invJ[2 * embedDim + 0] * (x - v0[0]) + invJ[2 * embedDim + 1] * (y - v0[1]) + invJ[2 * embedDim + 2] * (z - v0[2]);
585 
586   if ((xi >= -eps) && (eta >= -eps) && (zeta >= -eps) && (xi + eta + zeta <= 2.0 + eps)) *cell = c;
587   else *cell = DMLOCATEPOINT_POINT_NOT_FOUND;
588   PetscFunctionReturn(PETSC_SUCCESS);
589 }
590 
591 static PetscErrorCode DMPlexLocatePoint_Hex_3D_Linear_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscInt *cell)
592 {
593   const PetscScalar *array;
594   PetscScalar       *coords    = NULL;
595   const PetscInt     faces[24] = {0, 3, 2, 1, 5, 4, 7, 6, 3, 0, 4, 5, 1, 2, 6, 7, 3, 5, 6, 2, 0, 1, 7, 4};
596   PetscBool          found     = PETSC_TRUE;
597   PetscInt           numCoords, f;
598   PetscBool          isDG;
599 
600   PetscFunctionBegin;
601   PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
602   PetscCheck(numCoords == 24, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Quadrilateral should have 8 coordinates, not %" PetscInt_FMT, numCoords);
603   for (f = 0; f < 6; ++f) {
604     /* Check the point is under plane */
605     /*   Get face normal */
606     PetscReal v_i[3];
607     PetscReal v_j[3];
608     PetscReal normal[3];
609     PetscReal pp[3];
610     PetscReal dot;
611 
612     v_i[0]    = PetscRealPart(coords[faces[f * 4 + 3] * 3 + 0] - coords[faces[f * 4 + 0] * 3 + 0]);
613     v_i[1]    = PetscRealPart(coords[faces[f * 4 + 3] * 3 + 1] - coords[faces[f * 4 + 0] * 3 + 1]);
614     v_i[2]    = PetscRealPart(coords[faces[f * 4 + 3] * 3 + 2] - coords[faces[f * 4 + 0] * 3 + 2]);
615     v_j[0]    = PetscRealPart(coords[faces[f * 4 + 1] * 3 + 0] - coords[faces[f * 4 + 0] * 3 + 0]);
616     v_j[1]    = PetscRealPart(coords[faces[f * 4 + 1] * 3 + 1] - coords[faces[f * 4 + 0] * 3 + 1]);
617     v_j[2]    = PetscRealPart(coords[faces[f * 4 + 1] * 3 + 2] - coords[faces[f * 4 + 0] * 3 + 2]);
618     normal[0] = v_i[1] * v_j[2] - v_i[2] * v_j[1];
619     normal[1] = v_i[2] * v_j[0] - v_i[0] * v_j[2];
620     normal[2] = v_i[0] * v_j[1] - v_i[1] * v_j[0];
621     pp[0]     = PetscRealPart(coords[faces[f * 4 + 0] * 3 + 0] - point[0]);
622     pp[1]     = PetscRealPart(coords[faces[f * 4 + 0] * 3 + 1] - point[1]);
623     pp[2]     = PetscRealPart(coords[faces[f * 4 + 0] * 3 + 2] - point[2]);
624     dot       = normal[0] * pp[0] + normal[1] * pp[1] + normal[2] * pp[2];
625 
626     /* Check that projected point is in face (2D location problem) */
627     if (dot < 0.0) {
628       found = PETSC_FALSE;
629       break;
630     }
631   }
632   if (found) *cell = c;
633   else *cell = DMLOCATEPOINT_POINT_NOT_FOUND;
634   PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
635   PetscFunctionReturn(PETSC_SUCCESS);
636 }
637 
638 static PetscErrorCode DMPlexLocatePoint_Hex_3D_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscInt *cell)
639 {
640   DM           cdm;
641   PetscInt     degree, dimR, dimC;
642   PetscFE      fe;
643   PetscClassId id;
644   PetscSpace   sp;
645   PetscReal    pointR[3], ref[3], error;
646   Vec          coords;
647   PetscBool    found = PETSC_FALSE;
648 
649   PetscFunctionBegin;
650   PetscCall(DMGetDimension(dm, &dimR));
651   PetscCall(DMGetCoordinateDM(dm, &cdm));
652   PetscCall(DMGetDimension(cdm, &dimC));
653   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
654   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
655   if (id != PETSCFE_CLASSID) degree = 1;
656   else {
657     PetscCall(PetscFEGetBasisSpace(fe, &sp));
658     PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
659   }
660   if (degree == 1) {
661     /* Use simple location method for linear elements*/
662     PetscCall(DMPlexLocatePoint_Hex_3D_Linear_Internal(dm, point, c, cell));
663     PetscFunctionReturn(PETSC_SUCCESS);
664   }
665   /* Otherwise, we have to solve for the real to reference coordinates */
666   PetscCall(DMGetCoordinatesLocal(dm, &coords));
667   error = PETSC_SQRT_MACHINE_EPSILON;
668   for (PetscInt d = 0; d < dimC; d++) pointR[d] = PetscRealPart(point[d]);
669   PetscCall(DMPlexCoordinatesToReference_FE(cdm, fe, c, 1, pointR, ref, coords, dimC, dimR, 10, &error));
670   if (error < PETSC_SQRT_MACHINE_EPSILON) found = PETSC_TRUE;
671   if ((ref[0] > 1.0 + PETSC_SMALL) || (ref[0] < -1.0 - PETSC_SMALL) || (ref[1] > 1.0 + PETSC_SMALL) || (ref[1] < -1.0 - PETSC_SMALL) || (ref[2] > 1.0 + PETSC_SMALL) || (ref[2] < -1.0 - PETSC_SMALL)) found = PETSC_FALSE;
672   if (PetscDefined(USE_DEBUG) && found) {
673     PetscReal real[3], inverseError = 0, normPoint = DMPlex_NormD_Internal(dimC, pointR);
674 
675     normPoint = normPoint > PETSC_SMALL ? normPoint : 1.0;
676     PetscCall(DMPlexReferenceToCoordinates_FE(cdm, fe, c, 1, ref, real, coords, dimC, dimR));
677     inverseError = DMPlex_DistRealD_Internal(dimC, real, pointR);
678     if (inverseError > PETSC_SQRT_MACHINE_EPSILON * normPoint) found = PETSC_FALSE;
679     if (!found) PetscCall(PetscInfo(dm, "Point (%g, %g, %g) != Mapped Ref Coords (%g, %g, %g) with error %g\n", (double)pointR[0], (double)pointR[1], (double)pointR[2], (double)real[0], (double)real[1], (double)real[2], (double)inverseError));
680   }
681   if (found) *cell = c;
682   else *cell = DMLOCATEPOINT_POINT_NOT_FOUND;
683   PetscFunctionReturn(PETSC_SUCCESS);
684 }
685 
686 static PetscErrorCode PetscGridHashInitialize_Internal(PetscGridHash box, PetscInt dim, const PetscScalar point[])
687 {
688   PetscInt d;
689 
690   PetscFunctionBegin;
691   box->dim = dim;
692   for (d = 0; d < dim; ++d) box->lower[d] = box->upper[d] = point ? PetscRealPart(point[d]) : 0.;
693   PetscFunctionReturn(PETSC_SUCCESS);
694 }
695 
696 PetscErrorCode PetscGridHashCreate(MPI_Comm comm, PetscInt dim, const PetscScalar point[], PetscGridHash *box)
697 {
698   PetscFunctionBegin;
699   PetscCall(PetscCalloc1(1, box));
700   PetscCall(PetscGridHashInitialize_Internal(*box, dim, point));
701   PetscFunctionReturn(PETSC_SUCCESS);
702 }
703 
704 PetscErrorCode PetscGridHashEnlarge(PetscGridHash box, const PetscScalar point[])
705 {
706   PetscInt d;
707 
708   PetscFunctionBegin;
709   for (d = 0; d < box->dim; ++d) {
710     box->lower[d] = PetscMin(box->lower[d], PetscRealPart(point[d]));
711     box->upper[d] = PetscMax(box->upper[d], PetscRealPart(point[d]));
712   }
713   PetscFunctionReturn(PETSC_SUCCESS);
714 }
715 
716 static PetscErrorCode DMPlexCreateGridHash(DM dm, PetscGridHash *box)
717 {
718   Vec                coordinates;
719   const PetscScalar *a;
720   PetscInt           cdim, cStart, cEnd;
721 
722   PetscFunctionBegin;
723   PetscCall(DMGetCoordinateDim(dm, &cdim));
724   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
725   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
726 
727   PetscCall(VecGetArrayRead(coordinates, &a));
728   PetscCall(PetscGridHashCreate(PetscObjectComm((PetscObject)dm), cdim, a, box));
729   PetscCall(VecRestoreArrayRead(coordinates, &a));
730   for (PetscInt c = cStart; c < cEnd; ++c) {
731     const PetscScalar *array;
732     PetscScalar       *coords = NULL;
733     PetscInt           numCoords;
734     PetscBool          isDG;
735 
736     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
737     for (PetscInt i = 0; i < numCoords / cdim; ++i) PetscCall(PetscGridHashEnlarge(*box, &coords[i * cdim]));
738     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
739   }
740   PetscFunctionReturn(PETSC_SUCCESS);
741 }
742 
743 /*@C
744   PetscGridHashSetGrid - Divide the grid into boxes
745 
746   Not Collective
747 
748   Input Parameters:
749 + box - The grid hash object
750 . n   - The number of boxes in each dimension, may use `PETSC_DETERMINE` for the entries
751 - h   - The box size in each dimension, only used if n[d] == `PETSC_DETERMINE`, if not needed you can pass in `NULL`
752 
753   Level: developer
754 
755 .seealso: `DMPLEX`, `PetscGridHashCreate()`
756 @*/
757 PetscErrorCode PetscGridHashSetGrid(PetscGridHash box, const PetscInt n[], const PetscReal h[])
758 {
759   PetscInt d;
760 
761   PetscFunctionBegin;
762   PetscAssertPointer(n, 2);
763   if (h) PetscAssertPointer(h, 3);
764   for (d = 0; d < box->dim; ++d) {
765     box->extent[d] = box->upper[d] - box->lower[d];
766     if (n[d] == PETSC_DETERMINE) {
767       PetscCheck(h, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Missing h");
768       box->h[d] = h[d];
769       box->n[d] = PetscCeilReal(box->extent[d] / h[d]);
770     } else {
771       box->n[d] = n[d];
772       box->h[d] = box->extent[d] / n[d];
773     }
774   }
775   PetscFunctionReturn(PETSC_SUCCESS);
776 }
777 
778 /*@C
779   PetscGridHashGetEnclosingBox - Find the grid boxes containing each input point
780 
781   Not Collective
782 
783   Input Parameters:
784 + box       - The grid hash object
785 . numPoints - The number of input points
786 - points    - The input point coordinates
787 
788   Output Parameters:
789 + dboxes - An array of `numPoints` x `dim` integers expressing the enclosing box as (i_0, i_1, ..., i_dim)
790 - boxes  - An array of `numPoints` integers expressing the enclosing box as single number, or `NULL`
791 
792   Level: developer
793 
794   Note:
795   This only guarantees that a box contains a point, not that a cell does.
796 
797 .seealso: `DMPLEX`, `PetscGridHashCreate()`
798 @*/
799 PetscErrorCode PetscGridHashGetEnclosingBox(PetscGridHash box, PetscInt numPoints, const PetscScalar points[], PetscInt dboxes[], PetscInt boxes[])
800 {
801   const PetscReal *lower = box->lower;
802   const PetscReal *upper = box->upper;
803   const PetscReal *h     = box->h;
804   const PetscInt  *n     = box->n;
805   const PetscInt   dim   = box->dim;
806   PetscInt         d, p;
807 
808   PetscFunctionBegin;
809   for (p = 0; p < numPoints; ++p) {
810     for (d = 0; d < dim; ++d) {
811       PetscInt dbox = PetscFloorReal((PetscRealPart(points[p * dim + d]) - lower[d]) / h[d]);
812 
813       if (dbox == n[d] && PetscAbsReal(PetscRealPart(points[p * dim + d]) - upper[d]) < 1.0e-9) dbox = n[d] - 1;
814       if (dbox == -1 && PetscAbsReal(PetscRealPart(points[p * dim + d]) - lower[d]) < 1.0e-9) dbox = 0;
815       PetscCheck(dbox >= 0 && dbox < n[d], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Input point %" PetscInt_FMT " (%g, %g, %g) is outside of our bounding box (%g, %g, %g) - (%g, %g, %g)", p, (double)PetscRealPart(points[p * dim + 0]), dim > 1 ? (double)PetscRealPart(points[p * dim + 1]) : 0.0, dim > 2 ? (double)PetscRealPart(points[p * dim + 2]) : 0.0, (double)lower[0], (double)lower[1], (double)lower[2], (double)upper[0], (double)upper[1], (double)upper[2]);
816       dboxes[p * dim + d] = dbox;
817     }
818     if (boxes)
819       for (d = dim - 2, boxes[p] = dboxes[p * dim + dim - 1]; d >= 0; --d) boxes[p] = boxes[p] * n[d] + dboxes[p * dim + d];
820   }
821   PetscFunctionReturn(PETSC_SUCCESS);
822 }
823 
824 /*
825   PetscGridHashGetEnclosingBoxQuery - Find the grid boxes containing each input point
826 
827   Not Collective
828 
829   Input Parameters:
830 + box         - The grid hash object
831 . cellSection - The PetscSection mapping cells to boxes
832 . numPoints   - The number of input points
833 - points      - The input point coordinates
834 
835   Output Parameters:
836 + dboxes - An array of `numPoints`*`dim` integers expressing the enclosing box as (i_0, i_1, ..., i_dim)
837 . boxes  - An array of `numPoints` integers expressing the enclosing box as single number, or `NULL`
838 - found  - Flag indicating if point was located within a box
839 
840   Level: developer
841 
842   Note:
843   This does an additional check that a cell actually contains the point, and found is `PETSC_FALSE` if no cell does. Thus, this function requires that `cellSection` is already constructed.
844 
845 .seealso: `DMPLEX`, `PetscGridHashGetEnclosingBox()`
846 */
847 static PetscErrorCode PetscGridHashGetEnclosingBoxQuery(PetscGridHash box, PetscSection cellSection, PetscInt numPoints, const PetscScalar points[], PetscInt dboxes[], PetscInt boxes[], PetscBool *found)
848 {
849   const PetscReal *lower = box->lower;
850   const PetscReal *upper = box->upper;
851   const PetscReal *h     = box->h;
852   const PetscInt  *n     = box->n;
853   const PetscInt   dim   = box->dim;
854   PetscInt         bStart, bEnd, d, p;
855 
856   PetscFunctionBegin;
857   PetscValidHeaderSpecific(cellSection, PETSC_SECTION_CLASSID, 2);
858   *found = PETSC_FALSE;
859   PetscCall(PetscSectionGetChart(box->cellSection, &bStart, &bEnd));
860   for (p = 0; p < numPoints; ++p) {
861     for (d = 0; d < dim; ++d) {
862       PetscInt dbox = PetscFloorReal((PetscRealPart(points[p * dim + d]) - lower[d]) / h[d]);
863 
864       if (dbox == n[d] && PetscAbsReal(PetscRealPart(points[p * dim + d]) - upper[d]) < 1.0e-9) dbox = n[d] - 1;
865       if (dbox < 0 || dbox >= n[d]) PetscFunctionReturn(PETSC_SUCCESS);
866       dboxes[p * dim + d] = dbox;
867     }
868     if (boxes)
869       for (d = dim - 2, boxes[p] = dboxes[p * dim + dim - 1]; d >= 0; --d) boxes[p] = boxes[p] * n[d] + dboxes[p * dim + d];
870     // It is possible for a box to overlap no grid cells
871     if (boxes[p] < bStart || boxes[p] >= bEnd) PetscFunctionReturn(PETSC_SUCCESS);
872   }
873   *found = PETSC_TRUE;
874   PetscFunctionReturn(PETSC_SUCCESS);
875 }
876 
877 PetscErrorCode PetscGridHashDestroy(PetscGridHash *box)
878 {
879   PetscFunctionBegin;
880   if (*box) {
881     PetscCall(PetscSectionDestroy(&(*box)->cellSection));
882     PetscCall(ISDestroy(&(*box)->cells));
883     PetscCall(DMLabelDestroy(&(*box)->cellsSparse));
884   }
885   PetscCall(PetscFree(*box));
886   PetscFunctionReturn(PETSC_SUCCESS);
887 }
888 
889 PetscErrorCode DMPlexLocatePoint_Internal(DM dm, PetscInt dim, const PetscScalar point[], PetscInt cellStart, PetscInt *cell)
890 {
891   DMPolytopeType ct;
892 
893   PetscFunctionBegin;
894   PetscCall(DMPlexGetCellType(dm, cellStart, &ct));
895   switch (ct) {
896   case DM_POLYTOPE_SEGMENT:
897     PetscCall(DMPlexLocatePoint_Simplex_1D_Internal(dm, point, cellStart, cell));
898     break;
899   case DM_POLYTOPE_TRIANGLE:
900     PetscCall(DMPlexLocatePoint_Simplex_2D_Internal(dm, point, cellStart, cell));
901     break;
902   case DM_POLYTOPE_QUADRILATERAL:
903     PetscCall(DMPlexLocatePoint_Quad_2D_Internal(dm, point, cellStart, cell));
904     break;
905   case DM_POLYTOPE_TETRAHEDRON:
906     PetscCall(DMPlexLocatePoint_Simplex_3D_Internal(dm, point, cellStart, cell));
907     break;
908   case DM_POLYTOPE_HEXAHEDRON:
909     PetscCall(DMPlexLocatePoint_Hex_3D_Internal(dm, point, cellStart, cell));
910     break;
911   default:
912     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No point location for cell %" PetscInt_FMT " with type %s", cellStart, DMPolytopeTypes[ct]);
913   }
914   PetscFunctionReturn(PETSC_SUCCESS);
915 }
916 
917 /*
918   DMPlexClosestPoint_Internal - Returns the closest point in the cell to the given point
919 */
920 static PetscErrorCode DMPlexClosestPoint_Internal(DM dm, PetscInt dim, const PetscScalar point[], PetscInt cell, PetscReal cpoint[])
921 {
922   DMPolytopeType ct;
923 
924   PetscFunctionBegin;
925   PetscCall(DMPlexGetCellType(dm, cell, &ct));
926   switch (ct) {
927   case DM_POLYTOPE_TRIANGLE:
928     PetscCall(DMPlexClosestPoint_Simplex_2D_Internal(dm, point, cell, cpoint));
929     break;
930 #if 0
931     case DM_POLYTOPE_QUADRILATERAL:
932     PetscCall(DMPlexClosestPoint_General_2D_Internal(dm, point, cell, cpoint));break;
933     case DM_POLYTOPE_TETRAHEDRON:
934     PetscCall(DMPlexClosestPoint_Simplex_3D_Internal(dm, point, cell, cpoint));break;
935     case DM_POLYTOPE_HEXAHEDRON:
936     PetscCall(DMPlexClosestPoint_General_3D_Internal(dm, point, cell, cpoint));break;
937 #endif
938   default:
939     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No closest point location for cell %" PetscInt_FMT " with type %s", cell, DMPolytopeTypes[ct]);
940   }
941   PetscFunctionReturn(PETSC_SUCCESS);
942 }
943 
944 /*
945   DMPlexComputeGridHash_Internal - Create a grid hash structure covering the `DMPLEX`
946 
947   Collective
948 
949   Input Parameter:
950 . dm - The `DMPLEX`
951 
952   Output Parameter:
953 . localBox - The grid hash object
954 
955   Level: developer
956 
957   Notes:
958   How do we determine all boxes intersecting a given cell?
959 
960   1) Get convex body enclosing cell. We will use a box called the box-hull.
961 
962   2) Get smallest brick of boxes enclosing the box-hull
963 
964   3) Each box is composed of 6 planes, 3 lower and 3 upper. We loop over dimensions, and
965      for each new plane determine whether the cell is on the negative side, positive side, or intersects it.
966 
967      a) If the cell is on the negative side of the lower planes, it is not in the box
968 
969      b) If the cell is on the positive side of the upper planes, it is not in the box
970 
971      c) If there is no intersection, it is in the box
972 
973      d) If any intersection point is within the box limits, it is in the box
974 
975 .seealso: `DMPLEX`, `PetscGridHashCreate()`, `PetscGridHashGetEnclosingBox()`
976 */
977 static PetscErrorCode DMPlexComputeGridHash_Internal(DM dm, PetscGridHash *localBox)
978 {
979   PetscInt        debug = ((DM_Plex *)dm->data)->printLocate;
980   PetscGridHash   lbox;
981   PetscSF         sf;
982   const PetscInt *leaves;
983   PetscInt       *dboxes, *boxes;
984   PetscInt        cdim, cStart, cEnd, Nl = -1;
985   PetscBool       flg;
986 
987   PetscFunctionBegin;
988   PetscCall(DMGetCoordinateDim(dm, &cdim));
989   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
990   PetscCall(DMPlexCreateGridHash(dm, &lbox));
991   {
992     PetscInt n[3], d;
993 
994     PetscCall(PetscOptionsGetIntArray(NULL, ((PetscObject)dm)->prefix, "-dm_plex_hash_box_faces", n, &d, &flg));
995     if (flg) {
996       for (PetscInt i = d; i < cdim; ++i) n[i] = n[d - 1];
997     } else {
998       for (PetscInt i = 0; i < cdim; ++i) n[i] = PetscMax(2, PetscFloorReal(PetscPowReal((PetscReal)(cEnd - cStart), 1.0 / cdim) * 0.8));
999     }
1000     PetscCall(PetscGridHashSetGrid(lbox, n, NULL));
1001     if (debug)
1002       PetscCall(PetscPrintf(PETSC_COMM_SELF, "GridHash:\n  (%g, %g, %g) -- (%g, %g, %g)\n  n %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT "\n  h %g %g %g\n", (double)lbox->lower[0], (double)lbox->lower[1], cdim > 2 ? (double)lbox->lower[2] : 0.,
1003                             (double)lbox->upper[0], (double)lbox->upper[1], cdim > 2 ? (double)lbox->upper[2] : 0, n[0], n[1], cdim > 2 ? n[2] : 0, (double)lbox->h[0], (double)lbox->h[1], cdim > 2 ? (double)lbox->h[2] : 0.));
1004   }
1005 
1006   PetscCall(DMGetPointSF(dm, &sf));
1007   if (sf) PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
1008   Nl = PetscMax(Nl, 0);
1009   PetscCall(PetscCalloc2(16 * cdim, &dboxes, 16, &boxes));
1010 
1011   PetscCall(DMLabelCreate(PETSC_COMM_SELF, "cells", &lbox->cellsSparse));
1012   PetscCall(DMLabelCreateIndex(lbox->cellsSparse, cStart, cEnd));
1013   for (PetscInt c = cStart; c < cEnd; ++c) {
1014     PetscReal          intPoints[6 * 6 * 6 * 3];
1015     const PetscScalar *array;
1016     PetscScalar       *coords            = NULL;
1017     const PetscReal   *h                 = lbox->h;
1018     PetscReal          normal[9]         = {1., 0., 0., 0., 1., 0., 0., 0., 1.};
1019     PetscReal         *lowerIntPoints[3] = {&intPoints[0 * 6 * 6 * 3], &intPoints[1 * 6 * 6 * 3], &intPoints[2 * 6 * 6 * 3]};
1020     PetscReal         *upperIntPoints[3] = {&intPoints[3 * 6 * 6 * 3], &intPoints[4 * 6 * 6 * 3], &intPoints[5 * 6 * 6 * 3]};
1021     PetscReal          lp[3], up[3], *tmp;
1022     PetscInt           numCoords, idx, dlim[6], lowerInt[3], upperInt[3];
1023     PetscBool          isDG, lower[3], upper[3];
1024 
1025     PetscCall(PetscFindInt(c, Nl, leaves, &idx));
1026     if (idx >= 0) continue;
1027     // Get grid of boxes containing the cell
1028     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
1029     PetscCall(PetscGridHashGetEnclosingBox(lbox, numCoords / cdim, coords, dboxes, boxes));
1030     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords));
1031     for (PetscInt d = 0; d < cdim; ++d) dlim[d * 2 + 0] = dlim[d * 2 + 1] = dboxes[d];
1032     for (PetscInt d = cdim; d < 3; ++d) dlim[d * 2 + 0] = dlim[d * 2 + 1] = 0;
1033     for (PetscInt e = 1; e < numCoords / cdim; ++e) {
1034       for (PetscInt d = 0; d < cdim; ++d) {
1035         dlim[d * 2 + 0] = PetscMin(dlim[d * 2 + 0], dboxes[e * cdim + d]);
1036         dlim[d * 2 + 1] = PetscMax(dlim[d * 2 + 1], dboxes[e * cdim + d]);
1037       }
1038     }
1039     if (debug > 4) {
1040       for (PetscInt d = 0; d < cdim; ++d) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " direction %" PetscInt_FMT " box limits %" PetscInt_FMT "--%" PetscInt_FMT "\n", c, d, dlim[d * 2 + 0], dlim[d * 2 + 1]));
1041     }
1042     // Initialize with lower planes for first box
1043     for (PetscInt d = 0; d < cdim; ++d) {
1044       lp[d] = lbox->lower[d] + dlim[d * 2 + 0] * h[d];
1045       up[d] = lp[d] + h[d];
1046     }
1047     for (PetscInt d = 0; d < cdim; ++d) {
1048       PetscCall(DMPlexGetPlaneCellIntersection_Internal(dm, c, lp, &normal[d * 3], &lower[d], &lowerInt[d], lowerIntPoints[d]));
1049       if (debug > 4) {
1050         if (!lowerInt[d])
1051           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " lower direction %" PetscInt_FMT " (%g, %g, %g) does not intersect %s\n", c, d, (double)lp[0], (double)lp[1], cdim > 2 ? (double)lp[2] : 0., lower[d] ? "positive" : "negative"));
1052         else PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " lower direction %" PetscInt_FMT " (%g, %g, %g) intersects %" PetscInt_FMT " times\n", c, d, (double)lp[0], (double)lp[1], cdim > 2 ? (double)lp[2] : 0., lowerInt[d]));
1053       }
1054     }
1055     // Loop over grid
1056     for (PetscInt k = dlim[2 * 2 + 0]; k <= dlim[2 * 2 + 1]; ++k, lp[2] = up[2], up[2] += h[2]) {
1057       if (cdim > 2) PetscCall(DMPlexGetPlaneCellIntersection_Internal(dm, c, up, &normal[3 * 2], &upper[2], &upperInt[2], upperIntPoints[2]));
1058       if (cdim > 2 && debug > 4) {
1059         if (!upperInt[2]) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " upper direction 2 (%g, %g, %g) does not intersect %s\n", c, (double)up[0], (double)up[1], cdim > 2 ? (double)up[2] : 0., upper[2] ? "positive" : "negative"));
1060         else PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " upper direction 2 (%g, %g, %g) intersects %" PetscInt_FMT " times\n", c, (double)up[0], (double)up[1], cdim > 2 ? (double)up[2] : 0., upperInt[2]));
1061       }
1062       for (PetscInt j = dlim[1 * 2 + 0]; j <= dlim[1 * 2 + 1]; ++j, lp[1] = up[1], up[1] += h[1]) {
1063         if (cdim > 1) PetscCall(DMPlexGetPlaneCellIntersection_Internal(dm, c, up, &normal[3 * 1], &upper[1], &upperInt[1], upperIntPoints[1]));
1064         if (cdim > 1 && debug > 4) {
1065           if (!upperInt[1])
1066             PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " upper direction 1 (%g, %g, %g) does not intersect %s\n", c, (double)up[0], (double)up[1], cdim > 2 ? (double)up[2] : 0., upper[1] ? "positive" : "negative"));
1067           else PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " upper direction 1 (%g, %g, %g) intersects %" PetscInt_FMT " times\n", c, (double)up[0], (double)up[1], cdim > 2 ? (double)up[2] : 0., upperInt[1]));
1068         }
1069         for (PetscInt i = dlim[0 * 2 + 0]; i <= dlim[0 * 2 + 1]; ++i, lp[0] = up[0], up[0] += h[0]) {
1070           const PetscInt box    = (k * lbox->n[1] + j) * lbox->n[0] + i;
1071           PetscBool      excNeg = PETSC_TRUE;
1072           PetscBool      excPos = PETSC_TRUE;
1073           PetscInt       NlInt  = 0;
1074           PetscInt       NuInt  = 0;
1075 
1076           PetscCall(DMPlexGetPlaneCellIntersection_Internal(dm, c, up, &normal[3 * 0], &upper[0], &upperInt[0], upperIntPoints[0]));
1077           if (debug > 4) {
1078             if (!upperInt[0])
1079               PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " upper direction 0 (%g, %g, %g) does not intersect %s\n", c, (double)up[0], (double)up[1], cdim > 2 ? (double)up[2] : 0., upper[0] ? "positive" : "negative"));
1080             else PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " upper direction 0 (%g, %g, %g) intersects %" PetscInt_FMT " times\n", c, (double)up[0], (double)up[1], cdim > 2 ? (double)up[2] : 0., upperInt[0]));
1081           }
1082           for (PetscInt d = 0; d < cdim; ++d) {
1083             NlInt += lowerInt[d];
1084             NuInt += upperInt[d];
1085           }
1086           // If there is no intersection...
1087           if (!NlInt && !NuInt) {
1088             // If the cell is on the negative side of the lower planes, it is not in the box
1089             for (PetscInt d = 0; d < cdim; ++d)
1090               if (lower[d]) {
1091                 excNeg = PETSC_FALSE;
1092                 break;
1093               }
1094             // If the cell is on the positive side of the upper planes, it is not in the box
1095             for (PetscInt d = 0; d < cdim; ++d)
1096               if (!upper[d]) {
1097                 excPos = PETSC_FALSE;
1098                 break;
1099               }
1100             if (excNeg || excPos) {
1101               if (debug && excNeg) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " is on the negative side of the lower plane\n", c));
1102               if (debug && excPos) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " is on the positive side of the upper plane\n", c));
1103               continue;
1104             }
1105             // Otherwise it is in the box
1106             if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " is contained in box %" PetscInt_FMT "\n", c, box));
1107             PetscCall(DMLabelSetValue(lbox->cellsSparse, c, box));
1108             continue;
1109           }
1110           /*
1111             If any intersection point is within the box limits, it is in the box
1112             We need to have tolerances here since intersection point calculations can introduce errors
1113             Initialize a count to track which planes have intersection outside the box.
1114             if two adjacent planes have intersection points upper and lower all outside the box, look
1115             first at if another plane has intersection points outside the box, if so, it is inside the cell
1116             look next if no intersection points exist on the other planes, and check if the planes are on the
1117             outside of the intersection points but on opposite ends. If so, the box cuts through the cell.
1118           */
1119           PetscInt outsideCount[6] = {0, 0, 0, 0, 0, 0};
1120           for (PetscInt plane = 0; plane < cdim; ++plane) {
1121             for (PetscInt ip = 0; ip < lowerInt[plane]; ++ip) {
1122               PetscInt d;
1123 
1124               for (d = 0; d < cdim; ++d) {
1125                 if ((lowerIntPoints[plane][ip * cdim + d] < (lp[d] - PETSC_SMALL)) || (lowerIntPoints[plane][ip * cdim + d] > (up[d] + PETSC_SMALL))) {
1126                   if (lowerIntPoints[plane][ip * cdim + d] < (lp[d] - PETSC_SMALL)) outsideCount[d]++; // The lower point is to the left of this box, and we count it
1127                   break;
1128                 }
1129               }
1130               if (d == cdim) {
1131                 if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " intersected lower plane %" PetscInt_FMT " of box %" PetscInt_FMT "\n", c, plane, box));
1132                 PetscCall(DMLabelSetValue(lbox->cellsSparse, c, box));
1133                 goto end;
1134               }
1135             }
1136             for (PetscInt ip = 0; ip < upperInt[plane]; ++ip) {
1137               PetscInt d;
1138 
1139               for (d = 0; d < cdim; ++d) {
1140                 if ((upperIntPoints[plane][ip * cdim + d] < (lp[d] - PETSC_SMALL)) || (upperIntPoints[plane][ip * cdim + d] > (up[d] + PETSC_SMALL))) {
1141                   if (upperIntPoints[plane][ip * cdim + d] > (up[d] + PETSC_SMALL)) outsideCount[cdim + d]++; // The upper point is to the right of this box, and we count it
1142                   break;
1143                 }
1144               }
1145               if (d == cdim) {
1146                 if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Cell %" PetscInt_FMT " intersected upper plane %" PetscInt_FMT " of box %" PetscInt_FMT "\n", c, plane, box));
1147                 PetscCall(DMLabelSetValue(lbox->cellsSparse, c, box));
1148                 goto end;
1149               }
1150             }
1151           }
1152           /*
1153              Check the planes with intersections
1154              in 2D, check if the square falls in the middle of a cell
1155              ie all four planes have intersection points outside of the box
1156              You do not want to be doing this, because it means your grid hashing is finer than your grid,
1157              but we should still support it I guess
1158           */
1159           if (cdim == 2) {
1160             PetscInt nIntersects = 0;
1161             for (PetscInt d = 0; d < cdim; ++d) nIntersects += (outsideCount[d] + outsideCount[d + cdim]);
1162             // if the count adds up to 8, that means each plane has 2 external intersections and thus it is in the cell
1163             if (nIntersects == 8) {
1164               PetscCall(DMLabelSetValue(lbox->cellsSparse, c, box));
1165               goto end;
1166             }
1167           }
1168           /*
1169              In 3 dimensions, if two adjacent planes have at least 3 intersections outside the cell in the appropriate direction,
1170              we then check the 3rd planar dimension. If a plane falls between intersection points, the cell belongs to that box.
1171              If the planes are on opposite sides of the intersection points, the cell belongs to that box and it passes through the cell.
1172           */
1173           if (cdim == 3) {
1174             PetscInt faces[3] = {0, 0, 0}, checkInternalFace = 0;
1175             // Find two adjacent planes with at least 3 intersection points in the upper and lower
1176             // if the third plane has 3 intersection points or more, a pyramid base is formed on that plane and it is in the cell
1177             for (PetscInt d = 0; d < cdim; ++d)
1178               if (outsideCount[d] >= 3 && outsideCount[cdim + d] >= 3) {
1179                 faces[d]++;
1180                 checkInternalFace++;
1181               }
1182             if (checkInternalFace == 3) {
1183               // All planes have 3 intersection points, add it.
1184               PetscCall(DMLabelSetValue(lbox->cellsSparse, c, box));
1185               goto end;
1186             }
1187             // Gross, figure out which adjacent faces have at least 3 points
1188             PetscInt nonIntersectingFace = -1;
1189             if (faces[0] == faces[1]) nonIntersectingFace = 2;
1190             if (faces[0] == faces[2]) nonIntersectingFace = 1;
1191             if (faces[1] == faces[2]) nonIntersectingFace = 0;
1192             if (nonIntersectingFace >= 0) {
1193               for (PetscInt plane = 0; plane < cdim; ++plane) {
1194                 if (!lowerInt[nonIntersectingFace] && !upperInt[nonIntersectingFace]) continue;
1195                 // If we have 2 adjacent sides with pyramids of intersection outside of them, and there is a point between the end caps at all, it must be between the two non intersecting ends, and the box is inside the cell.
1196                 for (PetscInt ip = 0; ip < lowerInt[nonIntersectingFace]; ++ip) {
1197                   if (lowerIntPoints[plane][ip * cdim + nonIntersectingFace] > lp[nonIntersectingFace] - PETSC_SMALL || lowerIntPoints[plane][ip * cdim + nonIntersectingFace] < up[nonIntersectingFace] + PETSC_SMALL) goto setpoint;
1198                 }
1199                 for (PetscInt ip = 0; ip < upperInt[nonIntersectingFace]; ++ip) {
1200                   if (upperIntPoints[plane][ip * cdim + nonIntersectingFace] > lp[nonIntersectingFace] - PETSC_SMALL || upperIntPoints[plane][ip * cdim + nonIntersectingFace] < up[nonIntersectingFace] + PETSC_SMALL) goto setpoint;
1201                 }
1202                 goto end;
1203               }
1204               // The points are within the bonds of the non intersecting planes, add it.
1205             setpoint:
1206               PetscCall(DMLabelSetValue(lbox->cellsSparse, c, box));
1207               goto end;
1208             }
1209           }
1210         end:
1211           lower[0]          = upper[0];
1212           lowerInt[0]       = upperInt[0];
1213           tmp               = lowerIntPoints[0];
1214           lowerIntPoints[0] = upperIntPoints[0];
1215           upperIntPoints[0] = tmp;
1216         }
1217         lp[0]             = lbox->lower[0] + dlim[0 * 2 + 0] * h[0];
1218         up[0]             = lp[0] + h[0];
1219         lower[1]          = upper[1];
1220         lowerInt[1]       = upperInt[1];
1221         tmp               = lowerIntPoints[1];
1222         lowerIntPoints[1] = upperIntPoints[1];
1223         upperIntPoints[1] = tmp;
1224       }
1225       lp[1]             = lbox->lower[1] + dlim[1 * 2 + 0] * h[1];
1226       up[1]             = lp[1] + h[1];
1227       lower[2]          = upper[2];
1228       lowerInt[2]       = upperInt[2];
1229       tmp               = lowerIntPoints[2];
1230       lowerIntPoints[2] = upperIntPoints[2];
1231       upperIntPoints[2] = tmp;
1232     }
1233   }
1234   PetscCall(PetscFree2(dboxes, boxes));
1235 
1236   if (debug) PetscCall(DMLabelView(lbox->cellsSparse, PETSC_VIEWER_STDOUT_SELF));
1237   PetscCall(DMLabelConvertToSection(lbox->cellsSparse, &lbox->cellSection, &lbox->cells));
1238   PetscCall(DMLabelDestroy(&lbox->cellsSparse));
1239   *localBox = lbox;
1240   PetscFunctionReturn(PETSC_SUCCESS);
1241 }
1242 
1243 PetscErrorCode DMLocatePoints_Plex(DM dm, Vec v, DMPointLocationType ltype, PetscSF cellSF)
1244 {
1245   PetscInt        debug = ((DM_Plex *)dm->data)->printLocate;
1246   DM_Plex        *mesh  = (DM_Plex *)dm->data;
1247   PetscBool       hash = mesh->useHashLocation, reuse = PETSC_FALSE;
1248   PetscInt        bs, numPoints, p, numFound, *found = NULL;
1249   PetscInt        dim, Nl = 0, cStart, cEnd, numCells, c, d;
1250   PetscSF         sf;
1251   const PetscInt *leaves;
1252   const PetscInt *boxCells;
1253   PetscSFNode    *cells;
1254   PetscScalar    *a;
1255   PetscMPIInt     result;
1256   PetscLogDouble  t0, t1;
1257   PetscReal       gmin[3], gmax[3];
1258   PetscInt        terminating_query_type[] = {0, 0, 0};
1259   PetscMPIInt     rank;
1260 
1261   PetscFunctionBegin;
1262   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1263   PetscCall(PetscLogEventBegin(DMPLEX_LocatePoints, 0, 0, 0, 0));
1264   PetscCall(PetscTime(&t0));
1265   PetscCheck(ltype != DM_POINTLOCATION_NEAREST || hash, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Nearest point location only supported with grid hashing. Use -dm_plex_hash_location to enable it.");
1266   PetscCall(DMGetCoordinateDim(dm, &dim));
1267   PetscCall(VecGetBlockSize(v, &bs));
1268   PetscCallMPI(MPI_Comm_compare(PetscObjectComm((PetscObject)cellSF), PETSC_COMM_SELF, &result));
1269   PetscCheck(result == MPI_IDENT || result == MPI_CONGRUENT, PetscObjectComm((PetscObject)cellSF), PETSC_ERR_SUP, "Trying parallel point location: only local point location supported");
1270   // We ignore extra coordinates
1271   PetscCheck(bs >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Block size for point vector %" PetscInt_FMT " must be the mesh coordinate dimension %" PetscInt_FMT, bs, dim);
1272   PetscCall(DMGetCoordinatesLocalSetUp(dm));
1273   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1274   PetscCall(DMGetPointSF(dm, &sf));
1275   if (sf) PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
1276   Nl = PetscMax(Nl, 0);
1277   PetscCall(VecGetLocalSize(v, &numPoints));
1278   PetscCall(VecGetArray(v, &a));
1279   numPoints /= bs;
1280   {
1281     const PetscSFNode *sf_cells;
1282 
1283     PetscCall(PetscSFGetGraph(cellSF, NULL, NULL, NULL, &sf_cells));
1284     if (sf_cells) {
1285       PetscCall(PetscInfo(dm, "[DMLocatePoints_Plex] Re-using existing StarForest node list\n"));
1286       cells = (PetscSFNode *)sf_cells;
1287       reuse = PETSC_TRUE;
1288     } else {
1289       PetscCall(PetscInfo(dm, "[DMLocatePoints_Plex] Creating and initializing new StarForest node list\n"));
1290       PetscCall(PetscMalloc1(numPoints, &cells));
1291       /* initialize cells if created */
1292       for (p = 0; p < numPoints; p++) {
1293         cells[p].rank  = 0;
1294         cells[p].index = DMLOCATEPOINT_POINT_NOT_FOUND;
1295       }
1296     }
1297   }
1298   PetscCall(DMGetBoundingBox(dm, gmin, gmax));
1299   if (hash) {
1300     if (!mesh->lbox) {
1301       PetscCall(PetscInfo(dm, "Initializing grid hashing\n"));
1302       PetscCall(DMPlexComputeGridHash_Internal(dm, &mesh->lbox));
1303     }
1304     /* Designate the local box for each point */
1305     /* Send points to correct process */
1306     /* Search cells that lie in each subbox */
1307     /*   Should we bin points before doing search? */
1308     PetscCall(ISGetIndices(mesh->lbox->cells, &boxCells));
1309   }
1310   for (p = 0, numFound = 0; p < numPoints; ++p) {
1311     const PetscScalar *point   = &a[p * bs];
1312     PetscInt           dbin[3] = {-1, -1, -1}, bin, cell = -1, cellOffset;
1313     PetscBool          point_outside_domain = PETSC_FALSE;
1314 
1315     /* check bounding box of domain */
1316     for (d = 0; d < dim; d++) {
1317       if (PetscRealPart(point[d]) < gmin[d]) {
1318         point_outside_domain = PETSC_TRUE;
1319         break;
1320       }
1321       if (PetscRealPart(point[d]) > gmax[d]) {
1322         point_outside_domain = PETSC_TRUE;
1323         break;
1324       }
1325     }
1326     if (point_outside_domain) {
1327       cells[p].rank  = 0;
1328       cells[p].index = DMLOCATEPOINT_POINT_NOT_FOUND;
1329       terminating_query_type[0]++;
1330       continue;
1331     }
1332 
1333     /* check initial values in cells[].index - abort early if found */
1334     if (cells[p].index != DMLOCATEPOINT_POINT_NOT_FOUND) {
1335       c              = cells[p].index;
1336       cells[p].index = DMLOCATEPOINT_POINT_NOT_FOUND;
1337       PetscCall(DMPlexLocatePoint_Internal(dm, dim, point, c, &cell));
1338       if (cell >= 0) {
1339         cells[p].rank  = 0;
1340         cells[p].index = cell;
1341         numFound++;
1342       }
1343     }
1344     if (cells[p].index != DMLOCATEPOINT_POINT_NOT_FOUND) {
1345       terminating_query_type[1]++;
1346       continue;
1347     }
1348 
1349     if (hash) {
1350       PetscBool found_box;
1351 
1352       if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "[%d]Checking point %" PetscInt_FMT " (%.2g, %.2g, %.2g)\n", rank, p, (double)PetscRealPart(point[0]), (double)PetscRealPart(point[1]), dim > 2 ? (double)PetscRealPart(point[2]) : 0.));
1353       /* allow for case that point is outside box - abort early */
1354       PetscCall(PetscGridHashGetEnclosingBoxQuery(mesh->lbox, mesh->lbox->cellSection, 1, point, dbin, &bin, &found_box));
1355       if (found_box) {
1356         if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "[%d]  Found point in box %" PetscInt_FMT " (%" PetscInt_FMT ", %" PetscInt_FMT ", %" PetscInt_FMT ")\n", rank, bin, dbin[0], dbin[1], dim > 2 ? dbin[2] : 0));
1357         /* TODO Lay an interface over this so we can switch between Section (dense) and Label (sparse) */
1358         PetscCall(PetscSectionGetDof(mesh->lbox->cellSection, bin, &numCells));
1359         PetscCall(PetscSectionGetOffset(mesh->lbox->cellSection, bin, &cellOffset));
1360         for (c = cellOffset; c < cellOffset + numCells; ++c) {
1361           if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "[%d]    Checking for point in cell %" PetscInt_FMT "\n", rank, boxCells[c]));
1362           PetscCall(DMPlexLocatePoint_Internal(dm, dim, point, boxCells[c], &cell));
1363           if (cell >= 0) {
1364             if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "[%d]      FOUND in cell %" PetscInt_FMT "\n", rank, cell));
1365             cells[p].rank  = 0;
1366             cells[p].index = cell;
1367             numFound++;
1368             terminating_query_type[2]++;
1369             break;
1370           }
1371         }
1372       }
1373     } else {
1374       PetscBool found = PETSC_FALSE;
1375       for (c = cStart; c < cEnd; ++c) {
1376         PetscInt idx;
1377 
1378         PetscCall(PetscFindInt(c, Nl, leaves, &idx));
1379         if (idx >= 0) continue;
1380         PetscCall(DMPlexLocatePoint_Internal(dm, dim, point, c, &cell));
1381         if (cell >= 0) {
1382           cells[p].rank  = 0;
1383           cells[p].index = cell;
1384           numFound++;
1385           terminating_query_type[2]++;
1386           found = PETSC_TRUE;
1387           break;
1388         }
1389       }
1390       if (!found) terminating_query_type[0]++;
1391     }
1392   }
1393   if (hash) PetscCall(ISRestoreIndices(mesh->lbox->cells, &boxCells));
1394   if (ltype == DM_POINTLOCATION_NEAREST && hash && numFound < numPoints) {
1395     for (p = 0; p < numPoints; p++) {
1396       const PetscScalar *point     = &a[p * bs];
1397       PetscReal          cpoint[3] = {0, 0, 0}, diff[3], best[3] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MAX_REAL}, dist, distMax = PETSC_MAX_REAL;
1398       PetscInt           dbin[3] = {-1, -1, -1}, bin, cellOffset, d, bestc = -1;
1399 
1400       if (cells[p].index < 0) {
1401         PetscCall(PetscGridHashGetEnclosingBox(mesh->lbox, 1, point, dbin, &bin));
1402         PetscCall(PetscSectionGetDof(mesh->lbox->cellSection, bin, &numCells));
1403         PetscCall(PetscSectionGetOffset(mesh->lbox->cellSection, bin, &cellOffset));
1404         for (c = cellOffset; c < cellOffset + numCells; ++c) {
1405           PetscCall(DMPlexClosestPoint_Internal(dm, dim, point, boxCells[c], cpoint));
1406           for (d = 0; d < dim; ++d) diff[d] = cpoint[d] - PetscRealPart(point[d]);
1407           dist = DMPlex_NormD_Internal(dim, diff);
1408           if (dist < distMax) {
1409             for (d = 0; d < dim; ++d) best[d] = cpoint[d];
1410             bestc   = boxCells[c];
1411             distMax = dist;
1412           }
1413         }
1414         if (distMax < PETSC_MAX_REAL) {
1415           ++numFound;
1416           cells[p].rank  = 0;
1417           cells[p].index = bestc;
1418           for (d = 0; d < dim; ++d) a[p * bs + d] = best[d];
1419         }
1420       }
1421     }
1422   }
1423   /* This code is only be relevant when interfaced to parallel point location */
1424   /* Check for highest numbered proc that claims a point (do we care?) */
1425   if (ltype == DM_POINTLOCATION_REMOVE && numFound < numPoints) {
1426     PetscCall(PetscMalloc1(numFound, &found));
1427     for (p = 0, numFound = 0; p < numPoints; p++) {
1428       if (cells[p].rank >= 0 && cells[p].index >= 0) {
1429         if (numFound < p) cells[numFound] = cells[p];
1430         found[numFound++] = p;
1431       }
1432     }
1433   }
1434   PetscCall(VecRestoreArray(v, &a));
1435   if (!reuse) PetscCall(PetscSFSetGraph(cellSF, cEnd - cStart, numFound, found, PETSC_OWN_POINTER, cells, PETSC_OWN_POINTER));
1436   PetscCall(PetscTime(&t1));
1437   if (hash) {
1438     PetscCall(PetscInfo(dm, "[DMLocatePoints_Plex] terminating_query_type : %" PetscInt_FMT " [outside domain] : %" PetscInt_FMT " [inside initial cell] : %" PetscInt_FMT " [hash]\n", terminating_query_type[0], terminating_query_type[1], terminating_query_type[2]));
1439   } else {
1440     PetscCall(PetscInfo(dm, "[DMLocatePoints_Plex] terminating_query_type : %" PetscInt_FMT " [outside domain] : %" PetscInt_FMT " [inside initial cell] : %" PetscInt_FMT " [brute-force]\n", terminating_query_type[0], terminating_query_type[1], terminating_query_type[2]));
1441   }
1442   PetscCall(PetscInfo(dm, "[DMLocatePoints_Plex] npoints %" PetscInt_FMT " : time(rank0) %1.2e (sec): points/sec %1.4e\n", numPoints, t1 - t0, numPoints / (t1 - t0)));
1443   PetscCall(PetscLogEventEnd(DMPLEX_LocatePoints, 0, 0, 0, 0));
1444   PetscFunctionReturn(PETSC_SUCCESS);
1445 }
1446 
1447 /*@
1448   DMPlexComputeProjection2Dto1D - Rewrite coordinates to be the 1D projection of the 2D coordinates
1449 
1450   Not Collective
1451 
1452   Input/Output Parameter:
1453 . coords - The coordinates of a segment, on output the new y-coordinate, and 0 for x, an array of size 4, last two entries are unchanged
1454 
1455   Output Parameter:
1456 . R - The rotation which accomplishes the projection, array of size 4
1457 
1458   Level: developer
1459 
1460 .seealso: `DMPLEX`, `DMPlexComputeProjection3Dto1D()`, `DMPlexComputeProjection3Dto2D()`
1461 @*/
1462 PetscErrorCode DMPlexComputeProjection2Dto1D(PetscScalar coords[], PetscReal R[])
1463 {
1464   const PetscReal x = PetscRealPart(coords[2] - coords[0]);
1465   const PetscReal y = PetscRealPart(coords[3] - coords[1]);
1466   const PetscReal r = PetscSqrtReal(x * x + y * y), c = x / r, s = y / r;
1467 
1468   PetscFunctionBegin;
1469   R[0]      = c;
1470   R[1]      = -s;
1471   R[2]      = s;
1472   R[3]      = c;
1473   coords[0] = 0.0;
1474   coords[1] = r;
1475   PetscFunctionReturn(PETSC_SUCCESS);
1476 }
1477 
1478 /*@
1479   DMPlexComputeProjection3Dto1D - Rewrite coordinates to be the 1D projection of the 3D coordinates
1480 
1481   Not Collective
1482 
1483   Input/Output Parameter:
1484 . coords - The coordinates of a segment; on output, the new y-coordinate, and 0 for x and z, an array of size 6, the other entries are unchanged
1485 
1486   Output Parameter:
1487 . R - The rotation which accomplishes the projection, an array of size 9
1488 
1489   Level: developer
1490 
1491   Note:
1492   This uses the basis completion described by Frisvad {cite}`frisvad2012building`
1493 
1494 .seealso: `DMPLEX`, `DMPlexComputeProjection2Dto1D()`, `DMPlexComputeProjection3Dto2D()`
1495 @*/
1496 PetscErrorCode DMPlexComputeProjection3Dto1D(PetscScalar coords[], PetscReal R[])
1497 {
1498   PetscReal x    = PetscRealPart(coords[3] - coords[0]);
1499   PetscReal y    = PetscRealPart(coords[4] - coords[1]);
1500   PetscReal z    = PetscRealPart(coords[5] - coords[2]);
1501   PetscReal r    = PetscSqrtReal(x * x + y * y + z * z);
1502   PetscReal rinv = 1. / r;
1503 
1504   PetscFunctionBegin;
1505   x *= rinv;
1506   y *= rinv;
1507   z *= rinv;
1508   if (x > 0.) {
1509     PetscReal inv1pX = 1. / (1. + x);
1510 
1511     R[0] = x;
1512     R[1] = -y;
1513     R[2] = -z;
1514     R[3] = y;
1515     R[4] = 1. - y * y * inv1pX;
1516     R[5] = -y * z * inv1pX;
1517     R[6] = z;
1518     R[7] = -y * z * inv1pX;
1519     R[8] = 1. - z * z * inv1pX;
1520   } else {
1521     PetscReal inv1mX = 1. / (1. - x);
1522 
1523     R[0] = x;
1524     R[1] = z;
1525     R[2] = y;
1526     R[3] = y;
1527     R[4] = -y * z * inv1mX;
1528     R[5] = 1. - y * y * inv1mX;
1529     R[6] = z;
1530     R[7] = 1. - z * z * inv1mX;
1531     R[8] = -y * z * inv1mX;
1532   }
1533   coords[0] = 0.0;
1534   coords[1] = r;
1535   coords[2] = 0.0;
1536   PetscFunctionReturn(PETSC_SUCCESS);
1537 }
1538 
1539 /*@
1540   DMPlexComputeProjection3Dto2D - Rewrite coordinates of 3 or more coplanar 3D points to a common 2D basis for the
1541   plane.  The normal is defined by positive orientation of the first 3 points.
1542 
1543   Not Collective
1544 
1545   Input Parameter:
1546 . coordSize - Length of coordinate array (3x number of points); must be at least 9 (3 points)
1547 
1548   Input/Output Parameter:
1549 . coords - The interlaced coordinates of each coplanar 3D point; on output the first
1550            2*coordSize/3 entries contain interlaced 2D points, with the rest undefined
1551 
1552   Output Parameter:
1553 . R - 3x3 row-major rotation matrix whose columns are the tangent basis [t1, t2, n].  Multiplying by R^T transforms from original frame to tangent frame.
1554 
1555   Level: developer
1556 
1557 .seealso: `DMPLEX`, `DMPlexComputeProjection2Dto1D()`, `DMPlexComputeProjection3Dto1D()`
1558 @*/
1559 PetscErrorCode DMPlexComputeProjection3Dto2D(PetscInt coordSize, PetscScalar coords[], PetscReal R[])
1560 {
1561   PetscReal      x1[3], x2[3], n[3], c[3], norm;
1562   const PetscInt dim = 3;
1563   PetscInt       d, p;
1564 
1565   PetscFunctionBegin;
1566   /* 0) Calculate normal vector */
1567   for (d = 0; d < dim; ++d) {
1568     x1[d] = PetscRealPart(coords[1 * dim + d] - coords[0 * dim + d]);
1569     x2[d] = PetscRealPart(coords[2 * dim + d] - coords[0 * dim + d]);
1570   }
1571   // n = x1 \otimes x2
1572   n[0] = x1[1] * x2[2] - x1[2] * x2[1];
1573   n[1] = x1[2] * x2[0] - x1[0] * x2[2];
1574   n[2] = x1[0] * x2[1] - x1[1] * x2[0];
1575   norm = PetscSqrtReal(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
1576   for (d = 0; d < dim; d++) n[d] /= norm;
1577   norm = PetscSqrtReal(x1[0] * x1[0] + x1[1] * x1[1] + x1[2] * x1[2]);
1578   for (d = 0; d < dim; d++) x1[d] /= norm;
1579   // x2 = n \otimes x1
1580   x2[0] = n[1] * x1[2] - n[2] * x1[1];
1581   x2[1] = n[2] * x1[0] - n[0] * x1[2];
1582   x2[2] = n[0] * x1[1] - n[1] * x1[0];
1583   for (d = 0; d < dim; d++) {
1584     R[d * dim + 0] = x1[d];
1585     R[d * dim + 1] = x2[d];
1586     R[d * dim + 2] = n[d];
1587     c[d]           = PetscRealPart(coords[0 * dim + d]);
1588   }
1589   for (p = 0; p < coordSize / dim; p++) {
1590     PetscReal y[3];
1591     for (d = 0; d < dim; d++) y[d] = PetscRealPart(coords[p * dim + d]) - c[d];
1592     for (d = 0; d < 2; d++) coords[p * 2 + d] = R[0 * dim + d] * y[0] + R[1 * dim + d] * y[1] + R[2 * dim + d] * y[2];
1593   }
1594   PetscFunctionReturn(PETSC_SUCCESS);
1595 }
1596 
1597 PETSC_UNUSED static inline void Volume_Triangle_Internal(PetscReal *vol, PetscReal coords[])
1598 {
1599   /* Signed volume is 1/2 the determinant
1600 
1601    |  1  1  1 |
1602    | x0 x1 x2 |
1603    | y0 y1 y2 |
1604 
1605      but if x0,y0 is the origin, we have
1606 
1607    | x1 x2 |
1608    | y1 y2 |
1609   */
1610   const PetscReal x1 = coords[2] - coords[0], y1 = coords[3] - coords[1];
1611   const PetscReal x2 = coords[4] - coords[0], y2 = coords[5] - coords[1];
1612   PetscReal       M[4], detM;
1613   M[0] = x1;
1614   M[1] = x2;
1615   M[2] = y1;
1616   M[3] = y2;
1617   DMPlex_Det2D_Internal(&detM, M);
1618   *vol = 0.5 * detM;
1619   (void)PetscLogFlops(5.0);
1620 }
1621 
1622 PETSC_UNUSED static inline void Volume_Tetrahedron_Internal(PetscReal *vol, PetscReal coords[])
1623 {
1624   /* Signed volume is 1/6th of the determinant
1625 
1626    |  1  1  1  1 |
1627    | x0 x1 x2 x3 |
1628    | y0 y1 y2 y3 |
1629    | z0 z1 z2 z3 |
1630 
1631      but if x0,y0,z0 is the origin, we have
1632 
1633    | x1 x2 x3 |
1634    | y1 y2 y3 |
1635    | z1 z2 z3 |
1636   */
1637   const PetscReal x1 = coords[3] - coords[0], y1 = coords[4] - coords[1], z1 = coords[5] - coords[2];
1638   const PetscReal x2 = coords[6] - coords[0], y2 = coords[7] - coords[1], z2 = coords[8] - coords[2];
1639   const PetscReal x3 = coords[9] - coords[0], y3 = coords[10] - coords[1], z3 = coords[11] - coords[2];
1640   const PetscReal onesixth = ((PetscReal)1. / (PetscReal)6.);
1641   PetscReal       M[9], detM;
1642   M[0] = x1;
1643   M[1] = x2;
1644   M[2] = x3;
1645   M[3] = y1;
1646   M[4] = y2;
1647   M[5] = y3;
1648   M[6] = z1;
1649   M[7] = z2;
1650   M[8] = z3;
1651   DMPlex_Det3D_Internal(&detM, M);
1652   *vol = -onesixth * detM;
1653   (void)PetscLogFlops(10.0);
1654 }
1655 
1656 static inline void Volume_Tetrahedron_Origin_Internal(PetscReal *vol, PetscReal coords[])
1657 {
1658   const PetscReal onesixth = ((PetscReal)1. / (PetscReal)6.);
1659   DMPlex_Det3D_Internal(vol, coords);
1660   *vol *= -onesixth;
1661 }
1662 
1663 static PetscErrorCode DMPlexComputePointGeometry_Internal(DM dm, PetscInt e, PetscReal v0[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1664 {
1665   PetscSection       coordSection;
1666   Vec                coordinates;
1667   const PetscScalar *coords;
1668   PetscInt           dim, d, off;
1669 
1670   PetscFunctionBegin;
1671   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1672   PetscCall(DMGetCoordinateSection(dm, &coordSection));
1673   PetscCall(PetscSectionGetDof(coordSection, e, &dim));
1674   if (!dim) PetscFunctionReturn(PETSC_SUCCESS);
1675   PetscCall(PetscSectionGetOffset(coordSection, e, &off));
1676   PetscCall(VecGetArrayRead(coordinates, &coords));
1677   if (v0) {
1678     for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[off + d]);
1679   }
1680   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1681   *detJ = 1.;
1682   if (J) {
1683     for (d = 0; d < dim * dim; d++) J[d] = 0.;
1684     for (d = 0; d < dim; d++) J[d * dim + d] = 1.;
1685     if (invJ) {
1686       for (d = 0; d < dim * dim; d++) invJ[d] = 0.;
1687       for (d = 0; d < dim; d++) invJ[d * dim + d] = 1.;
1688     }
1689   }
1690   PetscFunctionReturn(PETSC_SUCCESS);
1691 }
1692 
1693 /*@C
1694   DMPlexGetCellCoordinates - Get coordinates for a cell, taking into account periodicity
1695 
1696   Not Collective
1697 
1698   Input Parameters:
1699 + dm   - The `DMPLEX`
1700 - cell - The cell number
1701 
1702   Output Parameters:
1703 + isDG   - Using cellwise coordinates
1704 . Nc     - The number of coordinates
1705 . array  - The coordinate array
1706 - coords - The cell coordinates
1707 
1708   Level: developer
1709 
1710 .seealso: `DMPLEX`, `DMPlexRestoreCellCoordinates()`, `DMGetCoordinatesLocal()`, `DMGetCellCoordinatesLocal()`
1711 @*/
1712 PetscErrorCode DMPlexGetCellCoordinates(DM dm, PetscInt cell, PetscBool *isDG, PetscInt *Nc, const PetscScalar *array[], PetscScalar *coords[])
1713 {
1714   DM                 cdm;
1715   Vec                coordinates;
1716   PetscSection       cs;
1717   const PetscScalar *ccoords;
1718   PetscInt           pStart, pEnd;
1719 
1720   PetscFunctionBeginHot;
1721   *isDG   = PETSC_FALSE;
1722   *Nc     = 0;
1723   *array  = NULL;
1724   *coords = NULL;
1725   /* Check for cellwise coordinates */
1726   PetscCall(DMGetCellCoordinateSection(dm, &cs));
1727   if (!cs) goto cg;
1728   /* Check that the cell exists in the cellwise section */
1729   PetscCall(PetscSectionGetChart(cs, &pStart, &pEnd));
1730   if (cell < pStart || cell >= pEnd) goto cg;
1731   /* Check for cellwise coordinates for this cell */
1732   PetscCall(PetscSectionGetDof(cs, cell, Nc));
1733   if (!*Nc) goto cg;
1734   /* Check for cellwise coordinates */
1735   PetscCall(DMGetCellCoordinatesLocalNoncollective(dm, &coordinates));
1736   if (!coordinates) goto cg;
1737   /* Get cellwise coordinates */
1738   PetscCall(DMGetCellCoordinateDM(dm, &cdm));
1739   PetscCall(VecGetArrayRead(coordinates, array));
1740   PetscCall(DMPlexPointLocalRead(cdm, cell, *array, &ccoords));
1741   PetscCall(DMGetWorkArray(cdm, *Nc, MPIU_SCALAR, coords));
1742   PetscCall(PetscArraycpy(*coords, ccoords, *Nc));
1743   PetscCall(VecRestoreArrayRead(coordinates, array));
1744   *isDG = PETSC_TRUE;
1745   PetscFunctionReturn(PETSC_SUCCESS);
1746 cg:
1747   /* Use continuous coordinates */
1748   PetscCall(DMGetCoordinateDM(dm, &cdm));
1749   PetscCall(DMGetCoordinateSection(dm, &cs));
1750   PetscCall(DMGetCoordinatesLocalNoncollective(dm, &coordinates));
1751   PetscCall(DMPlexVecGetOrientedClosure_Internal(cdm, cs, PETSC_FALSE, coordinates, cell, 0, Nc, coords));
1752   PetscFunctionReturn(PETSC_SUCCESS);
1753 }
1754 
1755 /*@C
1756   DMPlexRestoreCellCoordinates - Get coordinates for a cell, taking into account periodicity
1757 
1758   Not Collective
1759 
1760   Input Parameters:
1761 + dm   - The `DMPLEX`
1762 - cell - The cell number
1763 
1764   Output Parameters:
1765 + isDG   - Using cellwise coordinates
1766 . Nc     - The number of coordinates
1767 . array  - The coordinate array
1768 - coords - The cell coordinates
1769 
1770   Level: developer
1771 
1772 .seealso: `DMPLEX`, `DMPlexGetCellCoordinates()`, `DMGetCoordinatesLocal()`, `DMGetCellCoordinatesLocal()`
1773 @*/
1774 PetscErrorCode DMPlexRestoreCellCoordinates(DM dm, PetscInt cell, PetscBool *isDG, PetscInt *Nc, const PetscScalar *array[], PetscScalar *coords[])
1775 {
1776   DM           cdm;
1777   PetscSection cs;
1778   Vec          coordinates;
1779 
1780   PetscFunctionBeginHot;
1781   if (*isDG) {
1782     PetscCall(DMGetCellCoordinateDM(dm, &cdm));
1783     PetscCall(DMRestoreWorkArray(cdm, *Nc, MPIU_SCALAR, coords));
1784   } else {
1785     PetscCall(DMGetCoordinateDM(dm, &cdm));
1786     PetscCall(DMGetCoordinateSection(dm, &cs));
1787     PetscCall(DMGetCoordinatesLocalNoncollective(dm, &coordinates));
1788     PetscCall(DMPlexVecRestoreClosure(cdm, cs, coordinates, cell, Nc, coords));
1789   }
1790   PetscFunctionReturn(PETSC_SUCCESS);
1791 }
1792 
1793 static PetscErrorCode DMPlexComputeLineGeometry_Internal(DM dm, PetscInt e, PetscReal v0[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1794 {
1795   const PetscScalar *array;
1796   PetscScalar       *coords = NULL;
1797   PetscInt           numCoords, d;
1798   PetscBool          isDG;
1799 
1800   PetscFunctionBegin;
1801   PetscCall(DMPlexGetCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1802   PetscCheck(!invJ || J, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "In order to compute invJ, J must not be NULL");
1803   *detJ = 0.0;
1804   if (numCoords == 6) {
1805     const PetscInt dim = 3;
1806     PetscReal      R[9], J0;
1807 
1808     if (v0) {
1809       for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);
1810     }
1811     PetscCall(DMPlexComputeProjection3Dto1D(coords, R));
1812     if (J) {
1813       J0   = 0.5 * PetscRealPart(coords[1]);
1814       J[0] = R[0] * J0;
1815       J[1] = R[1];
1816       J[2] = R[2];
1817       J[3] = R[3] * J0;
1818       J[4] = R[4];
1819       J[5] = R[5];
1820       J[6] = R[6] * J0;
1821       J[7] = R[7];
1822       J[8] = R[8];
1823       DMPlex_Det3D_Internal(detJ, J);
1824       if (invJ) DMPlex_Invert3D_Internal(invJ, J, *detJ);
1825     }
1826   } else if (numCoords == 4) {
1827     const PetscInt dim = 2;
1828     PetscReal      R[4], J0;
1829 
1830     if (v0) {
1831       for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);
1832     }
1833     PetscCall(DMPlexComputeProjection2Dto1D(coords, R));
1834     if (J) {
1835       J0   = 0.5 * PetscRealPart(coords[1]);
1836       J[0] = R[0] * J0;
1837       J[1] = R[1];
1838       J[2] = R[2] * J0;
1839       J[3] = R[3];
1840       DMPlex_Det2D_Internal(detJ, J);
1841       if (invJ) DMPlex_Invert2D_Internal(invJ, J, *detJ);
1842     }
1843   } else if (numCoords == 2) {
1844     const PetscInt dim = 1;
1845 
1846     if (v0) {
1847       for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);
1848     }
1849     if (J) {
1850       J[0]  = 0.5 * (PetscRealPart(coords[1]) - PetscRealPart(coords[0]));
1851       *detJ = J[0];
1852       PetscCall(PetscLogFlops(2.0));
1853       if (invJ) {
1854         invJ[0] = 1.0 / J[0];
1855         PetscCall(PetscLogFlops(1.0));
1856       }
1857     }
1858   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of coordinates for segment %" PetscInt_FMT " is %" PetscInt_FMT " != 2 or 4 or 6", e, numCoords);
1859   PetscCall(DMPlexRestoreCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1860   PetscFunctionReturn(PETSC_SUCCESS);
1861 }
1862 
1863 static PetscErrorCode DMPlexComputeTriangleGeometry_Internal(DM dm, PetscInt e, PetscReal v0[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1864 {
1865   const PetscScalar *array;
1866   PetscScalar       *coords = NULL;
1867   PetscInt           numCoords, d;
1868   PetscBool          isDG;
1869 
1870   PetscFunctionBegin;
1871   PetscCall(DMPlexGetCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1872   PetscCheck(!invJ || J, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "In order to compute invJ, J must not be NULL");
1873   *detJ = 0.0;
1874   if (numCoords == 9) {
1875     const PetscInt dim = 3;
1876     PetscReal      R[9], J0[9] = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
1877 
1878     if (v0) {
1879       for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);
1880     }
1881     PetscCall(DMPlexComputeProjection3Dto2D(numCoords, coords, R));
1882     if (J) {
1883       const PetscInt pdim = 2;
1884 
1885       for (d = 0; d < pdim; d++) {
1886         for (PetscInt f = 0; f < pdim; f++) J0[d * dim + f] = 0.5 * (PetscRealPart(coords[(f + 1) * pdim + d]) - PetscRealPart(coords[0 * pdim + d]));
1887       }
1888       PetscCall(PetscLogFlops(8.0));
1889       DMPlex_Det3D_Internal(detJ, J0);
1890       for (d = 0; d < dim; d++) {
1891         for (PetscInt f = 0; f < dim; f++) {
1892           J[d * dim + f] = 0.0;
1893           for (PetscInt g = 0; g < dim; g++) J[d * dim + f] += R[d * dim + g] * J0[g * dim + f];
1894         }
1895       }
1896       PetscCall(PetscLogFlops(18.0));
1897     }
1898     if (invJ) DMPlex_Invert3D_Internal(invJ, J, *detJ);
1899   } else if (numCoords == 6) {
1900     const PetscInt dim = 2;
1901 
1902     if (v0) {
1903       for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);
1904     }
1905     if (J) {
1906       for (d = 0; d < dim; d++) {
1907         for (PetscInt f = 0; f < dim; f++) J[d * dim + f] = 0.5 * (PetscRealPart(coords[(f + 1) * dim + d]) - PetscRealPart(coords[0 * dim + d]));
1908       }
1909       PetscCall(PetscLogFlops(8.0));
1910       DMPlex_Det2D_Internal(detJ, J);
1911     }
1912     if (invJ) DMPlex_Invert2D_Internal(invJ, J, *detJ);
1913   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of coordinates for this triangle is %" PetscInt_FMT " != 6 or 9", numCoords);
1914   PetscCall(DMPlexRestoreCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1915   PetscFunctionReturn(PETSC_SUCCESS);
1916 }
1917 
1918 static PetscErrorCode DMPlexComputeRectangleGeometry_Internal(DM dm, PetscInt e, PetscBool isTensor, PetscInt Nq, const PetscReal points[], PetscReal v[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1919 {
1920   const PetscScalar *array;
1921   PetscScalar       *coords = NULL;
1922   PetscInt           numCoords, d;
1923   PetscBool          isDG;
1924 
1925   PetscFunctionBegin;
1926   PetscCall(DMPlexGetCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1927   PetscCheck(!invJ || J, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "In order to compute invJ, J must not be NULL");
1928   if (!Nq) {
1929     PetscInt vorder[4] = {0, 1, 2, 3};
1930 
1931     if (isTensor) {
1932       vorder[2] = 3;
1933       vorder[3] = 2;
1934     }
1935     *detJ = 0.0;
1936     if (numCoords == 12) {
1937       const PetscInt dim = 3;
1938       PetscReal      R[9], J0[9] = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
1939 
1940       if (v) {
1941         for (d = 0; d < dim; d++) v[d] = PetscRealPart(coords[d]);
1942       }
1943       PetscCall(DMPlexComputeProjection3Dto2D(numCoords, coords, R));
1944       if (J) {
1945         const PetscInt pdim = 2;
1946 
1947         for (d = 0; d < pdim; d++) {
1948           J0[d * dim + 0] = 0.5 * (PetscRealPart(coords[vorder[1] * pdim + d]) - PetscRealPart(coords[vorder[0] * pdim + d]));
1949           J0[d * dim + 1] = 0.5 * (PetscRealPart(coords[vorder[2] * pdim + d]) - PetscRealPart(coords[vorder[1] * pdim + d]));
1950         }
1951         PetscCall(PetscLogFlops(8.0));
1952         DMPlex_Det3D_Internal(detJ, J0);
1953         for (d = 0; d < dim; d++) {
1954           for (PetscInt f = 0; f < dim; f++) {
1955             J[d * dim + f] = 0.0;
1956             for (PetscInt g = 0; g < dim; g++) J[d * dim + f] += R[d * dim + g] * J0[g * dim + f];
1957           }
1958         }
1959         PetscCall(PetscLogFlops(18.0));
1960       }
1961       if (invJ) DMPlex_Invert3D_Internal(invJ, J, *detJ);
1962     } else if (numCoords == 8) {
1963       const PetscInt dim = 2;
1964 
1965       if (v) {
1966         for (d = 0; d < dim; d++) v[d] = PetscRealPart(coords[d]);
1967       }
1968       if (J) {
1969         for (d = 0; d < dim; d++) {
1970           J[d * dim + 0] = 0.5 * (PetscRealPart(coords[vorder[1] * dim + d]) - PetscRealPart(coords[vorder[0] * dim + d]));
1971           J[d * dim + 1] = 0.5 * (PetscRealPart(coords[vorder[3] * dim + d]) - PetscRealPart(coords[vorder[0] * dim + d]));
1972         }
1973         PetscCall(PetscLogFlops(8.0));
1974         DMPlex_Det2D_Internal(detJ, J);
1975       }
1976       if (invJ) DMPlex_Invert2D_Internal(invJ, J, *detJ);
1977     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of coordinates for this quadrilateral is %" PetscInt_FMT " != 8 or 12", numCoords);
1978   } else {
1979     const PetscInt Nv         = 4;
1980     const PetscInt dimR       = 2;
1981     PetscInt       zToPlex[4] = {0, 1, 3, 2};
1982     PetscReal      zOrder[12];
1983     PetscReal      zCoeff[12];
1984     PetscInt       i, j, k, l, dim;
1985 
1986     if (isTensor) {
1987       zToPlex[2] = 2;
1988       zToPlex[3] = 3;
1989     }
1990     if (numCoords == 12) {
1991       dim = 3;
1992     } else if (numCoords == 8) {
1993       dim = 2;
1994     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of coordinates for this quadrilateral is %" PetscInt_FMT " != 8 or 12", numCoords);
1995     for (i = 0; i < Nv; i++) {
1996       PetscInt zi = zToPlex[i];
1997 
1998       for (j = 0; j < dim; j++) zOrder[dim * i + j] = PetscRealPart(coords[dim * zi + j]);
1999     }
2000     for (j = 0; j < dim; j++) {
2001       /* Nodal basis for evaluation at the vertices: (1 \mp xi) (1 \mp eta):
2002            \phi^0 = (1 - xi - eta + xi eta) --> 1      = 1/4 ( \phi^0 + \phi^1 + \phi^2 + \phi^3)
2003            \phi^1 = (1 + xi - eta - xi eta) --> xi     = 1/4 (-\phi^0 + \phi^1 - \phi^2 + \phi^3)
2004            \phi^2 = (1 - xi + eta - xi eta) --> eta    = 1/4 (-\phi^0 - \phi^1 + \phi^2 + \phi^3)
2005            \phi^3 = (1 + xi + eta + xi eta) --> xi eta = 1/4 ( \phi^0 - \phi^1 - \phi^2 + \phi^3)
2006       */
2007       zCoeff[dim * 0 + j] = 0.25 * (zOrder[dim * 0 + j] + zOrder[dim * 1 + j] + zOrder[dim * 2 + j] + zOrder[dim * 3 + j]);
2008       zCoeff[dim * 1 + j] = 0.25 * (-zOrder[dim * 0 + j] + zOrder[dim * 1 + j] - zOrder[dim * 2 + j] + zOrder[dim * 3 + j]);
2009       zCoeff[dim * 2 + j] = 0.25 * (-zOrder[dim * 0 + j] - zOrder[dim * 1 + j] + zOrder[dim * 2 + j] + zOrder[dim * 3 + j]);
2010       zCoeff[dim * 3 + j] = 0.25 * (zOrder[dim * 0 + j] - zOrder[dim * 1 + j] - zOrder[dim * 2 + j] + zOrder[dim * 3 + j]);
2011     }
2012     for (i = 0; i < Nq; i++) {
2013       PetscReal xi = points[dimR * i], eta = points[dimR * i + 1];
2014 
2015       if (v) {
2016         PetscReal extPoint[4];
2017 
2018         extPoint[0] = 1.;
2019         extPoint[1] = xi;
2020         extPoint[2] = eta;
2021         extPoint[3] = xi * eta;
2022         for (j = 0; j < dim; j++) {
2023           PetscReal val = 0.;
2024 
2025           for (k = 0; k < Nv; k++) val += extPoint[k] * zCoeff[dim * k + j];
2026           v[i * dim + j] = val;
2027         }
2028       }
2029       if (J) {
2030         PetscReal extJ[8];
2031 
2032         extJ[0] = 0.;
2033         extJ[1] = 0.;
2034         extJ[2] = 1.;
2035         extJ[3] = 0.;
2036         extJ[4] = 0.;
2037         extJ[5] = 1.;
2038         extJ[6] = eta;
2039         extJ[7] = xi;
2040         for (j = 0; j < dim; j++) {
2041           for (k = 0; k < dimR; k++) {
2042             PetscReal val = 0.;
2043 
2044             for (l = 0; l < Nv; l++) val += zCoeff[dim * l + j] * extJ[dimR * l + k];
2045             J[i * dim * dim + dim * j + k] = val;
2046           }
2047         }
2048         if (dim == 3) { /* put the cross product in the third component of the Jacobian */
2049           PetscReal  x, y, z;
2050           PetscReal *iJ = &J[i * dim * dim];
2051           PetscReal  norm;
2052 
2053           x     = iJ[1 * dim + 0] * iJ[2 * dim + 1] - iJ[1 * dim + 1] * iJ[2 * dim + 0];
2054           y     = iJ[0 * dim + 1] * iJ[2 * dim + 0] - iJ[0 * dim + 0] * iJ[2 * dim + 1];
2055           z     = iJ[0 * dim + 0] * iJ[1 * dim + 1] - iJ[0 * dim + 1] * iJ[1 * dim + 0];
2056           norm  = PetscSqrtReal(x * x + y * y + z * z);
2057           iJ[2] = x / norm;
2058           iJ[5] = y / norm;
2059           iJ[8] = z / norm;
2060           DMPlex_Det3D_Internal(&detJ[i], &J[i * dim * dim]);
2061           if (invJ) DMPlex_Invert3D_Internal(&invJ[i * dim * dim], &J[i * dim * dim], detJ[i]);
2062         } else {
2063           DMPlex_Det2D_Internal(&detJ[i], &J[i * dim * dim]);
2064           if (invJ) DMPlex_Invert2D_Internal(&invJ[i * dim * dim], &J[i * dim * dim], detJ[i]);
2065         }
2066       }
2067     }
2068   }
2069   PetscCall(DMPlexRestoreCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
2070   PetscFunctionReturn(PETSC_SUCCESS);
2071 }
2072 
2073 static PetscErrorCode DMPlexComputeTetrahedronGeometry_Internal(DM dm, PetscInt e, PetscReal v0[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
2074 {
2075   const PetscScalar *array;
2076   PetscScalar       *coords = NULL;
2077   const PetscInt     dim    = 3;
2078   PetscInt           numCoords, d;
2079   PetscBool          isDG;
2080 
2081   PetscFunctionBegin;
2082   PetscCall(DMPlexGetCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
2083   PetscCheck(!invJ || J, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "In order to compute invJ, J must not be NULL");
2084   *detJ = 0.0;
2085   if (v0) {
2086     for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);
2087   }
2088   if (J) {
2089     for (d = 0; d < dim; d++) {
2090       /* I orient with outward face normals */
2091       J[d * dim + 0] = 0.5 * (PetscRealPart(coords[2 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
2092       J[d * dim + 1] = 0.5 * (PetscRealPart(coords[1 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
2093       J[d * dim + 2] = 0.5 * (PetscRealPart(coords[3 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
2094     }
2095     PetscCall(PetscLogFlops(18.0));
2096     DMPlex_Det3D_Internal(detJ, J);
2097   }
2098   if (invJ) DMPlex_Invert3D_Internal(invJ, J, *detJ);
2099   PetscCall(DMPlexRestoreCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
2100   PetscFunctionReturn(PETSC_SUCCESS);
2101 }
2102 
2103 static PetscErrorCode DMPlexComputeHexahedronGeometry_Internal(DM dm, PetscInt e, PetscInt Nq, const PetscReal points[], PetscReal v[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
2104 {
2105   const PetscScalar *array;
2106   PetscScalar       *coords = NULL;
2107   const PetscInt     dim    = 3;
2108   PetscInt           numCoords, d;
2109   PetscBool          isDG;
2110 
2111   PetscFunctionBegin;
2112   PetscCall(DMPlexGetCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
2113   PetscCheck(!invJ || J, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "In order to compute invJ, J must not be NULL");
2114   if (!Nq) {
2115     *detJ = 0.0;
2116     if (v) {
2117       for (d = 0; d < dim; d++) v[d] = PetscRealPart(coords[d]);
2118     }
2119     if (J) {
2120       for (d = 0; d < dim; d++) {
2121         J[d * dim + 0] = 0.5 * (PetscRealPart(coords[3 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
2122         J[d * dim + 1] = 0.5 * (PetscRealPart(coords[1 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
2123         J[d * dim + 2] = 0.5 * (PetscRealPart(coords[4 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
2124       }
2125       PetscCall(PetscLogFlops(18.0));
2126       DMPlex_Det3D_Internal(detJ, J);
2127     }
2128     if (invJ) DMPlex_Invert3D_Internal(invJ, J, *detJ);
2129   } else {
2130     const PetscInt Nv         = 8;
2131     const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
2132     const PetscInt dim        = 3;
2133     const PetscInt dimR       = 3;
2134     PetscReal      zOrder[24];
2135     PetscReal      zCoeff[24];
2136     PetscInt       i, j, k, l;
2137 
2138     for (i = 0; i < Nv; i++) {
2139       PetscInt zi = zToPlex[i];
2140 
2141       for (j = 0; j < dim; j++) zOrder[dim * i + j] = PetscRealPart(coords[dim * zi + j]);
2142     }
2143     for (j = 0; j < dim; j++) {
2144       zCoeff[dim * 0 + j] = 0.125 * (zOrder[dim * 0 + j] + zOrder[dim * 1 + j] + zOrder[dim * 2 + j] + zOrder[dim * 3 + j] + zOrder[dim * 4 + j] + zOrder[dim * 5 + j] + zOrder[dim * 6 + j] + zOrder[dim * 7 + j]);
2145       zCoeff[dim * 1 + j] = 0.125 * (-zOrder[dim * 0 + j] + zOrder[dim * 1 + j] - zOrder[dim * 2 + j] + zOrder[dim * 3 + j] - zOrder[dim * 4 + j] + zOrder[dim * 5 + j] - zOrder[dim * 6 + j] + zOrder[dim * 7 + j]);
2146       zCoeff[dim * 2 + j] = 0.125 * (-zOrder[dim * 0 + j] - zOrder[dim * 1 + j] + zOrder[dim * 2 + j] + zOrder[dim * 3 + j] - zOrder[dim * 4 + j] - zOrder[dim * 5 + j] + zOrder[dim * 6 + j] + zOrder[dim * 7 + j]);
2147       zCoeff[dim * 3 + j] = 0.125 * (zOrder[dim * 0 + j] - zOrder[dim * 1 + j] - zOrder[dim * 2 + j] + zOrder[dim * 3 + j] + zOrder[dim * 4 + j] - zOrder[dim * 5 + j] - zOrder[dim * 6 + j] + zOrder[dim * 7 + j]);
2148       zCoeff[dim * 4 + j] = 0.125 * (-zOrder[dim * 0 + j] - zOrder[dim * 1 + j] - zOrder[dim * 2 + j] - zOrder[dim * 3 + j] + zOrder[dim * 4 + j] + zOrder[dim * 5 + j] + zOrder[dim * 6 + j] + zOrder[dim * 7 + j]);
2149       zCoeff[dim * 5 + j] = 0.125 * (+zOrder[dim * 0 + j] - zOrder[dim * 1 + j] + zOrder[dim * 2 + j] - zOrder[dim * 3 + j] - zOrder[dim * 4 + j] + zOrder[dim * 5 + j] - zOrder[dim * 6 + j] + zOrder[dim * 7 + j]);
2150       zCoeff[dim * 6 + j] = 0.125 * (+zOrder[dim * 0 + j] + zOrder[dim * 1 + j] - zOrder[dim * 2 + j] - zOrder[dim * 3 + j] - zOrder[dim * 4 + j] - zOrder[dim * 5 + j] + zOrder[dim * 6 + j] + zOrder[dim * 7 + j]);
2151       zCoeff[dim * 7 + j] = 0.125 * (-zOrder[dim * 0 + j] + zOrder[dim * 1 + j] + zOrder[dim * 2 + j] - zOrder[dim * 3 + j] + zOrder[dim * 4 + j] - zOrder[dim * 5 + j] - zOrder[dim * 6 + j] + zOrder[dim * 7 + j]);
2152     }
2153     for (i = 0; i < Nq; i++) {
2154       PetscReal xi = points[dimR * i], eta = points[dimR * i + 1], theta = points[dimR * i + 2];
2155 
2156       if (v) {
2157         PetscReal extPoint[8];
2158 
2159         extPoint[0] = 1.;
2160         extPoint[1] = xi;
2161         extPoint[2] = eta;
2162         extPoint[3] = xi * eta;
2163         extPoint[4] = theta;
2164         extPoint[5] = theta * xi;
2165         extPoint[6] = theta * eta;
2166         extPoint[7] = theta * eta * xi;
2167         for (j = 0; j < dim; j++) {
2168           PetscReal val = 0.;
2169 
2170           for (k = 0; k < Nv; k++) val += extPoint[k] * zCoeff[dim * k + j];
2171           v[i * dim + j] = val;
2172         }
2173       }
2174       if (J) {
2175         PetscReal extJ[24];
2176 
2177         extJ[0]  = 0.;
2178         extJ[1]  = 0.;
2179         extJ[2]  = 0.;
2180         extJ[3]  = 1.;
2181         extJ[4]  = 0.;
2182         extJ[5]  = 0.;
2183         extJ[6]  = 0.;
2184         extJ[7]  = 1.;
2185         extJ[8]  = 0.;
2186         extJ[9]  = eta;
2187         extJ[10] = xi;
2188         extJ[11] = 0.;
2189         extJ[12] = 0.;
2190         extJ[13] = 0.;
2191         extJ[14] = 1.;
2192         extJ[15] = theta;
2193         extJ[16] = 0.;
2194         extJ[17] = xi;
2195         extJ[18] = 0.;
2196         extJ[19] = theta;
2197         extJ[20] = eta;
2198         extJ[21] = theta * eta;
2199         extJ[22] = theta * xi;
2200         extJ[23] = eta * xi;
2201 
2202         for (j = 0; j < dim; j++) {
2203           for (k = 0; k < dimR; k++) {
2204             PetscReal val = 0.;
2205 
2206             for (l = 0; l < Nv; l++) val += zCoeff[dim * l + j] * extJ[dimR * l + k];
2207             J[i * dim * dim + dim * j + k] = val;
2208           }
2209         }
2210         DMPlex_Det3D_Internal(&detJ[i], &J[i * dim * dim]);
2211         if (invJ) DMPlex_Invert3D_Internal(&invJ[i * dim * dim], &J[i * dim * dim], detJ[i]);
2212       }
2213     }
2214   }
2215   PetscCall(DMPlexRestoreCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
2216   PetscFunctionReturn(PETSC_SUCCESS);
2217 }
2218 
2219 static PetscErrorCode DMPlexComputeTriangularPrismGeometry_Internal(DM dm, PetscInt e, PetscInt Nq, const PetscReal points[], PetscReal v[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
2220 {
2221   const PetscScalar *array;
2222   PetscScalar       *coords = NULL;
2223   const PetscInt     dim    = 3;
2224   PetscInt           numCoords, d;
2225   PetscBool          isDG;
2226 
2227   PetscFunctionBegin;
2228   PetscCall(DMPlexGetCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
2229   PetscCheck(!invJ || J, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "In order to compute invJ, J must not be NULL");
2230   if (!Nq) {
2231     /* Assume that the map to the reference is affine */
2232     *detJ = 0.0;
2233     if (v) {
2234       for (d = 0; d < dim; d++) v[d] = PetscRealPart(coords[d]);
2235     }
2236     if (J) {
2237       for (d = 0; d < dim; d++) {
2238         J[d * dim + 0] = 0.5 * (PetscRealPart(coords[2 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
2239         J[d * dim + 1] = 0.5 * (PetscRealPart(coords[1 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
2240         J[d * dim + 2] = 0.5 * (PetscRealPart(coords[4 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
2241       }
2242       PetscCall(PetscLogFlops(18.0));
2243       DMPlex_Det3D_Internal(detJ, J);
2244     }
2245     if (invJ) DMPlex_Invert3D_Internal(invJ, J, *detJ);
2246   } else {
2247     const PetscInt dim  = 3;
2248     const PetscInt dimR = 3;
2249     const PetscInt Nv   = 6;
2250     PetscReal      verts[18];
2251     PetscReal      coeff[18];
2252     PetscInt       i, j, k, l;
2253 
2254     for (i = 0; i < Nv; ++i)
2255       for (j = 0; j < dim; ++j) verts[dim * i + j] = PetscRealPart(coords[dim * i + j]);
2256     for (j = 0; j < dim; ++j) {
2257       /* Check for triangle,
2258            phi^0 = -1/2 (xi + eta)  chi^0 = delta(-1, -1)   x(xi) = \sum_k x_k phi^k(xi) = \sum_k chi^k(x) phi^k(xi)
2259            phi^1 =  1/2 (1 + xi)    chi^1 = delta( 1, -1)   y(xi) = \sum_k y_k phi^k(xi) = \sum_k chi^k(y) phi^k(xi)
2260            phi^2 =  1/2 (1 + eta)   chi^2 = delta(-1,  1)
2261 
2262            phi^0 + phi^1 + phi^2 = 1    coef_1   = 1/2 (         chi^1 + chi^2)
2263           -phi^0 + phi^1 - phi^2 = xi   coef_xi  = 1/2 (-chi^0 + chi^1)
2264           -phi^0 - phi^1 + phi^2 = eta  coef_eta = 1/2 (-chi^0         + chi^2)
2265 
2266           < chi_0 chi_1 chi_2> A /  1  1  1 \ / phi_0 \   <chi> I <phi>^T  so we need the inverse transpose
2267                                  | -1  1 -1 | | phi_1 | =
2268                                  \ -1 -1  1 / \ phi_2 /
2269 
2270           Check phi^0: 1/2 (phi^0 chi^1 + phi^0 chi^2 + phi^0 chi^0 - phi^0 chi^1 + phi^0 chi^0 - phi^0 chi^2) = phi^0 chi^0
2271       */
2272       /* Nodal basis for evaluation at the vertices: {-xi - eta, 1 + xi, 1 + eta} (1 \mp zeta):
2273            \phi^0 = 1/4 (   -xi - eta        + xi zeta + eta zeta) --> /  1  1  1  1  1  1 \ 1
2274            \phi^1 = 1/4 (1      + eta - zeta           - eta zeta) --> | -1  1 -1 -1 -1  1 | eta
2275            \phi^2 = 1/4 (1 + xi       - zeta - xi zeta)            --> | -1 -1  1 -1  1 -1 | xi
2276            \phi^3 = 1/4 (   -xi - eta        - xi zeta - eta zeta) --> | -1 -1 -1  1  1  1 | zeta
2277            \phi^4 = 1/4 (1 + xi       + zeta + xi zeta)            --> |  1  1 -1 -1  1 -1 | xi zeta
2278            \phi^5 = 1/4 (1      + eta + zeta           + eta zeta) --> \  1 -1  1 -1 -1  1 / eta zeta
2279            1/4 /  0  1  1  0  1  1 \
2280                | -1  1  0 -1  0  1 |
2281                | -1  0  1 -1  1  0 |
2282                |  0 -1 -1  0  1  1 |
2283                |  1  0 -1 -1  1  0 |
2284                \  1 -1  0 -1  0  1 /
2285       */
2286       coeff[dim * 0 + j] = (1. / 4.) * (verts[dim * 1 + j] + verts[dim * 2 + j] + verts[dim * 4 + j] + verts[dim * 5 + j]);
2287       coeff[dim * 1 + j] = (1. / 4.) * (-verts[dim * 0 + j] + verts[dim * 1 + j] - verts[dim * 3 + j] + verts[dim * 5 + j]);
2288       coeff[dim * 2 + j] = (1. / 4.) * (-verts[dim * 0 + j] + verts[dim * 2 + j] - verts[dim * 3 + j] + verts[dim * 4 + j]);
2289       coeff[dim * 3 + j] = (1. / 4.) * (-verts[dim * 1 + j] - verts[dim * 2 + j] + verts[dim * 4 + j] + verts[dim * 5 + j]);
2290       coeff[dim * 4 + j] = (1. / 4.) * (verts[dim * 0 + j] - verts[dim * 2 + j] - verts[dim * 3 + j] + verts[dim * 4 + j]);
2291       coeff[dim * 5 + j] = (1. / 4.) * (verts[dim * 0 + j] - verts[dim * 1 + j] - verts[dim * 3 + j] + verts[dim * 5 + j]);
2292       /* For reference prism:
2293       {0, 0, 0}
2294       {0, 1, 0}
2295       {1, 0, 0}
2296       {0, 0, 1}
2297       {0, 0, 0}
2298       {0, 0, 0}
2299       */
2300     }
2301     for (i = 0; i < Nq; ++i) {
2302       const PetscReal xi = points[dimR * i], eta = points[dimR * i + 1], zeta = points[dimR * i + 2];
2303 
2304       if (v) {
2305         PetscReal extPoint[6];
2306         PetscInt  c;
2307 
2308         extPoint[0] = 1.;
2309         extPoint[1] = eta;
2310         extPoint[2] = xi;
2311         extPoint[3] = zeta;
2312         extPoint[4] = xi * zeta;
2313         extPoint[5] = eta * zeta;
2314         for (c = 0; c < dim; ++c) {
2315           PetscReal val = 0.;
2316 
2317           for (k = 0; k < Nv; ++k) val += extPoint[k] * coeff[k * dim + c];
2318           v[i * dim + c] = val;
2319         }
2320       }
2321       if (J) {
2322         PetscReal extJ[18];
2323 
2324         extJ[0]  = 0.;
2325         extJ[1]  = 0.;
2326         extJ[2]  = 0.;
2327         extJ[3]  = 0.;
2328         extJ[4]  = 1.;
2329         extJ[5]  = 0.;
2330         extJ[6]  = 1.;
2331         extJ[7]  = 0.;
2332         extJ[8]  = 0.;
2333         extJ[9]  = 0.;
2334         extJ[10] = 0.;
2335         extJ[11] = 1.;
2336         extJ[12] = zeta;
2337         extJ[13] = 0.;
2338         extJ[14] = xi;
2339         extJ[15] = 0.;
2340         extJ[16] = zeta;
2341         extJ[17] = eta;
2342 
2343         for (j = 0; j < dim; j++) {
2344           for (k = 0; k < dimR; k++) {
2345             PetscReal val = 0.;
2346 
2347             for (l = 0; l < Nv; l++) val += coeff[dim * l + j] * extJ[dimR * l + k];
2348             J[i * dim * dim + dim * j + k] = val;
2349           }
2350         }
2351         DMPlex_Det3D_Internal(&detJ[i], &J[i * dim * dim]);
2352         if (invJ) DMPlex_Invert3D_Internal(&invJ[i * dim * dim], &J[i * dim * dim], detJ[i]);
2353       }
2354     }
2355   }
2356   PetscCall(DMPlexRestoreCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
2357   PetscFunctionReturn(PETSC_SUCCESS);
2358 }
2359 
2360 static PetscErrorCode DMPlexComputeCellGeometryFEM_Implicit(DM dm, PetscInt cell, PetscQuadrature quad, PetscReal *v, PetscReal *J, PetscReal *invJ, PetscReal *detJ)
2361 {
2362   DMPolytopeType   ct;
2363   PetscInt         depth, dim, coordDim, coneSize, i;
2364   PetscInt         Nq     = 0;
2365   const PetscReal *points = NULL;
2366   DMLabel          depthLabel;
2367   PetscReal        xi0[3]   = {-1., -1., -1.}, v0[3], J0[9], detJ0;
2368   PetscBool        isAffine = PETSC_TRUE;
2369 
2370   PetscFunctionBegin;
2371   PetscCall(DMPlexGetDepth(dm, &depth));
2372   PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
2373   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
2374   PetscCall(DMLabelGetValue(depthLabel, cell, &dim));
2375   if (depth == 1 && dim == 1) PetscCall(DMGetDimension(dm, &dim));
2376   PetscCall(DMGetCoordinateDim(dm, &coordDim));
2377   PetscCheck(coordDim <= 3, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unsupported coordinate dimension %" PetscInt_FMT " > 3", coordDim);
2378   if (quad) PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, &points, NULL));
2379   PetscCall(DMPlexGetCellType(dm, cell, &ct));
2380   switch (ct) {
2381   case DM_POLYTOPE_POINT:
2382     PetscCall(DMPlexComputePointGeometry_Internal(dm, cell, v, J, invJ, detJ));
2383     isAffine = PETSC_FALSE;
2384     break;
2385   case DM_POLYTOPE_SEGMENT:
2386   case DM_POLYTOPE_POINT_PRISM_TENSOR:
2387     if (Nq) PetscCall(DMPlexComputeLineGeometry_Internal(dm, cell, v0, J0, NULL, &detJ0));
2388     else PetscCall(DMPlexComputeLineGeometry_Internal(dm, cell, v, J, invJ, detJ));
2389     break;
2390   case DM_POLYTOPE_TRIANGLE:
2391     if (Nq) PetscCall(DMPlexComputeTriangleGeometry_Internal(dm, cell, v0, J0, NULL, &detJ0));
2392     else PetscCall(DMPlexComputeTriangleGeometry_Internal(dm, cell, v, J, invJ, detJ));
2393     break;
2394   case DM_POLYTOPE_QUADRILATERAL:
2395     PetscCall(DMPlexComputeRectangleGeometry_Internal(dm, cell, PETSC_FALSE, Nq, points, v, J, invJ, detJ));
2396     isAffine = PETSC_FALSE;
2397     break;
2398   case DM_POLYTOPE_SEG_PRISM_TENSOR:
2399     PetscCall(DMPlexComputeRectangleGeometry_Internal(dm, cell, PETSC_TRUE, Nq, points, v, J, invJ, detJ));
2400     isAffine = PETSC_FALSE;
2401     break;
2402   case DM_POLYTOPE_TETRAHEDRON:
2403     if (Nq) PetscCall(DMPlexComputeTetrahedronGeometry_Internal(dm, cell, v0, J0, NULL, &detJ0));
2404     else PetscCall(DMPlexComputeTetrahedronGeometry_Internal(dm, cell, v, J, invJ, detJ));
2405     break;
2406   case DM_POLYTOPE_HEXAHEDRON:
2407     PetscCall(DMPlexComputeHexahedronGeometry_Internal(dm, cell, Nq, points, v, J, invJ, detJ));
2408     isAffine = PETSC_FALSE;
2409     break;
2410   case DM_POLYTOPE_TRI_PRISM:
2411     PetscCall(DMPlexComputeTriangularPrismGeometry_Internal(dm, cell, Nq, points, v, J, invJ, detJ));
2412     isAffine = PETSC_FALSE;
2413     break;
2414   default:
2415     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No element geometry for cell %" PetscInt_FMT " with type %s", cell, DMPolytopeTypes[PetscMax(0, PetscMin(ct, DM_NUM_POLYTOPES))]);
2416   }
2417   if (isAffine && Nq) {
2418     if (v) {
2419       for (i = 0; i < Nq; i++) CoordinatesRefToReal(coordDim, dim, xi0, v0, J0, &points[dim * i], &v[coordDim * i]);
2420     }
2421     if (detJ) {
2422       for (i = 0; i < Nq; i++) detJ[i] = detJ0;
2423     }
2424     if (J) {
2425       PetscInt k;
2426 
2427       for (i = 0, k = 0; i < Nq; i++) {
2428         PetscInt j;
2429 
2430         for (j = 0; j < coordDim * coordDim; j++, k++) J[k] = J0[j];
2431       }
2432     }
2433     if (invJ) {
2434       PetscInt k;
2435       switch (coordDim) {
2436       case 0:
2437         break;
2438       case 1:
2439         invJ[0] = 1. / J0[0];
2440         break;
2441       case 2:
2442         DMPlex_Invert2D_Internal(invJ, J0, detJ0);
2443         break;
2444       case 3:
2445         DMPlex_Invert3D_Internal(invJ, J0, detJ0);
2446         break;
2447       }
2448       for (i = 1, k = coordDim * coordDim; i < Nq; i++) {
2449         PetscInt j;
2450 
2451         for (j = 0; j < coordDim * coordDim; j++, k++) invJ[k] = invJ[j];
2452       }
2453     }
2454   }
2455   PetscFunctionReturn(PETSC_SUCCESS);
2456 }
2457 
2458 /*@C
2459   DMPlexComputeCellGeometryAffineFEM - Assuming an affine map, compute the Jacobian, inverse Jacobian, and Jacobian determinant for a given cell
2460 
2461   Collective
2462 
2463   Input Parameters:
2464 + dm   - the `DMPLEX`
2465 - cell - the cell
2466 
2467   Output Parameters:
2468 + v0   - the translation part of this affine transform, meaning the translation to the origin (not the first vertex of the reference cell)
2469 . J    - the Jacobian of the transform from the reference element
2470 . invJ - the inverse of the Jacobian
2471 - detJ - the Jacobian determinant
2472 
2473   Level: advanced
2474 
2475 .seealso: `DMPLEX`, `DMPlexComputeCellGeometryFEM()`, `DMGetCoordinateSection()`, `DMGetCoordinates()`
2476 @*/
2477 PetscErrorCode DMPlexComputeCellGeometryAffineFEM(DM dm, PetscInt cell, PetscReal *v0, PetscReal *J, PetscReal *invJ, PetscReal *detJ)
2478 {
2479   PetscFunctionBegin;
2480   PetscCall(DMPlexComputeCellGeometryFEM_Implicit(dm, cell, NULL, v0, J, invJ, detJ));
2481   PetscFunctionReturn(PETSC_SUCCESS);
2482 }
2483 
2484 static PetscErrorCode DMPlexComputeCellGeometryFEM_FE(DM dm, PetscFE fe, PetscInt point, PetscQuadrature quad, PetscReal v[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
2485 {
2486   const PetscScalar *array;
2487   PetscScalar       *coords = NULL;
2488   PetscInt           numCoords;
2489   PetscBool          isDG;
2490   PetscQuadrature    feQuad;
2491   const PetscReal   *quadPoints;
2492   PetscTabulation    T;
2493   PetscInt           dim, cdim, pdim, qdim, Nq, q;
2494 
2495   PetscFunctionBegin;
2496   PetscCall(DMGetDimension(dm, &dim));
2497   PetscCall(DMGetCoordinateDim(dm, &cdim));
2498   PetscCall(DMPlexGetCellCoordinates(dm, point, &isDG, &numCoords, &array, &coords));
2499   if (!quad) { /* use the first point of the first functional of the dual space */
2500     PetscDualSpace dsp;
2501 
2502     PetscCall(PetscFEGetDualSpace(fe, &dsp));
2503     PetscCall(PetscDualSpaceGetFunctional(dsp, 0, &quad));
2504     PetscCall(PetscQuadratureGetData(quad, &qdim, NULL, &Nq, &quadPoints, NULL));
2505     Nq = 1;
2506   } else {
2507     PetscCall(PetscQuadratureGetData(quad, &qdim, NULL, &Nq, &quadPoints, NULL));
2508   }
2509   PetscCall(PetscFEGetDimension(fe, &pdim));
2510   PetscCall(PetscFEGetQuadrature(fe, &feQuad));
2511   if (feQuad == quad) {
2512     PetscCall(PetscFEGetCellTabulation(fe, J ? 1 : 0, &T));
2513     PetscCheck(numCoords == pdim * cdim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "There are %" PetscInt_FMT " coordinates for point %" PetscInt_FMT " != %" PetscInt_FMT "*%" PetscInt_FMT, numCoords, point, pdim, cdim);
2514   } else {
2515     PetscCall(PetscFECreateTabulation(fe, 1, Nq, quadPoints, J ? 1 : 0, &T));
2516   }
2517   PetscCheck(qdim == dim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Point dimension %" PetscInt_FMT " != quadrature dimension %" PetscInt_FMT, dim, qdim);
2518   {
2519     const PetscReal *basis    = T->T[0];
2520     const PetscReal *basisDer = T->T[1];
2521     PetscReal        detJt;
2522 
2523     PetscAssert(Nq == T->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Np %" PetscInt_FMT " != %" PetscInt_FMT, Nq, T->Np);
2524     PetscAssert(pdim == T->Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Nb %" PetscInt_FMT " != %" PetscInt_FMT, pdim, T->Nb);
2525     PetscAssert(dim == T->Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Nc %" PetscInt_FMT " != %" PetscInt_FMT, dim, T->Nc);
2526     PetscAssert(cdim == T->cdim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "cdim %" PetscInt_FMT " != %" PetscInt_FMT, cdim, T->cdim);
2527     if (v) {
2528       PetscCall(PetscArrayzero(v, Nq * cdim));
2529       for (q = 0; q < Nq; ++q) {
2530         PetscInt i, k;
2531 
2532         for (k = 0; k < pdim; ++k) {
2533           const PetscInt vertex = k / cdim;
2534           for (i = 0; i < cdim; ++i) v[q * cdim + i] += basis[(q * pdim + k) * cdim + i] * PetscRealPart(coords[vertex * cdim + i]);
2535         }
2536         PetscCall(PetscLogFlops(2.0 * pdim * cdim));
2537       }
2538     }
2539     if (J) {
2540       PetscCall(PetscArrayzero(J, Nq * cdim * cdim));
2541       for (q = 0; q < Nq; ++q) {
2542         PetscInt i, j, k, c, r;
2543 
2544         /* J = dx_i/d\xi_j = sum[k=0,n-1] dN_k/d\xi_j * x_i(k) */
2545         for (k = 0; k < pdim; ++k) {
2546           const PetscInt vertex = k / cdim;
2547           for (j = 0; j < dim; ++j) {
2548             for (i = 0; i < cdim; ++i) J[(q * cdim + i) * cdim + j] += basisDer[((q * pdim + k) * cdim + i) * dim + j] * PetscRealPart(coords[vertex * cdim + i]);
2549           }
2550         }
2551         PetscCall(PetscLogFlops(2.0 * pdim * dim * cdim));
2552         if (cdim > dim) {
2553           for (c = dim; c < cdim; ++c)
2554             for (r = 0; r < cdim; ++r) J[r * cdim + c] = r == c ? 1.0 : 0.0;
2555         }
2556         if (!detJ && !invJ) continue;
2557         detJt = 0.;
2558         switch (cdim) {
2559         case 3:
2560           DMPlex_Det3D_Internal(&detJt, &J[q * cdim * dim]);
2561           if (invJ) DMPlex_Invert3D_Internal(&invJ[q * cdim * dim], &J[q * cdim * dim], detJt);
2562           break;
2563         case 2:
2564           DMPlex_Det2D_Internal(&detJt, &J[q * cdim * dim]);
2565           if (invJ) DMPlex_Invert2D_Internal(&invJ[q * cdim * dim], &J[q * cdim * dim], detJt);
2566           break;
2567         case 1:
2568           detJt = J[q * cdim * dim];
2569           if (invJ) invJ[q * cdim * dim] = 1.0 / detJt;
2570         }
2571         if (detJ) detJ[q] = detJt;
2572       }
2573     } else PetscCheck(!detJ && !invJ, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Need J to compute invJ or detJ");
2574   }
2575   if (feQuad != quad) PetscCall(PetscTabulationDestroy(&T));
2576   PetscCall(DMPlexRestoreCellCoordinates(dm, point, &isDG, &numCoords, &array, &coords));
2577   PetscFunctionReturn(PETSC_SUCCESS);
2578 }
2579 
2580 /*@C
2581   DMPlexComputeCellGeometryFEM - Compute the Jacobian, inverse Jacobian, and Jacobian determinant at each quadrature point in the given cell
2582 
2583   Collective
2584 
2585   Input Parameters:
2586 + dm   - the `DMPLEX`
2587 . cell - the cell
2588 - quad - the quadrature containing the points in the reference element where the geometry will be evaluated.  If `quad` is `NULL`, geometry will be
2589          evaluated at the first vertex of the reference element
2590 
2591   Output Parameters:
2592 + v    - the image of the transformed quadrature points, otherwise the image of the first vertex in the closure of the reference element
2593 . J    - the Jacobian of the transform from the reference element at each quadrature point
2594 . invJ - the inverse of the Jacobian at each quadrature point
2595 - detJ - the Jacobian determinant at each quadrature point
2596 
2597   Level: advanced
2598 
2599 .seealso: `DMPLEX`, `DMGetCoordinateSection()`, `DMGetCoordinates()`
2600 @*/
2601 PetscErrorCode DMPlexComputeCellGeometryFEM(DM dm, PetscInt cell, PetscQuadrature quad, PetscReal *v, PetscReal *J, PetscReal *invJ, PetscReal *detJ)
2602 {
2603   DM      cdm;
2604   PetscFE fe = NULL;
2605 
2606   PetscFunctionBegin;
2607   PetscAssertPointer(detJ, 7);
2608   PetscCall(DMGetCoordinateDM(dm, &cdm));
2609   if (cdm) {
2610     PetscClassId id;
2611     PetscInt     numFields;
2612     PetscDS      prob;
2613     PetscObject  disc;
2614 
2615     PetscCall(DMGetNumFields(cdm, &numFields));
2616     if (numFields) {
2617       PetscCall(DMGetDS(cdm, &prob));
2618       PetscCall(PetscDSGetDiscretization(prob, 0, &disc));
2619       PetscCall(PetscObjectGetClassId(disc, &id));
2620       if (id == PETSCFE_CLASSID) fe = (PetscFE)disc;
2621     }
2622   }
2623   if (!fe) PetscCall(DMPlexComputeCellGeometryFEM_Implicit(dm, cell, quad, v, J, invJ, detJ));
2624   else PetscCall(DMPlexComputeCellGeometryFEM_FE(dm, fe, cell, quad, v, J, invJ, detJ));
2625   PetscFunctionReturn(PETSC_SUCCESS);
2626 }
2627 
2628 static PetscErrorCode DMPlexComputeGeometryFVM_0D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2629 {
2630   PetscSection       coordSection;
2631   Vec                coordinates;
2632   const PetscScalar *coords = NULL;
2633   PetscInt           d, dof, off;
2634 
2635   PetscFunctionBegin;
2636   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
2637   PetscCall(DMGetCoordinateSection(dm, &coordSection));
2638   PetscCall(VecGetArrayRead(coordinates, &coords));
2639 
2640   /* for a point the centroid is just the coord */
2641   if (centroid) {
2642     PetscCall(PetscSectionGetDof(coordSection, cell, &dof));
2643     PetscCall(PetscSectionGetOffset(coordSection, cell, &off));
2644     for (d = 0; d < dof; d++) centroid[d] = PetscRealPart(coords[off + d]);
2645   }
2646   if (normal) {
2647     const PetscInt *support, *cones;
2648     PetscInt        supportSize;
2649     PetscReal       norm, sign;
2650 
2651     /* compute the norm based upon the support centroids */
2652     PetscCall(DMPlexGetSupportSize(dm, cell, &supportSize));
2653     PetscCall(DMPlexGetSupport(dm, cell, &support));
2654     PetscCall(DMPlexComputeCellGeometryFVM(dm, support[0], NULL, normal, NULL));
2655 
2656     /* Take the normal from the centroid of the support to the vertex*/
2657     PetscCall(PetscSectionGetDof(coordSection, cell, &dof));
2658     PetscCall(PetscSectionGetOffset(coordSection, cell, &off));
2659     for (d = 0; d < dof; d++) normal[d] -= PetscRealPart(coords[off + d]);
2660 
2661     /* Determine the sign of the normal based upon its location in the support */
2662     PetscCall(DMPlexGetCone(dm, support[0], &cones));
2663     sign = cones[0] == cell ? 1.0 : -1.0;
2664 
2665     norm = DMPlex_NormD_Internal(dim, normal);
2666     for (d = 0; d < dim; ++d) normal[d] /= (norm * sign);
2667   }
2668   if (vol) *vol = 1.0;
2669   PetscCall(VecRestoreArrayRead(coordinates, &coords));
2670   PetscFunctionReturn(PETSC_SUCCESS);
2671 }
2672 
2673 static PetscErrorCode DMPlexComputeGeometryFVM_1D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2674 {
2675   const PetscScalar *array;
2676   PetscScalar       *coords = NULL;
2677   PetscInt           cdim, coordSize, d;
2678   PetscBool          isDG;
2679 
2680   PetscFunctionBegin;
2681   PetscCall(DMGetCoordinateDim(dm, &cdim));
2682   PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2683   PetscCheck(coordSize == cdim * 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge has %" PetscInt_FMT " coordinates != %" PetscInt_FMT, coordSize, cdim * 2);
2684   if (centroid) {
2685     for (d = 0; d < cdim; ++d) centroid[d] = 0.5 * PetscRealPart(coords[d] + coords[cdim + d]);
2686   }
2687   if (normal) {
2688     PetscReal norm;
2689 
2690     switch (cdim) {
2691     case 3:
2692       normal[2] = 0.; /* fall through */
2693     case 2:
2694       normal[0] = -PetscRealPart(coords[1] - coords[cdim + 1]);
2695       normal[1] = PetscRealPart(coords[0] - coords[cdim + 0]);
2696       break;
2697     case 1:
2698       normal[0] = 1.0;
2699       break;
2700     default:
2701       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Dimension %" PetscInt_FMT " not supported", cdim);
2702     }
2703     norm = DMPlex_NormD_Internal(cdim, normal);
2704     for (d = 0; d < cdim; ++d) normal[d] /= norm;
2705   }
2706   if (vol) {
2707     *vol = 0.0;
2708     for (d = 0; d < cdim; ++d) *vol += PetscSqr(PetscRealPart(coords[d] - coords[cdim + d]));
2709     *vol = PetscSqrtReal(*vol);
2710   }
2711   PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2712   PetscFunctionReturn(PETSC_SUCCESS);
2713 }
2714 
2715 /* Centroid_i = (\sum_n A_n Cn_i) / A */
2716 static PetscErrorCode DMPlexComputeGeometryFVM_2D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2717 {
2718   DMPolytopeType     ct;
2719   const PetscScalar *array;
2720   PetscScalar       *coords = NULL;
2721   PetscInt           coordSize;
2722   PetscBool          isDG;
2723   PetscInt           fv[4] = {0, 1, 2, 3};
2724   PetscInt           cdim, numCorners, p, d;
2725 
2726   PetscFunctionBegin;
2727   /* Must check for hybrid cells because prisms have a different orientation scheme */
2728   PetscCall(DMPlexGetCellType(dm, cell, &ct));
2729   switch (ct) {
2730   case DM_POLYTOPE_SEG_PRISM_TENSOR:
2731     fv[2] = 3;
2732     fv[3] = 2;
2733     break;
2734   default:
2735     break;
2736   }
2737   PetscCall(DMGetCoordinateDim(dm, &cdim));
2738   PetscCall(DMPlexGetConeSize(dm, cell, &numCorners));
2739   PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2740   {
2741     PetscReal c[3] = {0., 0., 0.}, n[3] = {0., 0., 0.}, origin[3] = {0., 0., 0.}, norm;
2742 
2743     for (d = 0; d < cdim; d++) origin[d] = PetscRealPart(coords[d]);
2744     for (p = 0; p < numCorners - 2; ++p) {
2745       PetscReal e0[3] = {0., 0., 0.}, e1[3] = {0., 0., 0.};
2746       for (d = 0; d < cdim; d++) {
2747         e0[d] = PetscRealPart(coords[cdim * fv[p + 1] + d]) - origin[d];
2748         e1[d] = PetscRealPart(coords[cdim * fv[p + 2] + d]) - origin[d];
2749       }
2750       const PetscReal dx = e0[1] * e1[2] - e0[2] * e1[1];
2751       const PetscReal dy = e0[2] * e1[0] - e0[0] * e1[2];
2752       const PetscReal dz = e0[0] * e1[1] - e0[1] * e1[0];
2753       const PetscReal a  = PetscSqrtReal(dx * dx + dy * dy + dz * dz);
2754 
2755       n[0] += dx;
2756       n[1] += dy;
2757       n[2] += dz;
2758       for (d = 0; d < cdim; d++) c[d] += a * PetscRealPart(origin[d] + coords[cdim * fv[p + 1] + d] + coords[cdim * fv[p + 2] + d]) / 3.;
2759     }
2760     norm = PetscSqrtReal(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
2761     // Allow zero volume cells
2762     if (norm != 0) {
2763       n[0] /= norm;
2764       n[1] /= norm;
2765       n[2] /= norm;
2766       c[0] /= norm;
2767       c[1] /= norm;
2768       c[2] /= norm;
2769     }
2770     if (vol) *vol = 0.5 * norm;
2771     if (centroid)
2772       for (d = 0; d < cdim; ++d) centroid[d] = c[d];
2773     if (normal)
2774       for (d = 0; d < cdim; ++d) normal[d] = n[d];
2775   }
2776   PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2777   PetscFunctionReturn(PETSC_SUCCESS);
2778 }
2779 
2780 /* Centroid_i = (\sum_n V_n Cn_i) / V */
2781 static PetscErrorCode DMPlexComputeGeometryFVM_3D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2782 {
2783   DMPolytopeType        ct;
2784   const PetscScalar    *array;
2785   PetscScalar          *coords = NULL;
2786   PetscInt              coordSize;
2787   PetscBool             isDG;
2788   PetscReal             vsum      = 0.0, vtmp, coordsTmp[3 * 3], origin[3];
2789   const PetscInt        order[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
2790   const PetscInt       *cone, *faceSizes, *faces;
2791   const DMPolytopeType *faceTypes;
2792   PetscBool             isHybrid = PETSC_FALSE;
2793   PetscInt              numFaces, f, fOff = 0, p, d;
2794 
2795   PetscFunctionBegin;
2796   PetscCheck(dim <= 3, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No support for dim %" PetscInt_FMT " > 3", dim);
2797   /* Must check for hybrid cells because prisms have a different orientation scheme */
2798   PetscCall(DMPlexGetCellType(dm, cell, &ct));
2799   switch (ct) {
2800   case DM_POLYTOPE_POINT_PRISM_TENSOR:
2801   case DM_POLYTOPE_SEG_PRISM_TENSOR:
2802   case DM_POLYTOPE_TRI_PRISM_TENSOR:
2803   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
2804     isHybrid = PETSC_TRUE;
2805   default:
2806     break;
2807   }
2808 
2809   if (centroid)
2810     for (d = 0; d < dim; ++d) centroid[d] = 0.0;
2811   PetscCall(DMPlexGetCone(dm, cell, &cone));
2812 
2813   // Using the closure of faces for coordinates does not work in periodic geometries, so we index into the cell coordinates
2814   PetscCall(DMPlexGetRawFaces_Internal(dm, ct, order, &numFaces, &faceTypes, &faceSizes, &faces));
2815   PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2816   for (f = 0; f < numFaces; ++f) {
2817     PetscBool flip = isHybrid && f == 0 ? PETSC_TRUE : PETSC_FALSE; /* The first hybrid face is reversed */
2818 
2819     // If using zero as the origin vertex for each tetrahedron, an element far from the origin will have positive and
2820     // negative volumes that nearly cancel, thus incurring rounding error. Here we define origin[] as the first vertex
2821     // so that all tetrahedra have positive volume.
2822     if (f == 0)
2823       for (d = 0; d < dim; d++) origin[d] = PetscRealPart(coords[d]);
2824     switch (faceTypes[f]) {
2825     case DM_POLYTOPE_TRIANGLE:
2826       for (d = 0; d < dim; ++d) {
2827         coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + 0] * dim + d]) - origin[d];
2828         coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + 1] * dim + d]) - origin[d];
2829         coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + 2] * dim + d]) - origin[d];
2830       }
2831       Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2832       if (flip) vtmp = -vtmp;
2833       vsum += vtmp;
2834       if (centroid) { /* Centroid of OABC = (a+b+c)/4 */
2835         for (d = 0; d < dim; ++d) {
2836           for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp;
2837         }
2838       }
2839       break;
2840     case DM_POLYTOPE_QUADRILATERAL:
2841     case DM_POLYTOPE_SEG_PRISM_TENSOR: {
2842       PetscInt fv[4] = {0, 1, 2, 3};
2843 
2844       /* Side faces for hybrid cells are stored as tensor products */
2845       if (isHybrid && f > 1) {
2846         fv[2] = 3;
2847         fv[3] = 2;
2848       }
2849       /* DO FOR PYRAMID */
2850       /* First tet */
2851       for (d = 0; d < dim; ++d) {
2852         coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + fv[0]] * dim + d]) - origin[d];
2853         coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + fv[1]] * dim + d]) - origin[d];
2854         coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + fv[3]] * dim + d]) - origin[d];
2855       }
2856       Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2857       if (flip) vtmp = -vtmp;
2858       vsum += vtmp;
2859       if (centroid) {
2860         for (d = 0; d < dim; ++d) {
2861           for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp;
2862         }
2863       }
2864       /* Second tet */
2865       for (d = 0; d < dim; ++d) {
2866         coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + fv[1]] * dim + d]) - origin[d];
2867         coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + fv[2]] * dim + d]) - origin[d];
2868         coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + fv[3]] * dim + d]) - origin[d];
2869       }
2870       Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2871       if (flip) vtmp = -vtmp;
2872       vsum += vtmp;
2873       if (centroid) {
2874         for (d = 0; d < dim; ++d) {
2875           for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp;
2876         }
2877       }
2878       break;
2879     }
2880     default:
2881       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle face %" PetscInt_FMT " of type %s", cone[f], DMPolytopeTypes[ct]);
2882     }
2883     fOff += faceSizes[f];
2884   }
2885   PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, order, &numFaces, &faceTypes, &faceSizes, &faces));
2886   PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2887   if (vol) *vol = PetscAbsReal(vsum);
2888   if (normal)
2889     for (d = 0; d < dim; ++d) normal[d] = 0.0;
2890   if (centroid)
2891     for (d = 0; d < dim; ++d) centroid[d] = centroid[d] / (vsum * 4) + origin[d];
2892   PetscFunctionReturn(PETSC_SUCCESS);
2893 }
2894 
2895 /*@C
2896   DMPlexComputeCellGeometryFVM - Compute the volume for a given cell
2897 
2898   Collective
2899 
2900   Input Parameters:
2901 + dm   - the `DMPLEX`
2902 - cell - the cell
2903 
2904   Output Parameters:
2905 + vol      - the cell volume
2906 . centroid - the cell centroid
2907 - normal   - the cell normal, if appropriate
2908 
2909   Level: advanced
2910 
2911 .seealso: `DMPLEX`, `DMGetCoordinateSection()`, `DMGetCoordinates()`
2912 @*/
2913 PetscErrorCode DMPlexComputeCellGeometryFVM(DM dm, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2914 {
2915   PetscInt depth, dim;
2916 
2917   PetscFunctionBegin;
2918   PetscCall(DMPlexGetDepth(dm, &depth));
2919   PetscCall(DMGetDimension(dm, &dim));
2920   PetscCheck(depth == dim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh must be interpolated");
2921   PetscCall(DMPlexGetPointDepth(dm, cell, &depth));
2922   switch (depth) {
2923   case 0:
2924     PetscCall(DMPlexComputeGeometryFVM_0D_Internal(dm, dim, cell, vol, centroid, normal));
2925     break;
2926   case 1:
2927     PetscCall(DMPlexComputeGeometryFVM_1D_Internal(dm, dim, cell, vol, centroid, normal));
2928     break;
2929   case 2:
2930     PetscCall(DMPlexComputeGeometryFVM_2D_Internal(dm, dim, cell, vol, centroid, normal));
2931     break;
2932   case 3:
2933     PetscCall(DMPlexComputeGeometryFVM_3D_Internal(dm, dim, cell, vol, centroid, normal));
2934     break;
2935   default:
2936     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unsupported dimension %" PetscInt_FMT " (depth %" PetscInt_FMT ") for element geometry computation", dim, depth);
2937   }
2938   PetscFunctionReturn(PETSC_SUCCESS);
2939 }
2940 
2941 /*@
2942   DMPlexComputeGeometryFVM - Computes the cell and face geometry for a finite volume method
2943 
2944   Input Parameter:
2945 . dm - The `DMPLEX`
2946 
2947   Output Parameters:
2948 + cellgeom - A `Vec` of `PetscFVCellGeom` data
2949 - facegeom - A `Vec` of `PetscFVFaceGeom` data
2950 
2951   Level: developer
2952 
2953 .seealso: `DMPLEX`, `PetscFVFaceGeom`, `PetscFVCellGeom`
2954 @*/
2955 PetscErrorCode DMPlexComputeGeometryFVM(DM dm, Vec *cellgeom, Vec *facegeom)
2956 {
2957   DM           dmFace, dmCell;
2958   DMLabel      ghostLabel;
2959   PetscSection sectionFace, sectionCell;
2960   PetscSection coordSection;
2961   Vec          coordinates;
2962   PetscScalar *fgeom, *cgeom;
2963   PetscReal    minradius, gminradius;
2964   PetscInt     dim, cStart, cEnd, cEndInterior, c, fStart, fEnd, f;
2965 
2966   PetscFunctionBegin;
2967   PetscCall(DMGetDimension(dm, &dim));
2968   PetscCall(DMGetCoordinateSection(dm, &coordSection));
2969   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
2970   /* Make cell centroids and volumes */
2971   PetscCall(DMClone(dm, &dmCell));
2972   PetscCall(DMSetCoordinateSection(dmCell, PETSC_DETERMINE, coordSection));
2973   PetscCall(DMSetCoordinatesLocal(dmCell, coordinates));
2974   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &sectionCell));
2975   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2976   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
2977   PetscCall(PetscSectionSetChart(sectionCell, cStart, cEnd));
2978   for (c = cStart; c < cEnd; ++c) PetscCall(PetscSectionSetDof(sectionCell, c, (PetscInt)PetscCeilReal(((PetscReal)sizeof(PetscFVCellGeom)) / sizeof(PetscScalar))));
2979   PetscCall(PetscSectionSetUp(sectionCell));
2980   PetscCall(DMSetLocalSection(dmCell, sectionCell));
2981   PetscCall(PetscSectionDestroy(&sectionCell));
2982   PetscCall(DMCreateLocalVector(dmCell, cellgeom));
2983   if (cEndInterior < 0) cEndInterior = cEnd;
2984   PetscCall(VecGetArray(*cellgeom, &cgeom));
2985   for (c = cStart; c < cEndInterior; ++c) {
2986     PetscFVCellGeom *cg;
2987 
2988     PetscCall(DMPlexPointLocalRef(dmCell, c, cgeom, &cg));
2989     PetscCall(PetscArrayzero(cg, 1));
2990     PetscCall(DMPlexComputeCellGeometryFVM(dmCell, c, &cg->volume, cg->centroid, NULL));
2991   }
2992   /* Compute face normals and minimum cell radius */
2993   PetscCall(DMClone(dm, &dmFace));
2994   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &sectionFace));
2995   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
2996   PetscCall(PetscSectionSetChart(sectionFace, fStart, fEnd));
2997   for (f = fStart; f < fEnd; ++f) PetscCall(PetscSectionSetDof(sectionFace, f, (PetscInt)PetscCeilReal(((PetscReal)sizeof(PetscFVFaceGeom)) / sizeof(PetscScalar))));
2998   PetscCall(PetscSectionSetUp(sectionFace));
2999   PetscCall(DMSetLocalSection(dmFace, sectionFace));
3000   PetscCall(PetscSectionDestroy(&sectionFace));
3001   PetscCall(DMCreateLocalVector(dmFace, facegeom));
3002   PetscCall(VecGetArray(*facegeom, &fgeom));
3003   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3004   minradius = PETSC_MAX_REAL;
3005   for (f = fStart; f < fEnd; ++f) {
3006     PetscFVFaceGeom *fg;
3007     PetscReal        area;
3008     const PetscInt  *cells;
3009     PetscInt         ncells, ghost = -1, d, numChildren;
3010 
3011     if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost));
3012     PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL));
3013     PetscCall(DMPlexGetSupport(dm, f, &cells));
3014     PetscCall(DMPlexGetSupportSize(dm, f, &ncells));
3015     /* It is possible to get a face with no support when using partition overlap */
3016     if (!ncells || ghost >= 0 || numChildren) continue;
3017     PetscCall(DMPlexPointLocalRef(dmFace, f, fgeom, &fg));
3018     PetscCall(DMPlexComputeCellGeometryFVM(dm, f, &area, fg->centroid, fg->normal));
3019     for (d = 0; d < dim; ++d) fg->normal[d] *= area;
3020     /* Flip face orientation if necessary to match ordering in support, and Update minimum radius */
3021     {
3022       PetscFVCellGeom *cL, *cR;
3023       PetscReal       *lcentroid, *rcentroid;
3024       PetscReal        l[3], r[3], v[3];
3025 
3026       PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cgeom, &cL));
3027       lcentroid = cells[0] >= cEndInterior ? fg->centroid : cL->centroid;
3028       if (ncells > 1) {
3029         PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cgeom, &cR));
3030         rcentroid = cells[1] >= cEndInterior ? fg->centroid : cR->centroid;
3031       } else {
3032         rcentroid = fg->centroid;
3033       }
3034       PetscCall(DMLocalizeCoordinateReal_Internal(dm, dim, fg->centroid, lcentroid, l));
3035       PetscCall(DMLocalizeCoordinateReal_Internal(dm, dim, fg->centroid, rcentroid, r));
3036       DMPlex_WaxpyD_Internal(dim, -1, l, r, v);
3037       if (DMPlex_DotRealD_Internal(dim, fg->normal, v) < 0) {
3038         for (d = 0; d < dim; ++d) fg->normal[d] = -fg->normal[d];
3039       }
3040       if (DMPlex_DotRealD_Internal(dim, fg->normal, v) <= 0) {
3041         PetscCheck(dim != 2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Direction for face %" PetscInt_FMT " could not be fixed, normal (%g,%g) v (%g,%g)", f, (double)fg->normal[0], (double)fg->normal[1], (double)v[0], (double)v[1]);
3042         PetscCheck(dim != 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Direction for face %" PetscInt_FMT " could not be fixed, normal (%g,%g,%g) v (%g,%g,%g)", f, (double)fg->normal[0], (double)fg->normal[1], (double)fg->normal[2], (double)v[0], (double)v[1], (double)v[2]);
3043         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Direction for face %" PetscInt_FMT " could not be fixed", f);
3044       }
3045       if (cells[0] < cEndInterior) {
3046         DMPlex_WaxpyD_Internal(dim, -1, fg->centroid, cL->centroid, v);
3047         minradius = PetscMin(minradius, DMPlex_NormD_Internal(dim, v));
3048       }
3049       if (ncells > 1 && cells[1] < cEndInterior) {
3050         DMPlex_WaxpyD_Internal(dim, -1, fg->centroid, cR->centroid, v);
3051         minradius = PetscMin(minradius, DMPlex_NormD_Internal(dim, v));
3052       }
3053     }
3054   }
3055   PetscCallMPI(MPIU_Allreduce(&minradius, &gminradius, 1, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
3056   PetscCall(DMPlexSetMinRadius(dm, gminradius));
3057   /* Compute centroids of ghost cells */
3058   for (c = cEndInterior; c < cEnd; ++c) {
3059     PetscFVFaceGeom *fg;
3060     const PetscInt  *cone, *support;
3061     PetscInt         coneSize, supportSize, s;
3062 
3063     PetscCall(DMPlexGetConeSize(dmCell, c, &coneSize));
3064     PetscCheck(coneSize == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Ghost cell %" PetscInt_FMT " has cone size %" PetscInt_FMT " != 1", c, coneSize);
3065     PetscCall(DMPlexGetCone(dmCell, c, &cone));
3066     PetscCall(DMPlexGetSupportSize(dmCell, cone[0], &supportSize));
3067     PetscCheck(supportSize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[0], supportSize);
3068     PetscCall(DMPlexGetSupport(dmCell, cone[0], &support));
3069     PetscCall(DMPlexPointLocalRef(dmFace, cone[0], fgeom, &fg));
3070     for (s = 0; s < 2; ++s) {
3071       /* Reflect ghost centroid across plane of face */
3072       if (support[s] == c) {
3073         PetscFVCellGeom *ci;
3074         PetscFVCellGeom *cg;
3075         PetscReal        c2f[3], a;
3076 
3077         PetscCall(DMPlexPointLocalRead(dmCell, support[(s + 1) % 2], cgeom, &ci));
3078         DMPlex_WaxpyD_Internal(dim, -1, ci->centroid, fg->centroid, c2f); /* cell to face centroid */
3079         a = DMPlex_DotRealD_Internal(dim, c2f, fg->normal) / DMPlex_DotRealD_Internal(dim, fg->normal, fg->normal);
3080         PetscCall(DMPlexPointLocalRef(dmCell, support[s], cgeom, &cg));
3081         DMPlex_WaxpyD_Internal(dim, 2 * a, fg->normal, ci->centroid, cg->centroid);
3082         cg->volume = ci->volume;
3083       }
3084     }
3085   }
3086   PetscCall(VecRestoreArray(*facegeom, &fgeom));
3087   PetscCall(VecRestoreArray(*cellgeom, &cgeom));
3088   PetscCall(DMDestroy(&dmCell));
3089   PetscCall(DMDestroy(&dmFace));
3090   PetscFunctionReturn(PETSC_SUCCESS);
3091 }
3092 
3093 /*@
3094   DMPlexGetMinRadius - Returns the minimum distance from any cell centroid to a face
3095 
3096   Not Collective
3097 
3098   Input Parameter:
3099 . dm - the `DMPLEX`
3100 
3101   Output Parameter:
3102 . minradius - the minimum cell radius
3103 
3104   Level: developer
3105 
3106 .seealso: `DMPLEX`, `DMGetCoordinates()`
3107 @*/
3108 PetscErrorCode DMPlexGetMinRadius(DM dm, PetscReal *minradius)
3109 {
3110   PetscFunctionBegin;
3111   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3112   PetscAssertPointer(minradius, 2);
3113   *minradius = ((DM_Plex *)dm->data)->minradius;
3114   PetscFunctionReturn(PETSC_SUCCESS);
3115 }
3116 
3117 /*@
3118   DMPlexSetMinRadius - Sets the minimum distance from the cell centroid to a face
3119 
3120   Logically Collective
3121 
3122   Input Parameters:
3123 + dm        - the `DMPLEX`
3124 - minradius - the minimum cell radius
3125 
3126   Level: developer
3127 
3128 .seealso: `DMPLEX`, `DMSetCoordinates()`
3129 @*/
3130 PetscErrorCode DMPlexSetMinRadius(DM dm, PetscReal minradius)
3131 {
3132   PetscFunctionBegin;
3133   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3134   ((DM_Plex *)dm->data)->minradius = minradius;
3135   PetscFunctionReturn(PETSC_SUCCESS);
3136 }
3137 
3138 static PetscErrorCode BuildGradientReconstruction_Internal(DM dm, PetscFV fvm, DM dmFace, PetscScalar *fgeom, DM dmCell, PetscScalar *cgeom)
3139 {
3140   DMLabel      ghostLabel;
3141   PetscScalar *dx, *grad, **gref;
3142   PetscInt     dim, cStart, cEnd, c, cEndInterior, maxNumFaces;
3143 
3144   PetscFunctionBegin;
3145   PetscCall(DMGetDimension(dm, &dim));
3146   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
3147   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
3148   cEndInterior = cEndInterior < 0 ? cEnd : cEndInterior;
3149   PetscCall(DMPlexGetMaxSizes(dm, &maxNumFaces, NULL));
3150   PetscCall(PetscFVLeastSquaresSetMaxFaces(fvm, maxNumFaces));
3151   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3152   PetscCall(PetscMalloc3(maxNumFaces * dim, &dx, maxNumFaces * dim, &grad, maxNumFaces, &gref));
3153   for (c = cStart; c < cEndInterior; c++) {
3154     const PetscInt  *faces;
3155     PetscInt         numFaces, usedFaces, f, d;
3156     PetscFVCellGeom *cg;
3157     PetscBool        boundary;
3158     PetscInt         ghost;
3159 
3160     // do not attempt to compute a gradient reconstruction stencil in a ghost cell.  It will never be used
3161     PetscCall(DMLabelGetValue(ghostLabel, c, &ghost));
3162     if (ghost >= 0) continue;
3163 
3164     PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg));
3165     PetscCall(DMPlexGetConeSize(dm, c, &numFaces));
3166     PetscCall(DMPlexGetCone(dm, c, &faces));
3167     PetscCheck(numFaces >= dim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Cell %" PetscInt_FMT " has only %" PetscInt_FMT " faces, not enough for gradient reconstruction", c, numFaces);
3168     for (f = 0, usedFaces = 0; f < numFaces; ++f) {
3169       PetscFVCellGeom *cg1;
3170       PetscFVFaceGeom *fg;
3171       const PetscInt  *fcells;
3172       PetscInt         ncell, side;
3173 
3174       PetscCall(DMLabelGetValue(ghostLabel, faces[f], &ghost));
3175       PetscCall(DMIsBoundaryPoint(dm, faces[f], &boundary));
3176       if ((ghost >= 0) || boundary) continue;
3177       PetscCall(DMPlexGetSupport(dm, faces[f], &fcells));
3178       side  = (c != fcells[0]); /* c is on left=0 or right=1 of face */
3179       ncell = fcells[!side];    /* the neighbor */
3180       PetscCall(DMPlexPointLocalRef(dmFace, faces[f], fgeom, &fg));
3181       PetscCall(DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1));
3182       for (d = 0; d < dim; ++d) dx[usedFaces * dim + d] = cg1->centroid[d] - cg->centroid[d];
3183       gref[usedFaces++] = fg->grad[side]; /* Gradient reconstruction term will go here */
3184     }
3185     PetscCheck(usedFaces, PETSC_COMM_SELF, PETSC_ERR_USER, "Mesh contains isolated cell (no neighbors). Is it intentional?");
3186     PetscCall(PetscFVComputeGradient(fvm, usedFaces, dx, grad));
3187     for (f = 0, usedFaces = 0; f < numFaces; ++f) {
3188       PetscCall(DMLabelGetValue(ghostLabel, faces[f], &ghost));
3189       PetscCall(DMIsBoundaryPoint(dm, faces[f], &boundary));
3190       if ((ghost >= 0) || boundary) continue;
3191       for (d = 0; d < dim; ++d) gref[usedFaces][d] = grad[usedFaces * dim + d];
3192       ++usedFaces;
3193     }
3194   }
3195   PetscCall(PetscFree3(dx, grad, gref));
3196   PetscFunctionReturn(PETSC_SUCCESS);
3197 }
3198 
3199 static PetscErrorCode BuildGradientReconstruction_Internal_Tree(DM dm, PetscFV fvm, DM dmFace, PetscScalar *fgeom, DM dmCell, PetscScalar *cgeom)
3200 {
3201   DMLabel      ghostLabel;
3202   PetscScalar *dx, *grad, **gref;
3203   PetscInt     dim, cStart, cEnd, c, cEndInterior, fStart, fEnd, f, nStart, nEnd, maxNumFaces = 0;
3204   PetscSection neighSec;
3205   PetscInt(*neighbors)[2];
3206   PetscInt *counter;
3207 
3208   PetscFunctionBegin;
3209   PetscCall(DMGetDimension(dm, &dim));
3210   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
3211   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
3212   if (cEndInterior < 0) cEndInterior = cEnd;
3213   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &neighSec));
3214   PetscCall(PetscSectionSetChart(neighSec, cStart, cEndInterior));
3215   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
3216   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3217   for (f = fStart; f < fEnd; f++) {
3218     const PetscInt *fcells;
3219     PetscBool       boundary;
3220     PetscInt        ghost = -1;
3221     PetscInt        numChildren, numCells, c;
3222 
3223     if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost));
3224     PetscCall(DMIsBoundaryPoint(dm, f, &boundary));
3225     PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL));
3226     if ((ghost >= 0) || boundary || numChildren) continue;
3227     PetscCall(DMPlexGetSupportSize(dm, f, &numCells));
3228     if (numCells == 2) {
3229       PetscCall(DMPlexGetSupport(dm, f, &fcells));
3230       for (c = 0; c < 2; c++) {
3231         PetscInt cell = fcells[c];
3232 
3233         if (cell >= cStart && cell < cEndInterior) PetscCall(PetscSectionAddDof(neighSec, cell, 1));
3234       }
3235     }
3236   }
3237   PetscCall(PetscSectionSetUp(neighSec));
3238   PetscCall(PetscSectionGetMaxDof(neighSec, &maxNumFaces));
3239   PetscCall(PetscFVLeastSquaresSetMaxFaces(fvm, maxNumFaces));
3240   nStart = 0;
3241   PetscCall(PetscSectionGetStorageSize(neighSec, &nEnd));
3242   PetscCall(PetscMalloc1(nEnd - nStart, &neighbors));
3243   PetscCall(PetscCalloc1(cEndInterior - cStart, &counter));
3244   for (f = fStart; f < fEnd; f++) {
3245     const PetscInt *fcells;
3246     PetscBool       boundary;
3247     PetscInt        ghost = -1;
3248     PetscInt        numChildren, numCells, c;
3249 
3250     if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost));
3251     PetscCall(DMIsBoundaryPoint(dm, f, &boundary));
3252     PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL));
3253     if ((ghost >= 0) || boundary || numChildren) continue;
3254     PetscCall(DMPlexGetSupportSize(dm, f, &numCells));
3255     if (numCells == 2) {
3256       PetscCall(DMPlexGetSupport(dm, f, &fcells));
3257       for (c = 0; c < 2; c++) {
3258         PetscInt cell = fcells[c], off;
3259 
3260         if (cell >= cStart && cell < cEndInterior) {
3261           PetscCall(PetscSectionGetOffset(neighSec, cell, &off));
3262           off += counter[cell - cStart]++;
3263           neighbors[off][0] = f;
3264           neighbors[off][1] = fcells[1 - c];
3265         }
3266       }
3267     }
3268   }
3269   PetscCall(PetscFree(counter));
3270   PetscCall(PetscMalloc3(maxNumFaces * dim, &dx, maxNumFaces * dim, &grad, maxNumFaces, &gref));
3271   for (c = cStart; c < cEndInterior; c++) {
3272     PetscInt         numFaces, f, d, off, ghost = -1;
3273     PetscFVCellGeom *cg;
3274 
3275     PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg));
3276     PetscCall(PetscSectionGetDof(neighSec, c, &numFaces));
3277     PetscCall(PetscSectionGetOffset(neighSec, c, &off));
3278 
3279     // do not attempt to compute a gradient reconstruction stencil in a ghost cell.  It will never be used
3280     if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, c, &ghost));
3281     if (ghost >= 0) continue;
3282 
3283     PetscCheck(numFaces >= dim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Cell %" PetscInt_FMT " has only %" PetscInt_FMT " faces, not enough for gradient reconstruction", c, numFaces);
3284     for (f = 0; f < numFaces; ++f) {
3285       PetscFVCellGeom *cg1;
3286       PetscFVFaceGeom *fg;
3287       const PetscInt  *fcells;
3288       PetscInt         ncell, side, nface;
3289 
3290       nface = neighbors[off + f][0];
3291       ncell = neighbors[off + f][1];
3292       PetscCall(DMPlexGetSupport(dm, nface, &fcells));
3293       side = (c != fcells[0]);
3294       PetscCall(DMPlexPointLocalRef(dmFace, nface, fgeom, &fg));
3295       PetscCall(DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1));
3296       for (d = 0; d < dim; ++d) dx[f * dim + d] = cg1->centroid[d] - cg->centroid[d];
3297       gref[f] = fg->grad[side]; /* Gradient reconstruction term will go here */
3298     }
3299     PetscCall(PetscFVComputeGradient(fvm, numFaces, dx, grad));
3300     for (f = 0; f < numFaces; ++f) {
3301       for (d = 0; d < dim; ++d) gref[f][d] = grad[f * dim + d];
3302     }
3303   }
3304   PetscCall(PetscFree3(dx, grad, gref));
3305   PetscCall(PetscSectionDestroy(&neighSec));
3306   PetscCall(PetscFree(neighbors));
3307   PetscFunctionReturn(PETSC_SUCCESS);
3308 }
3309 
3310 /*@
3311   DMPlexComputeGradientFVM - Compute geometric factors for gradient reconstruction, which are stored in the geometry data, and compute layout for gradient data
3312 
3313   Collective
3314 
3315   Input Parameters:
3316 + dm           - The `DMPLEX`
3317 . fvm          - The `PetscFV`
3318 - cellGeometry - The face geometry from `DMPlexComputeCellGeometryFVM()`
3319 
3320   Input/Output Parameter:
3321 . faceGeometry - The face geometry from `DMPlexComputeFaceGeometryFVM()`; on output
3322                  the geometric factors for gradient calculation are inserted
3323 
3324   Output Parameter:
3325 . dmGrad - The `DM` describing the layout of gradient data
3326 
3327   Level: developer
3328 
3329 .seealso: `DMPLEX`, `DMPlexGetFaceGeometryFVM()`, `DMPlexGetCellGeometryFVM()`
3330 @*/
3331 PetscErrorCode DMPlexComputeGradientFVM(DM dm, PetscFV fvm, Vec faceGeometry, Vec cellGeometry, DM *dmGrad)
3332 {
3333   DM           dmFace, dmCell;
3334   PetscScalar *fgeom, *cgeom;
3335   PetscSection sectionGrad, parentSection;
3336   PetscInt     dim, pdim, cStart, cEnd, cEndInterior, c;
3337 
3338   PetscFunctionBegin;
3339   PetscCall(DMGetDimension(dm, &dim));
3340   PetscCall(PetscFVGetNumComponents(fvm, &pdim));
3341   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
3342   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
3343   /* Construct the interpolant corresponding to each face from the least-square solution over the cell neighborhood */
3344   PetscCall(VecGetDM(faceGeometry, &dmFace));
3345   PetscCall(VecGetDM(cellGeometry, &dmCell));
3346   PetscCall(VecGetArray(faceGeometry, &fgeom));
3347   PetscCall(VecGetArray(cellGeometry, &cgeom));
3348   PetscCall(DMPlexGetTree(dm, &parentSection, NULL, NULL, NULL, NULL));
3349   if (!parentSection) {
3350     PetscCall(BuildGradientReconstruction_Internal(dm, fvm, dmFace, fgeom, dmCell, cgeom));
3351   } else {
3352     PetscCall(BuildGradientReconstruction_Internal_Tree(dm, fvm, dmFace, fgeom, dmCell, cgeom));
3353   }
3354   PetscCall(VecRestoreArray(faceGeometry, &fgeom));
3355   PetscCall(VecRestoreArray(cellGeometry, &cgeom));
3356   /* Create storage for gradients */
3357   PetscCall(DMClone(dm, dmGrad));
3358   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &sectionGrad));
3359   PetscCall(PetscSectionSetChart(sectionGrad, cStart, cEnd));
3360   for (c = cStart; c < cEnd; ++c) PetscCall(PetscSectionSetDof(sectionGrad, c, pdim * dim));
3361   PetscCall(PetscSectionSetUp(sectionGrad));
3362   PetscCall(DMSetLocalSection(*dmGrad, sectionGrad));
3363   PetscCall(PetscSectionDestroy(&sectionGrad));
3364   PetscFunctionReturn(PETSC_SUCCESS);
3365 }
3366 
3367 /*@
3368   DMPlexGetDataFVM - Retrieve precomputed cell geometry
3369 
3370   Collective
3371 
3372   Input Parameters:
3373 + dm - The `DM`
3374 - fv - The `PetscFV`
3375 
3376   Output Parameters:
3377 + cellgeom - The cell geometry
3378 . facegeom - The face geometry
3379 - gradDM   - The gradient matrices
3380 
3381   Level: developer
3382 
3383 .seealso: `DMPLEX`, `DMPlexComputeGeometryFVM()`
3384 @*/
3385 PetscErrorCode DMPlexGetDataFVM(DM dm, PetscFV fv, Vec *cellgeom, Vec *facegeom, DM *gradDM)
3386 {
3387   PetscObject cellgeomobj, facegeomobj;
3388 
3389   PetscFunctionBegin;
3390   PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_cellgeom_fvm", &cellgeomobj));
3391   if (!cellgeomobj) {
3392     Vec cellgeomInt, facegeomInt;
3393 
3394     PetscCall(DMPlexComputeGeometryFVM(dm, &cellgeomInt, &facegeomInt));
3395     PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_cellgeom_fvm", (PetscObject)cellgeomInt));
3396     PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_facegeom_fvm", (PetscObject)facegeomInt));
3397     PetscCall(VecDestroy(&cellgeomInt));
3398     PetscCall(VecDestroy(&facegeomInt));
3399     PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_cellgeom_fvm", &cellgeomobj));
3400   }
3401   PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_facegeom_fvm", &facegeomobj));
3402   if (cellgeom) *cellgeom = (Vec)cellgeomobj;
3403   if (facegeom) *facegeom = (Vec)facegeomobj;
3404   if (gradDM) {
3405     PetscObject gradobj;
3406     PetscBool   computeGradients;
3407 
3408     PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
3409     if (!computeGradients) {
3410       *gradDM = NULL;
3411       PetscFunctionReturn(PETSC_SUCCESS);
3412     }
3413     PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_dmgrad_fvm", &gradobj));
3414     if (!gradobj) {
3415       DM dmGradInt;
3416 
3417       PetscCall(DMPlexComputeGradientFVM(dm, fv, (Vec)facegeomobj, (Vec)cellgeomobj, &dmGradInt));
3418       PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_dmgrad_fvm", (PetscObject)dmGradInt));
3419       PetscCall(DMDestroy(&dmGradInt));
3420       PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_dmgrad_fvm", &gradobj));
3421     }
3422     *gradDM = (DM)gradobj;
3423   }
3424   PetscFunctionReturn(PETSC_SUCCESS);
3425 }
3426 
3427 static PetscErrorCode DMPlexCoordinatesToReference_NewtonUpdate(PetscInt dimC, PetscInt dimR, PetscScalar *J, PetscScalar *invJ, PetscScalar *work, PetscReal *resNeg, PetscReal *guess)
3428 {
3429   PetscInt l, m;
3430 
3431   PetscFunctionBeginHot;
3432   if (dimC == dimR && dimR <= 3) {
3433     /* invert Jacobian, multiply */
3434     PetscScalar det, idet;
3435 
3436     switch (dimR) {
3437     case 1:
3438       invJ[0] = 1. / J[0];
3439       break;
3440     case 2:
3441       det     = J[0] * J[3] - J[1] * J[2];
3442       idet    = 1. / det;
3443       invJ[0] = J[3] * idet;
3444       invJ[1] = -J[1] * idet;
3445       invJ[2] = -J[2] * idet;
3446       invJ[3] = J[0] * idet;
3447       break;
3448     case 3: {
3449       invJ[0] = J[4] * J[8] - J[5] * J[7];
3450       invJ[1] = J[2] * J[7] - J[1] * J[8];
3451       invJ[2] = J[1] * J[5] - J[2] * J[4];
3452       det     = invJ[0] * J[0] + invJ[1] * J[3] + invJ[2] * J[6];
3453       idet    = 1. / det;
3454       invJ[0] *= idet;
3455       invJ[1] *= idet;
3456       invJ[2] *= idet;
3457       invJ[3] = idet * (J[5] * J[6] - J[3] * J[8]);
3458       invJ[4] = idet * (J[0] * J[8] - J[2] * J[6]);
3459       invJ[5] = idet * (J[2] * J[3] - J[0] * J[5]);
3460       invJ[6] = idet * (J[3] * J[7] - J[4] * J[6]);
3461       invJ[7] = idet * (J[1] * J[6] - J[0] * J[7]);
3462       invJ[8] = idet * (J[0] * J[4] - J[1] * J[3]);
3463     } break;
3464     }
3465     for (l = 0; l < dimR; l++) {
3466       for (m = 0; m < dimC; m++) guess[l] += PetscRealPart(invJ[l * dimC + m]) * resNeg[m];
3467     }
3468   } else {
3469 #if defined(PETSC_USE_COMPLEX)
3470     char transpose = 'C';
3471 #else
3472     char transpose = 'T';
3473 #endif
3474     PetscBLASInt m, n, one = 1, worksize, info;
3475 
3476     PetscCall(PetscBLASIntCast(dimR, &m));
3477     PetscCall(PetscBLASIntCast(dimC, &n));
3478     PetscCall(PetscBLASIntCast(dimC * dimC, &worksize));
3479     for (l = 0; l < dimC; l++) invJ[l] = resNeg[l];
3480 
3481     PetscCallBLAS("LAPACKgels", LAPACKgels_(&transpose, &m, &n, &one, J, &m, invJ, &n, work, &worksize, &info));
3482     PetscCheck(info == 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "Bad argument to GELS %" PetscBLASInt_FMT, info);
3483 
3484     for (l = 0; l < dimR; l++) guess[l] += PetscRealPart(invJ[l]);
3485   }
3486   PetscFunctionReturn(PETSC_SUCCESS);
3487 }
3488 
3489 static PetscErrorCode DMPlexCoordinatesToReference_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[], Vec coords, PetscInt dimC, PetscInt dimR)
3490 {
3491   PetscInt     coordSize, i, j, k, l, m, maxIts = 7, numV = (1 << dimR);
3492   PetscScalar *coordsScalar = NULL;
3493   PetscReal   *cellData, *cellCoords, *cellCoeffs, *extJ, *resNeg;
3494   PetscScalar *J, *invJ, *work;
3495 
3496   PetscFunctionBegin;
3497   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3498   PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3499   PetscCheck(coordSize >= dimC * numV, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expecting at least %" PetscInt_FMT " coordinates, got %" PetscInt_FMT, dimC * (1 << dimR), coordSize);
3500   PetscCall(DMGetWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData));
3501   PetscCall(DMGetWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J));
3502   cellCoords = &cellData[0];
3503   cellCoeffs = &cellData[coordSize];
3504   extJ       = &cellData[2 * coordSize];
3505   resNeg     = &cellData[2 * coordSize + dimR];
3506   invJ       = &J[dimR * dimC];
3507   work       = &J[2 * dimR * dimC];
3508   if (dimR == 2) {
3509     const PetscInt zToPlex[4] = {0, 1, 3, 2};
3510 
3511     for (i = 0; i < 4; i++) {
3512       PetscInt plexI = zToPlex[i];
3513 
3514       for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3515     }
3516   } else if (dimR == 3) {
3517     const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
3518 
3519     for (i = 0; i < 8; i++) {
3520       PetscInt plexI = zToPlex[i];
3521 
3522       for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3523     }
3524   } else {
3525     for (i = 0; i < coordSize; i++) cellCoords[i] = PetscRealPart(coordsScalar[i]);
3526   }
3527   /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */
3528   for (i = 0; i < dimR; i++) {
3529     PetscReal *swap;
3530 
3531     for (j = 0; j < (numV / 2); j++) {
3532       for (k = 0; k < dimC; k++) {
3533         cellCoeffs[dimC * j + k]                = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]);
3534         cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]);
3535       }
3536     }
3537 
3538     if (i < dimR - 1) {
3539       swap       = cellCoeffs;
3540       cellCoeffs = cellCoords;
3541       cellCoords = swap;
3542     }
3543   }
3544   PetscCall(PetscArrayzero(refCoords, numPoints * dimR));
3545   for (j = 0; j < numPoints; j++) {
3546     for (i = 0; i < maxIts; i++) {
3547       PetscReal *guess = &refCoords[dimR * j];
3548 
3549       /* compute -residual and Jacobian */
3550       for (k = 0; k < dimC; k++) resNeg[k] = realCoords[dimC * j + k];
3551       for (k = 0; k < dimC * dimR; k++) J[k] = 0.;
3552       for (k = 0; k < numV; k++) {
3553         PetscReal extCoord = 1.;
3554         for (l = 0; l < dimR; l++) {
3555           PetscReal coord = guess[l];
3556           PetscInt  dep   = (k & (1 << l)) >> l;
3557 
3558           extCoord *= dep * coord + !dep;
3559           extJ[l] = dep;
3560 
3561           for (m = 0; m < dimR; m++) {
3562             PetscReal coord = guess[m];
3563             PetscInt  dep   = ((k & (1 << m)) >> m) && (m != l);
3564             PetscReal mult  = dep * coord + !dep;
3565 
3566             extJ[l] *= mult;
3567           }
3568         }
3569         for (l = 0; l < dimC; l++) {
3570           PetscReal coeff = cellCoeffs[dimC * k + l];
3571 
3572           resNeg[l] -= coeff * extCoord;
3573           for (m = 0; m < dimR; m++) J[dimR * l + m] += coeff * extJ[m];
3574         }
3575       }
3576       if (0 && PetscDefined(USE_DEBUG)) {
3577         PetscReal maxAbs = 0.;
3578 
3579         for (l = 0; l < dimC; l++) maxAbs = PetscMax(maxAbs, PetscAbsReal(resNeg[l]));
3580         PetscCall(PetscInfo(dm, "cell %" PetscInt_FMT ", point %" PetscInt_FMT ", iter %" PetscInt_FMT ": res %g\n", cell, j, i, (double)maxAbs));
3581       }
3582 
3583       PetscCall(DMPlexCoordinatesToReference_NewtonUpdate(dimC, dimR, J, invJ, work, resNeg, guess));
3584     }
3585   }
3586   PetscCall(DMRestoreWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J));
3587   PetscCall(DMRestoreWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData));
3588   PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3589   PetscFunctionReturn(PETSC_SUCCESS);
3590 }
3591 
3592 static PetscErrorCode DMPlexReferenceToCoordinates_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt dimC, PetscInt dimR)
3593 {
3594   PetscInt     coordSize, i, j, k, l, numV = (1 << dimR);
3595   PetscScalar *coordsScalar = NULL;
3596   PetscReal   *cellData, *cellCoords, *cellCoeffs;
3597 
3598   PetscFunctionBegin;
3599   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3600   PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3601   PetscCheck(coordSize >= dimC * numV, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expecting at least %" PetscInt_FMT " coordinates, got %" PetscInt_FMT, dimC * (1 << dimR), coordSize);
3602   PetscCall(DMGetWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData));
3603   cellCoords = &cellData[0];
3604   cellCoeffs = &cellData[coordSize];
3605   if (dimR == 2) {
3606     const PetscInt zToPlex[4] = {0, 1, 3, 2};
3607 
3608     for (i = 0; i < 4; i++) {
3609       PetscInt plexI = zToPlex[i];
3610 
3611       for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3612     }
3613   } else if (dimR == 3) {
3614     const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
3615 
3616     for (i = 0; i < 8; i++) {
3617       PetscInt plexI = zToPlex[i];
3618 
3619       for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3620     }
3621   } else {
3622     for (i = 0; i < coordSize; i++) cellCoords[i] = PetscRealPart(coordsScalar[i]);
3623   }
3624   /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */
3625   for (i = 0; i < dimR; i++) {
3626     PetscReal *swap;
3627 
3628     for (j = 0; j < (numV / 2); j++) {
3629       for (k = 0; k < dimC; k++) {
3630         cellCoeffs[dimC * j + k]                = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]);
3631         cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]);
3632       }
3633     }
3634 
3635     if (i < dimR - 1) {
3636       swap       = cellCoeffs;
3637       cellCoeffs = cellCoords;
3638       cellCoords = swap;
3639     }
3640   }
3641   PetscCall(PetscArrayzero(realCoords, numPoints * dimC));
3642   for (j = 0; j < numPoints; j++) {
3643     const PetscReal *guess  = &refCoords[dimR * j];
3644     PetscReal       *mapped = &realCoords[dimC * j];
3645 
3646     for (k = 0; k < numV; k++) {
3647       PetscReal extCoord = 1.;
3648       for (l = 0; l < dimR; l++) {
3649         PetscReal coord = guess[l];
3650         PetscInt  dep   = (k & (1 << l)) >> l;
3651 
3652         extCoord *= dep * coord + !dep;
3653       }
3654       for (l = 0; l < dimC; l++) {
3655         PetscReal coeff = cellCoeffs[dimC * k + l];
3656 
3657         mapped[l] += coeff * extCoord;
3658       }
3659     }
3660   }
3661   PetscCall(DMRestoreWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData));
3662   PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3663   PetscFunctionReturn(PETSC_SUCCESS);
3664 }
3665 
3666 PetscErrorCode DMPlexCoordinatesToReference_FE(DM dm, PetscFE fe, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[], Vec coords, PetscInt Nc, PetscInt dimR, PetscInt maxIter, PetscReal *tol)
3667 {
3668   PetscInt     numComp, pdim, i, j, k, l, m, coordSize;
3669   PetscScalar *nodes = NULL;
3670   PetscReal   *invV, *modes;
3671   PetscReal   *B, *D, *resNeg;
3672   PetscScalar *J, *invJ, *work;
3673   PetscReal    tolerance = tol == NULL ? 0.0 : *tol;
3674 
3675   PetscFunctionBegin;
3676   PetscCall(PetscFEGetDimension(fe, &pdim));
3677   PetscCall(PetscFEGetNumComponents(fe, &numComp));
3678   PetscCheck(numComp == Nc, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "coordinate discretization must have as many components (%" PetscInt_FMT ") as embedding dimension (!= %" PetscInt_FMT ")", numComp, Nc);
3679   /* we shouldn't apply inverse closure permutation, if one exists */
3680   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, coords, cell, 0, &coordSize, &nodes));
3681   /* convert nodes to values in the stable evaluation basis */
3682   PetscCall(DMGetWorkArray(dm, pdim, MPIU_REAL, &modes));
3683   invV = fe->invV;
3684   for (i = 0; i < pdim; ++i) {
3685     modes[i] = 0.;
3686     for (j = 0; j < pdim; ++j) modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]);
3687   }
3688   PetscCall(DMGetWorkArray(dm, pdim * Nc + pdim * Nc * dimR + Nc, MPIU_REAL, &B));
3689   D      = &B[pdim * Nc];
3690   resNeg = &D[pdim * Nc * dimR];
3691   PetscCall(DMGetWorkArray(dm, 3 * Nc * dimR, MPIU_SCALAR, &J));
3692   invJ = &J[Nc * dimR];
3693   work = &invJ[Nc * dimR];
3694   for (i = 0; i < numPoints * dimR; i++) refCoords[i] = 0.;
3695   for (j = 0; j < numPoints; j++) {
3696     PetscReal normPoint = DMPlex_NormD_Internal(Nc, &realCoords[j * Nc]);
3697     normPoint           = normPoint > PETSC_SMALL ? normPoint : 1.0;
3698     for (i = 0; i < maxIter; i++) { /* we could batch this so that we're not making big B and D arrays all the time */
3699       PetscReal *guess = &refCoords[j * dimR], error = 0;
3700       PetscCall(PetscSpaceEvaluate(fe->basisSpace, 1, guess, B, D, NULL));
3701       for (k = 0; k < Nc; k++) resNeg[k] = realCoords[j * Nc + k];
3702       for (k = 0; k < Nc * dimR; k++) J[k] = 0.;
3703       for (k = 0; k < pdim; k++) {
3704         for (l = 0; l < Nc; l++) {
3705           resNeg[l] -= modes[k] * B[k * Nc + l];
3706           for (m = 0; m < dimR; m++) J[l * dimR + m] += modes[k] * D[(k * Nc + l) * dimR + m];
3707         }
3708       }
3709       if (0 && PetscDefined(USE_DEBUG)) {
3710         PetscReal maxAbs = 0.;
3711 
3712         for (l = 0; l < Nc; l++) maxAbs = PetscMax(maxAbs, PetscAbsReal(resNeg[l]));
3713         PetscCall(PetscInfo(dm, "cell %" PetscInt_FMT ", point %" PetscInt_FMT ", iter %" PetscInt_FMT ": res %g\n", cell, j, i, (double)maxAbs));
3714       }
3715       error = DMPlex_NormD_Internal(Nc, resNeg);
3716       if (error < tolerance * normPoint) {
3717         if (tol) *tol = error / normPoint;
3718         break;
3719       }
3720       PetscCall(DMPlexCoordinatesToReference_NewtonUpdate(Nc, dimR, J, invJ, work, resNeg, guess));
3721     }
3722   }
3723   PetscCall(DMRestoreWorkArray(dm, 3 * Nc * dimR, MPIU_SCALAR, &J));
3724   PetscCall(DMRestoreWorkArray(dm, pdim * Nc + pdim * Nc * dimR + Nc, MPIU_REAL, &B));
3725   PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_REAL, &modes));
3726   PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes));
3727   PetscFunctionReturn(PETSC_SUCCESS);
3728 }
3729 
3730 /* TODO: TOBY please fix this for Nc > 1 */
3731 PetscErrorCode DMPlexReferenceToCoordinates_FE(DM dm, PetscFE fe, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt Nc, PetscInt dimR)
3732 {
3733   PetscInt     numComp, pdim, i, j, k, l, coordSize;
3734   PetscScalar *nodes = NULL;
3735   PetscReal   *invV, *modes;
3736   PetscReal   *B;
3737 
3738   PetscFunctionBegin;
3739   PetscCall(PetscFEGetDimension(fe, &pdim));
3740   PetscCall(PetscFEGetNumComponents(fe, &numComp));
3741   PetscCheck(numComp == Nc, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "coordinate discretization must have as many components (%" PetscInt_FMT ") as embedding dimension (!= %" PetscInt_FMT ")", numComp, Nc);
3742   /* we shouldn't apply inverse closure permutation, if one exists */
3743   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, coords, cell, 0, &coordSize, &nodes));
3744   /* convert nodes to values in the stable evaluation basis */
3745   PetscCall(DMGetWorkArray(dm, pdim, MPIU_REAL, &modes));
3746   invV = fe->invV;
3747   for (i = 0; i < pdim; ++i) {
3748     modes[i] = 0.;
3749     for (j = 0; j < pdim; ++j) modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]);
3750   }
3751   PetscCall(DMGetWorkArray(dm, numPoints * pdim * Nc, MPIU_REAL, &B));
3752   PetscCall(PetscSpaceEvaluate(fe->basisSpace, numPoints, refCoords, B, NULL, NULL));
3753   for (i = 0; i < numPoints * Nc; i++) realCoords[i] = 0.;
3754   for (j = 0; j < numPoints; j++) {
3755     PetscReal *mapped = &realCoords[j * Nc];
3756 
3757     for (k = 0; k < pdim; k++) {
3758       for (l = 0; l < Nc; l++) mapped[l] += modes[k] * B[(j * pdim + k) * Nc + l];
3759     }
3760   }
3761   PetscCall(DMRestoreWorkArray(dm, numPoints * pdim * Nc, MPIU_REAL, &B));
3762   PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_REAL, &modes));
3763   PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes));
3764   PetscFunctionReturn(PETSC_SUCCESS);
3765 }
3766 
3767 /*@
3768   DMPlexCoordinatesToReference - Pull coordinates back from the mesh to the reference element
3769   using a single element map.
3770 
3771   Not Collective
3772 
3773   Input Parameters:
3774 + dm         - The mesh, with coordinate maps defined either by a `PetscDS` for the coordinate `DM` (see `DMGetCoordinateDM()`) or
3775                implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or
3776                as a multilinear map for tensor-product elements
3777 . cell       - the cell whose map is used.
3778 . numPoints  - the number of points to locate
3779 - realCoords - (numPoints x coordinate dimension) array of coordinates (see `DMGetCoordinateDim()`)
3780 
3781   Output Parameter:
3782 . refCoords - (`numPoints` x `dimension`) array of reference coordinates (see `DMGetDimension()`)
3783 
3784   Level: intermediate
3785 
3786   Notes:
3787   This inversion will be accurate inside the reference element, but may be inaccurate for
3788   mappings that do not extend uniquely outside the reference cell (e.g, most non-affine maps)
3789 
3790 .seealso: `DMPLEX`, `DMPlexReferenceToCoordinates()`
3791 @*/
3792 PetscErrorCode DMPlexCoordinatesToReference(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[])
3793 {
3794   PetscInt dimC, dimR, depth, cStart, cEnd, i;
3795   DM       coordDM = NULL;
3796   Vec      coords;
3797   PetscFE  fe = NULL;
3798 
3799   PetscFunctionBegin;
3800   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3801   PetscCall(DMGetDimension(dm, &dimR));
3802   PetscCall(DMGetCoordinateDim(dm, &dimC));
3803   if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(PETSC_SUCCESS);
3804   PetscCall(DMPlexGetDepth(dm, &depth));
3805   PetscCall(DMGetCoordinatesLocal(dm, &coords));
3806   PetscCall(DMGetCoordinateDM(dm, &coordDM));
3807   if (coordDM) {
3808     PetscInt coordFields;
3809 
3810     PetscCall(DMGetNumFields(coordDM, &coordFields));
3811     if (coordFields) {
3812       PetscClassId id;
3813       PetscObject  disc;
3814 
3815       PetscCall(DMGetField(coordDM, 0, NULL, &disc));
3816       PetscCall(PetscObjectGetClassId(disc, &id));
3817       if (id == PETSCFE_CLASSID) fe = (PetscFE)disc;
3818     }
3819   }
3820   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
3821   PetscCheck(cell >= cStart && cell < cEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "point %" PetscInt_FMT " not in cell range [%" PetscInt_FMT ",%" PetscInt_FMT ")", cell, cStart, cEnd);
3822   if (!fe) { /* implicit discretization: affine or multilinear */
3823     PetscInt  coneSize;
3824     PetscBool isSimplex, isTensor;
3825 
3826     PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
3827     isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE;
3828     isTensor  = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE;
3829     if (isSimplex) {
3830       PetscReal detJ, *v0, *J, *invJ;
3831 
3832       PetscCall(DMGetWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3833       J    = &v0[dimC];
3834       invJ = &J[dimC * dimC];
3835       PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, invJ, &detJ));
3836       for (i = 0; i < numPoints; i++) { /* Apply the inverse affine transformation for each point */
3837         const PetscReal x0[3] = {-1., -1., -1.};
3838 
3839         CoordinatesRealToRef(dimC, dimR, x0, v0, invJ, &realCoords[dimC * i], &refCoords[dimR * i]);
3840       }
3841       PetscCall(DMRestoreWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3842     } else if (isTensor) {
3843       PetscCall(DMPlexCoordinatesToReference_Tensor(coordDM, cell, numPoints, realCoords, refCoords, coords, dimC, dimR));
3844     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unrecognized cone size %" PetscInt_FMT, coneSize);
3845   } else {
3846     PetscCall(DMPlexCoordinatesToReference_FE(coordDM, fe, cell, numPoints, realCoords, refCoords, coords, dimC, dimR, 7, NULL));
3847   }
3848   PetscFunctionReturn(PETSC_SUCCESS);
3849 }
3850 
3851 /*@
3852   DMPlexReferenceToCoordinates - Map references coordinates to coordinates in the mesh for a single element map.
3853 
3854   Not Collective
3855 
3856   Input Parameters:
3857 + dm        - The mesh, with coordinate maps defined either by a PetscDS for the coordinate `DM` (see `DMGetCoordinateDM()`) or
3858                implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or
3859                as a multilinear map for tensor-product elements
3860 . cell      - the cell whose map is used.
3861 . numPoints - the number of points to locate
3862 - refCoords - (numPoints x dimension) array of reference coordinates (see `DMGetDimension()`)
3863 
3864   Output Parameter:
3865 . realCoords - (numPoints x coordinate dimension) array of coordinates (see `DMGetCoordinateDim()`)
3866 
3867   Level: intermediate
3868 
3869 .seealso: `DMPLEX`, `DMPlexCoordinatesToReference()`
3870 @*/
3871 PetscErrorCode DMPlexReferenceToCoordinates(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[])
3872 {
3873   PetscInt dimC, dimR, depth, cStart, cEnd, i;
3874   DM       coordDM = NULL;
3875   Vec      coords;
3876   PetscFE  fe = NULL;
3877 
3878   PetscFunctionBegin;
3879   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3880   PetscCall(DMGetDimension(dm, &dimR));
3881   PetscCall(DMGetCoordinateDim(dm, &dimC));
3882   if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(PETSC_SUCCESS);
3883   PetscCall(DMPlexGetDepth(dm, &depth));
3884   PetscCall(DMGetCoordinatesLocal(dm, &coords));
3885   PetscCall(DMGetCoordinateDM(dm, &coordDM));
3886   if (coordDM) {
3887     PetscInt coordFields;
3888 
3889     PetscCall(DMGetNumFields(coordDM, &coordFields));
3890     if (coordFields) {
3891       PetscClassId id;
3892       PetscObject  disc;
3893 
3894       PetscCall(DMGetField(coordDM, 0, NULL, &disc));
3895       PetscCall(PetscObjectGetClassId(disc, &id));
3896       if (id == PETSCFE_CLASSID) fe = (PetscFE)disc;
3897     }
3898   }
3899   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
3900   PetscCheck(cell >= cStart && cell < cEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "point %" PetscInt_FMT " not in cell range [%" PetscInt_FMT ",%" PetscInt_FMT ")", cell, cStart, cEnd);
3901   if (!fe) { /* implicit discretization: affine or multilinear */
3902     PetscInt  coneSize;
3903     PetscBool isSimplex, isTensor;
3904 
3905     PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
3906     isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE;
3907     isTensor  = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE;
3908     if (isSimplex) {
3909       PetscReal detJ, *v0, *J;
3910 
3911       PetscCall(DMGetWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3912       J = &v0[dimC];
3913       PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, NULL, &detJ));
3914       for (i = 0; i < numPoints; i++) { /* Apply the affine transformation for each point */
3915         const PetscReal xi0[3] = {-1., -1., -1.};
3916 
3917         CoordinatesRefToReal(dimC, dimR, xi0, v0, J, &refCoords[dimR * i], &realCoords[dimC * i]);
3918       }
3919       PetscCall(DMRestoreWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3920     } else if (isTensor) {
3921       PetscCall(DMPlexReferenceToCoordinates_Tensor(coordDM, cell, numPoints, refCoords, realCoords, coords, dimC, dimR));
3922     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unrecognized cone size %" PetscInt_FMT, coneSize);
3923   } else {
3924     PetscCall(DMPlexReferenceToCoordinates_FE(coordDM, fe, cell, numPoints, refCoords, realCoords, coords, dimC, dimR));
3925   }
3926   PetscFunctionReturn(PETSC_SUCCESS);
3927 }
3928 
3929 void coordMap_identity(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, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
3930 {
3931   const PetscInt Nc = uOff[1] - uOff[0];
3932   PetscInt       c;
3933 
3934   for (c = 0; c < Nc; ++c) f0[c] = u[c];
3935 }
3936 
3937 /* Shear applies the transformation, assuming we fix z,
3938   / 1  0  m_0 \
3939   | 0  1  m_1 |
3940   \ 0  0   1  /
3941 */
3942 void coordMap_shear(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, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar coords[])
3943 {
3944   const PetscInt Nc = uOff[1] - uOff[0];
3945   const PetscInt ax = (PetscInt)PetscRealPart(constants[0]);
3946   PetscInt       c;
3947 
3948   for (c = 0; c < Nc; ++c) coords[c] = u[c] + constants[c + 1] * u[ax];
3949 }
3950 
3951 /* Flare applies the transformation, assuming we fix x_f,
3952 
3953    x_i = x_i * alpha_i x_f
3954 */
3955 void coordMap_flare(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, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar coords[])
3956 {
3957   const PetscInt Nc = uOff[1] - uOff[0];
3958   const PetscInt cf = (PetscInt)PetscRealPart(constants[0]);
3959   PetscInt       c;
3960 
3961   for (c = 0; c < Nc; ++c) coords[c] = u[c] * (c == cf ? 1.0 : constants[c + 1] * u[cf]);
3962 }
3963 
3964 /*
3965   We would like to map the unit square to a quarter of the annulus between circles of radius 1 and 2. We start by mapping the straight sections, which
3966   will correspond to the top and bottom of our square. So
3967 
3968     (0,0)--(1,0)  ==>  (1,0)--(2,0)      Just a shift of (1,0)
3969     (0,1)--(1,1)  ==>  (0,1)--(0,2)      Switch x and y
3970 
3971   So it looks like we want to map each layer in y to a ray, so x is the radius and y is the angle:
3972 
3973     (x, y)  ==>  (x+1, \pi/2 y)                           in (r', \theta') space
3974             ==>  ((x+1) cos(\pi/2 y), (x+1) sin(\pi/2 y)) in (x', y') space
3975 */
3976 void coordMap_annulus(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, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar xp[])
3977 {
3978   const PetscReal ri = PetscRealPart(constants[0]);
3979   const PetscReal ro = PetscRealPart(constants[1]);
3980 
3981   xp[0] = (x[0] * (ro - ri) + ri) * PetscCosReal(0.5 * PETSC_PI * x[1]);
3982   xp[1] = (x[0] * (ro - ri) + ri) * PetscSinReal(0.5 * PETSC_PI * x[1]);
3983 }
3984 
3985 /*
3986   We would like to map the unit cube to a hemisphere of the spherical shell between balls of radius 1 and 2. We want to map the bottom surface onto the
3987   lower hemisphere and the upper surface onto the top, letting z be the radius.
3988 
3989     (x, y)  ==>  ((z+3)/2, \pi/2 (|x| or |y|), arctan y/x)                                                  in (r', \theta', \phi') space
3990             ==>  ((z+3)/2 \cos(\theta') cos(\phi'), (z+3)/2 \cos(\theta') sin(\phi'), (z+3)/2 sin(\theta')) in (x', y', z') space
3991 */
3992 void coordMap_shell(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, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar xp[])
3993 {
3994   const PetscReal pi4    = PETSC_PI / 4.0;
3995   const PetscReal ri     = PetscRealPart(constants[0]);
3996   const PetscReal ro     = PetscRealPart(constants[1]);
3997   const PetscReal rp     = (x[2] + 1) * 0.5 * (ro - ri) + ri;
3998   const PetscReal phip   = PetscAtan2Real(x[1], x[0]);
3999   const PetscReal thetap = 0.5 * PETSC_PI * (1.0 - ((((phip <= pi4) && (phip >= -pi4)) || ((phip >= 3.0 * pi4) || (phip <= -3.0 * pi4))) ? PetscAbsReal(x[0]) : PetscAbsReal(x[1])));
4000 
4001   xp[0] = rp * PetscCosReal(thetap) * PetscCosReal(phip);
4002   xp[1] = rp * PetscCosReal(thetap) * PetscSinReal(phip);
4003   xp[2] = rp * PetscSinReal(thetap);
4004 }
4005 
4006 /*@C
4007   DMPlexRemapGeometry - This function maps the original `DM` coordinates to new coordinates.
4008 
4009   Not Collective
4010 
4011   Input Parameters:
4012 + dm   - The `DM`
4013 . time - The time
4014 - func - The function transforming current coordinates to new coordinates
4015 
4016   Calling sequence of `func`:
4017 + dim          - The spatial dimension
4018 . Nf           - The number of input fields (here 1)
4019 . NfAux        - The number of input auxiliary fields
4020 . uOff         - The offset of the coordinates in u[] (here 0)
4021 . uOff_x       - The offset of the coordinates in u_x[] (here 0)
4022 . u            - The coordinate values at this point in space
4023 . u_t          - The coordinate time derivative at this point in space (here `NULL`)
4024 . u_x          - The coordinate derivatives at this point in space
4025 . aOff         - The offset of each auxiliary field in u[]
4026 . aOff_x       - The offset of each auxiliary field in u_x[]
4027 . a            - The auxiliary field values at this point in space
4028 . a_t          - The auxiliary field time derivative at this point in space (or `NULL`)
4029 . a_x          - The auxiliary field derivatives at this point in space
4030 . t            - The current time
4031 . x            - The coordinates of this point (here not used)
4032 . numConstants - The number of constants
4033 . constants    - The value of each constant
4034 - f            - The new coordinates at this point in space
4035 
4036   Level: intermediate
4037 
4038 .seealso: `DMPLEX`, `DMGetCoordinates()`, `DMGetCoordinatesLocal()`, `DMGetCoordinateDM()`, `DMProjectFieldLocal()`, `DMProjectFieldLabelLocal()`
4039 @*/
4040 PetscErrorCode DMPlexRemapGeometry(DM dm, PetscReal time, void (*func)(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, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f[]))
4041 {
4042   DM           cdm;
4043   PetscDS      cds;
4044   DMField      cf;
4045   PetscObject  obj;
4046   PetscClassId id;
4047   Vec          lCoords, tmpCoords;
4048 
4049   PetscFunctionBegin;
4050   PetscCall(DMGetCoordinateDM(dm, &cdm));
4051   PetscCall(DMGetCoordinatesLocal(dm, &lCoords));
4052   PetscCall(DMGetDS(cdm, &cds));
4053   PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
4054   PetscCall(PetscObjectGetClassId(obj, &id));
4055   if (id != PETSCFE_CLASSID) {
4056     PetscSection       cSection;
4057     const PetscScalar *constants;
4058     PetscScalar       *coords, f[16];
4059     PetscInt           dim, cdim, Nc, vStart, vEnd;
4060 
4061     PetscCall(DMGetDimension(dm, &dim));
4062     PetscCall(DMGetCoordinateDim(dm, &cdim));
4063     PetscCheck(cdim <= 16, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Affine version of DMPlexRemapGeometry is currently limited to dimensions <= 16, not %" PetscInt_FMT, cdim);
4064     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
4065     PetscCall(DMGetCoordinateSection(dm, &cSection));
4066     PetscCall(PetscDSGetConstants(cds, &Nc, &constants));
4067     PetscCall(VecGetArrayWrite(lCoords, &coords));
4068     for (PetscInt v = vStart; v < vEnd; ++v) {
4069       PetscInt uOff[2] = {0, cdim};
4070       PetscInt off, c;
4071 
4072       PetscCall(PetscSectionGetOffset(cSection, v, &off));
4073       (*func)(dim, 1, 0, uOff, NULL, &coords[off], NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0.0, NULL, Nc, constants, f);
4074       for (c = 0; c < cdim; ++c) coords[off + c] = f[c];
4075     }
4076     PetscCall(VecRestoreArrayWrite(lCoords, &coords));
4077   } else {
4078     PetscCall(DMGetLocalVector(cdm, &tmpCoords));
4079     PetscCall(VecCopy(lCoords, tmpCoords));
4080     /* We have to do the coordinate field manually right now since the coordinate DM will not have its own */
4081     PetscCall(DMGetCoordinateField(dm, &cf));
4082     cdm->coordinates[0].field = cf;
4083     PetscCall(DMProjectFieldLocal(cdm, time, tmpCoords, &func, INSERT_VALUES, lCoords));
4084     cdm->coordinates[0].field = NULL;
4085     PetscCall(DMRestoreLocalVector(cdm, &tmpCoords));
4086     PetscCall(DMSetCoordinatesLocal(dm, lCoords));
4087   }
4088   PetscFunctionReturn(PETSC_SUCCESS);
4089 }
4090 
4091 /*@
4092   DMPlexShearGeometry - This shears the domain, meaning adds a multiple of the shear coordinate to all other coordinates.
4093 
4094   Not Collective
4095 
4096   Input Parameters:
4097 + dm          - The `DMPLEX`
4098 . direction   - The shear coordinate direction, e.g. `DM_X` is the x-axis
4099 - multipliers - The multiplier m for each direction which is not the shear direction
4100 
4101   Level: intermediate
4102 
4103 .seealso: `DMPLEX`, `DMPlexRemapGeometry()`, `DMDirection`, `DM_X`, `DM_Y`, `DM_Z`
4104 @*/
4105 PetscErrorCode DMPlexShearGeometry(DM dm, DMDirection direction, PetscReal multipliers[])
4106 {
4107   DM             cdm;
4108   PetscDS        cds;
4109   PetscScalar   *moduli;
4110   const PetscInt dir = (PetscInt)direction;
4111   PetscInt       dE, d, e;
4112 
4113   PetscFunctionBegin;
4114   PetscCall(DMGetCoordinateDM(dm, &cdm));
4115   PetscCall(DMGetCoordinateDim(dm, &dE));
4116   PetscCall(PetscMalloc1(dE + 1, &moduli));
4117   moduli[0] = dir;
4118   for (d = 0, e = 0; d < dE; ++d) moduli[d + 1] = d == dir ? 0.0 : (multipliers ? multipliers[e++] : 1.0);
4119   PetscCall(DMGetDS(cdm, &cds));
4120   PetscCall(PetscDSSetConstants(cds, dE + 1, moduli));
4121   PetscCall(DMPlexRemapGeometry(dm, 0.0, coordMap_shear));
4122   PetscCall(PetscFree(moduli));
4123   PetscFunctionReturn(PETSC_SUCCESS);
4124 }
4125