xref: /petsc/src/dm/impls/plex/plexgeometry.c (revision 8033637c19e0291e758defb3f4102ce2714e6010)
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", "sinusoid", "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(cdim == T->Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Nc %" PetscInt_FMT " != %" PetscInt_FMT, cdim, T->Nc);
2526     PetscAssert(dim == T->cdim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "cdim %" PetscInt_FMT " != %" PetscInt_FMT, dim, 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. This is a
2593          one-dimensional array of size $cdim * Nq$ where $cdim$ is the dimension of the `DM` coordinate space and $Nq$ is the number of quadrature points
2594 . J    - the Jacobian of the transform from the reference element at each quadrature point. This is a one-dimensional array of size $Nq * cdim * cdim$ containing
2595          each Jacobian in column-major order.
2596 . invJ - the inverse of the Jacobian at each quadrature point. This is a one-dimensional array of size $Nq * cdim * cdim$ containing
2597          each inverse Jacobian in column-major order.
2598 - detJ - the Jacobian determinant at each quadrature point. This is a one-dimensional array of size $Nq$.
2599 
2600   Level: advanced
2601 
2602   Note:
2603   Implicit cell geometry must be used when the topological mesh dimension is not equal to the coordinate dimension, for instance for embedded manifolds.
2604 
2605 .seealso: `DMPLEX`, `DMGetCoordinateSection()`, `DMGetCoordinates()`
2606 @*/
2607 PetscErrorCode DMPlexComputeCellGeometryFEM(DM dm, PetscInt cell, PetscQuadrature quad, PetscReal v[], PetscReal J[], PetscReal invJ[], PetscReal detJ[])
2608 {
2609   DM       cdm;
2610   PetscFE  fe = NULL;
2611   PetscInt dim, cdim;
2612 
2613   PetscFunctionBegin;
2614   PetscAssertPointer(detJ, 7);
2615   PetscCall(DMGetDimension(dm, &dim));
2616   PetscCall(DMGetCoordinateDim(dm, &cdim));
2617   PetscCall(DMGetCoordinateDM(dm, &cdm));
2618   if (cdm) {
2619     PetscClassId id;
2620     PetscInt     numFields;
2621     PetscDS      prob;
2622     PetscObject  disc;
2623 
2624     PetscCall(DMGetNumFields(cdm, &numFields));
2625     if (numFields) {
2626       PetscCall(DMGetDS(cdm, &prob));
2627       PetscCall(PetscDSGetDiscretization(prob, 0, &disc));
2628       PetscCall(PetscObjectGetClassId(disc, &id));
2629       if (id == PETSCFE_CLASSID) fe = (PetscFE)disc;
2630     }
2631   }
2632   if (!fe || (dim != cdim)) PetscCall(DMPlexComputeCellGeometryFEM_Implicit(dm, cell, quad, v, J, invJ, detJ));
2633   else PetscCall(DMPlexComputeCellGeometryFEM_FE(dm, fe, cell, quad, v, J, invJ, detJ));
2634   PetscFunctionReturn(PETSC_SUCCESS);
2635 }
2636 
2637 static PetscErrorCode DMPlexComputeGeometryFVM_0D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2638 {
2639   PetscSection       coordSection;
2640   Vec                coordinates;
2641   const PetscScalar *coords = NULL;
2642   PetscInt           d, dof, off;
2643 
2644   PetscFunctionBegin;
2645   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
2646   PetscCall(DMGetCoordinateSection(dm, &coordSection));
2647   PetscCall(VecGetArrayRead(coordinates, &coords));
2648 
2649   /* for a point the centroid is just the coord */
2650   if (centroid) {
2651     PetscCall(PetscSectionGetDof(coordSection, cell, &dof));
2652     PetscCall(PetscSectionGetOffset(coordSection, cell, &off));
2653     for (d = 0; d < dof; d++) centroid[d] = PetscRealPart(coords[off + d]);
2654   }
2655   if (normal) {
2656     const PetscInt *support, *cones;
2657     PetscInt        supportSize;
2658     PetscReal       norm, sign;
2659 
2660     /* compute the norm based upon the support centroids */
2661     PetscCall(DMPlexGetSupportSize(dm, cell, &supportSize));
2662     PetscCall(DMPlexGetSupport(dm, cell, &support));
2663     PetscCall(DMPlexComputeCellGeometryFVM(dm, support[0], NULL, normal, NULL));
2664 
2665     /* Take the normal from the centroid of the support to the vertex*/
2666     PetscCall(PetscSectionGetDof(coordSection, cell, &dof));
2667     PetscCall(PetscSectionGetOffset(coordSection, cell, &off));
2668     for (d = 0; d < dof; d++) normal[d] -= PetscRealPart(coords[off + d]);
2669 
2670     /* Determine the sign of the normal based upon its location in the support */
2671     PetscCall(DMPlexGetCone(dm, support[0], &cones));
2672     sign = cones[0] == cell ? 1.0 : -1.0;
2673 
2674     norm = DMPlex_NormD_Internal(dim, normal);
2675     for (d = 0; d < dim; ++d) normal[d] /= (norm * sign);
2676   }
2677   if (vol) *vol = 1.0;
2678   PetscCall(VecRestoreArrayRead(coordinates, &coords));
2679   PetscFunctionReturn(PETSC_SUCCESS);
2680 }
2681 
2682 static PetscErrorCode DMPlexComputeGeometryFVM_1D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2683 {
2684   const PetscScalar *array;
2685   PetscScalar       *coords = NULL;
2686   PetscInt           cdim, coordSize, d;
2687   PetscBool          isDG;
2688 
2689   PetscFunctionBegin;
2690   PetscCall(DMGetCoordinateDim(dm, &cdim));
2691   PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2692   PetscCheck(coordSize == cdim * 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge has %" PetscInt_FMT " coordinates != %" PetscInt_FMT, coordSize, cdim * 2);
2693   if (centroid) {
2694     for (d = 0; d < cdim; ++d) centroid[d] = 0.5 * PetscRealPart(coords[d] + coords[cdim + d]);
2695   }
2696   if (normal) {
2697     PetscReal norm;
2698 
2699     switch (cdim) {
2700     case 3:
2701       normal[2] = 0.; /* fall through */
2702     case 2:
2703       normal[0] = -PetscRealPart(coords[1] - coords[cdim + 1]);
2704       normal[1] = PetscRealPart(coords[0] - coords[cdim + 0]);
2705       break;
2706     case 1:
2707       normal[0] = 1.0;
2708       break;
2709     default:
2710       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Dimension %" PetscInt_FMT " not supported", cdim);
2711     }
2712     norm = DMPlex_NormD_Internal(cdim, normal);
2713     for (d = 0; d < cdim; ++d) normal[d] /= norm;
2714   }
2715   if (vol) {
2716     *vol = 0.0;
2717     for (d = 0; d < cdim; ++d) *vol += PetscSqr(PetscRealPart(coords[d] - coords[cdim + d]));
2718     *vol = PetscSqrtReal(*vol);
2719   }
2720   PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2721   PetscFunctionReturn(PETSC_SUCCESS);
2722 }
2723 
2724 /* Centroid_i = (\sum_n A_n Cn_i) / A */
2725 static PetscErrorCode DMPlexComputeGeometryFVM_2D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2726 {
2727   DMPolytopeType     ct;
2728   const PetscScalar *array;
2729   PetscScalar       *coords = NULL;
2730   PetscInt           coordSize;
2731   PetscBool          isDG;
2732   PetscInt           fv[4] = {0, 1, 2, 3};
2733   PetscInt           cdim, numCorners, p, d;
2734 
2735   PetscFunctionBegin;
2736   /* Must check for hybrid cells because prisms have a different orientation scheme */
2737   PetscCall(DMPlexGetCellType(dm, cell, &ct));
2738   switch (ct) {
2739   case DM_POLYTOPE_SEG_PRISM_TENSOR:
2740     fv[2] = 3;
2741     fv[3] = 2;
2742     break;
2743   default:
2744     break;
2745   }
2746   PetscCall(DMGetCoordinateDim(dm, &cdim));
2747   PetscCall(DMPlexGetConeSize(dm, cell, &numCorners));
2748   PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2749   {
2750     PetscReal c[3] = {0., 0., 0.}, n[3] = {0., 0., 0.}, origin[3] = {0., 0., 0.}, norm;
2751 
2752     for (d = 0; d < cdim; d++) origin[d] = PetscRealPart(coords[d]);
2753     for (p = 0; p < numCorners - 2; ++p) {
2754       PetscReal e0[3] = {0., 0., 0.}, e1[3] = {0., 0., 0.};
2755       for (d = 0; d < cdim; d++) {
2756         e0[d] = PetscRealPart(coords[cdim * fv[p + 1] + d]) - origin[d];
2757         e1[d] = PetscRealPart(coords[cdim * fv[p + 2] + d]) - origin[d];
2758       }
2759       const PetscReal dx = e0[1] * e1[2] - e0[2] * e1[1];
2760       const PetscReal dy = e0[2] * e1[0] - e0[0] * e1[2];
2761       const PetscReal dz = e0[0] * e1[1] - e0[1] * e1[0];
2762       const PetscReal a  = PetscSqrtReal(dx * dx + dy * dy + dz * dz);
2763 
2764       n[0] += dx;
2765       n[1] += dy;
2766       n[2] += dz;
2767       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.;
2768     }
2769     norm = PetscSqrtReal(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
2770     // Allow zero volume cells
2771     if (norm != 0) {
2772       n[0] /= norm;
2773       n[1] /= norm;
2774       n[2] /= norm;
2775       c[0] /= norm;
2776       c[1] /= norm;
2777       c[2] /= norm;
2778     }
2779     if (vol) *vol = 0.5 * norm;
2780     if (centroid)
2781       for (d = 0; d < cdim; ++d) centroid[d] = c[d];
2782     if (normal)
2783       for (d = 0; d < cdim; ++d) normal[d] = n[d];
2784   }
2785   PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2786   PetscFunctionReturn(PETSC_SUCCESS);
2787 }
2788 
2789 /* Centroid_i = (\sum_n V_n Cn_i) / V */
2790 static PetscErrorCode DMPlexComputeGeometryFVM_3D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2791 {
2792   DMPolytopeType        ct;
2793   const PetscScalar    *array;
2794   PetscScalar          *coords = NULL;
2795   PetscInt              coordSize;
2796   PetscBool             isDG;
2797   PetscReal             vsum      = 0.0, vtmp, coordsTmp[3 * 3], origin[3];
2798   const PetscInt        order[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
2799   const PetscInt       *cone, *faceSizes, *faces;
2800   const DMPolytopeType *faceTypes;
2801   PetscBool             isHybrid = PETSC_FALSE;
2802   PetscInt              numFaces, f, fOff = 0, p, d;
2803 
2804   PetscFunctionBegin;
2805   PetscCheck(dim <= 3, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No support for dim %" PetscInt_FMT " > 3", dim);
2806   /* Must check for hybrid cells because prisms have a different orientation scheme */
2807   PetscCall(DMPlexGetCellType(dm, cell, &ct));
2808   switch (ct) {
2809   case DM_POLYTOPE_POINT_PRISM_TENSOR:
2810   case DM_POLYTOPE_SEG_PRISM_TENSOR:
2811   case DM_POLYTOPE_TRI_PRISM_TENSOR:
2812   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
2813     isHybrid = PETSC_TRUE;
2814   default:
2815     break;
2816   }
2817 
2818   if (centroid)
2819     for (d = 0; d < dim; ++d) centroid[d] = 0.0;
2820   PetscCall(DMPlexGetCone(dm, cell, &cone));
2821 
2822   // Using the closure of faces for coordinates does not work in periodic geometries, so we index into the cell coordinates
2823   PetscCall(DMPlexGetRawFaces_Internal(dm, ct, order, &numFaces, &faceTypes, &faceSizes, &faces));
2824   PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2825   for (f = 0; f < numFaces; ++f) {
2826     PetscBool flip = isHybrid && f == 0 ? PETSC_TRUE : PETSC_FALSE; /* The first hybrid face is reversed */
2827 
2828     // If using zero as the origin vertex for each tetrahedron, an element far from the origin will have positive and
2829     // negative volumes that nearly cancel, thus incurring rounding error. Here we define origin[] as the first vertex
2830     // so that all tetrahedra have positive volume.
2831     if (f == 0)
2832       for (d = 0; d < dim; d++) origin[d] = PetscRealPart(coords[d]);
2833     switch (faceTypes[f]) {
2834     case DM_POLYTOPE_TRIANGLE:
2835       for (d = 0; d < dim; ++d) {
2836         coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + 0] * dim + d]) - origin[d];
2837         coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + 1] * dim + d]) - origin[d];
2838         coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + 2] * dim + d]) - origin[d];
2839       }
2840       Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2841       if (flip) vtmp = -vtmp;
2842       vsum += vtmp;
2843       if (centroid) { /* Centroid of OABC = (a+b+c)/4 */
2844         for (d = 0; d < dim; ++d) {
2845           for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp;
2846         }
2847       }
2848       break;
2849     case DM_POLYTOPE_QUADRILATERAL:
2850     case DM_POLYTOPE_SEG_PRISM_TENSOR: {
2851       PetscInt fv[4] = {0, 1, 2, 3};
2852 
2853       /* Side faces for hybrid cells are stored as tensor products */
2854       if (isHybrid && f > 1) {
2855         fv[2] = 3;
2856         fv[3] = 2;
2857       }
2858       /* DO FOR PYRAMID */
2859       /* First tet */
2860       for (d = 0; d < dim; ++d) {
2861         coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + fv[0]] * dim + d]) - origin[d];
2862         coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + fv[1]] * dim + d]) - origin[d];
2863         coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + fv[3]] * dim + d]) - origin[d];
2864       }
2865       Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2866       if (flip) vtmp = -vtmp;
2867       vsum += vtmp;
2868       if (centroid) {
2869         for (d = 0; d < dim; ++d) {
2870           for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp;
2871         }
2872       }
2873       /* Second tet */
2874       for (d = 0; d < dim; ++d) {
2875         coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + fv[1]] * dim + d]) - origin[d];
2876         coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + fv[2]] * dim + d]) - origin[d];
2877         coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + fv[3]] * dim + d]) - origin[d];
2878       }
2879       Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2880       if (flip) vtmp = -vtmp;
2881       vsum += vtmp;
2882       if (centroid) {
2883         for (d = 0; d < dim; ++d) {
2884           for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp;
2885         }
2886       }
2887       break;
2888     }
2889     default:
2890       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle face %" PetscInt_FMT " of type %s", cone[f], DMPolytopeTypes[ct]);
2891     }
2892     fOff += faceSizes[f];
2893   }
2894   PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, order, &numFaces, &faceTypes, &faceSizes, &faces));
2895   PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2896   if (vol) *vol = PetscAbsReal(vsum);
2897   if (normal)
2898     for (d = 0; d < dim; ++d) normal[d] = 0.0;
2899   if (centroid)
2900     for (d = 0; d < dim; ++d) centroid[d] = centroid[d] / (vsum * 4) + origin[d];
2901   PetscFunctionReturn(PETSC_SUCCESS);
2902 }
2903 
2904 /*@C
2905   DMPlexComputeCellGeometryFVM - Compute the volume for a given cell
2906 
2907   Collective
2908 
2909   Input Parameters:
2910 + dm   - the `DMPLEX`
2911 - cell - the cell
2912 
2913   Output Parameters:
2914 + vol      - the cell volume
2915 . centroid - the cell centroid
2916 - normal   - the cell normal, if appropriate
2917 
2918   Level: advanced
2919 
2920 .seealso: `DMPLEX`, `DMGetCoordinateSection()`, `DMGetCoordinates()`
2921 @*/
2922 PetscErrorCode DMPlexComputeCellGeometryFVM(DM dm, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2923 {
2924   PetscInt depth, dim;
2925 
2926   PetscFunctionBegin;
2927   PetscCall(DMPlexGetDepth(dm, &depth));
2928   PetscCall(DMGetDimension(dm, &dim));
2929   PetscCheck(depth == dim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh must be interpolated");
2930   PetscCall(DMPlexGetPointDepth(dm, cell, &depth));
2931   switch (depth) {
2932   case 0:
2933     PetscCall(DMPlexComputeGeometryFVM_0D_Internal(dm, dim, cell, vol, centroid, normal));
2934     break;
2935   case 1:
2936     PetscCall(DMPlexComputeGeometryFVM_1D_Internal(dm, dim, cell, vol, centroid, normal));
2937     break;
2938   case 2:
2939     PetscCall(DMPlexComputeGeometryFVM_2D_Internal(dm, dim, cell, vol, centroid, normal));
2940     break;
2941   case 3:
2942     PetscCall(DMPlexComputeGeometryFVM_3D_Internal(dm, dim, cell, vol, centroid, normal));
2943     break;
2944   default:
2945     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unsupported dimension %" PetscInt_FMT " (depth %" PetscInt_FMT ") for element geometry computation", dim, depth);
2946   }
2947   PetscFunctionReturn(PETSC_SUCCESS);
2948 }
2949 
2950 /*@
2951   DMPlexComputeGeometryFVM - Computes the cell and face geometry for a finite volume method
2952 
2953   Input Parameter:
2954 . dm - The `DMPLEX`
2955 
2956   Output Parameters:
2957 + cellgeom - A `Vec` of `PetscFVCellGeom` data
2958 - facegeom - A `Vec` of `PetscFVFaceGeom` data
2959 
2960   Level: developer
2961 
2962 .seealso: `DMPLEX`, `PetscFVFaceGeom`, `PetscFVCellGeom`
2963 @*/
2964 PetscErrorCode DMPlexComputeGeometryFVM(DM dm, Vec *cellgeom, Vec *facegeom)
2965 {
2966   DM           dmFace, dmCell;
2967   DMLabel      ghostLabel;
2968   PetscSection sectionFace, sectionCell;
2969   PetscSection coordSection;
2970   Vec          coordinates;
2971   PetscScalar *fgeom, *cgeom;
2972   PetscReal    minradius, gminradius;
2973   PetscInt     dim, cStart, cEnd, cEndInterior, c, fStart, fEnd, f;
2974 
2975   PetscFunctionBegin;
2976   PetscCall(DMGetDimension(dm, &dim));
2977   PetscCall(DMGetCoordinateSection(dm, &coordSection));
2978   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
2979   /* Make cell centroids and volumes */
2980   PetscCall(DMClone(dm, &dmCell));
2981   PetscCall(DMSetCoordinateSection(dmCell, PETSC_DETERMINE, coordSection));
2982   PetscCall(DMSetCoordinatesLocal(dmCell, coordinates));
2983   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &sectionCell));
2984   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2985   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
2986   PetscCall(PetscSectionSetChart(sectionCell, cStart, cEnd));
2987   for (c = cStart; c < cEnd; ++c) PetscCall(PetscSectionSetDof(sectionCell, c, (PetscInt)PetscCeilReal(((PetscReal)sizeof(PetscFVCellGeom)) / sizeof(PetscScalar))));
2988   PetscCall(PetscSectionSetUp(sectionCell));
2989   PetscCall(DMSetLocalSection(dmCell, sectionCell));
2990   PetscCall(PetscSectionDestroy(&sectionCell));
2991   PetscCall(DMCreateLocalVector(dmCell, cellgeom));
2992   if (cEndInterior < 0) cEndInterior = cEnd;
2993   PetscCall(VecGetArray(*cellgeom, &cgeom));
2994   for (c = cStart; c < cEndInterior; ++c) {
2995     PetscFVCellGeom *cg;
2996 
2997     PetscCall(DMPlexPointLocalRef(dmCell, c, cgeom, &cg));
2998     PetscCall(PetscArrayzero(cg, 1));
2999     PetscCall(DMPlexComputeCellGeometryFVM(dmCell, c, &cg->volume, cg->centroid, NULL));
3000   }
3001   /* Compute face normals and minimum cell radius */
3002   PetscCall(DMClone(dm, &dmFace));
3003   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &sectionFace));
3004   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
3005   PetscCall(PetscSectionSetChart(sectionFace, fStart, fEnd));
3006   for (f = fStart; f < fEnd; ++f) PetscCall(PetscSectionSetDof(sectionFace, f, (PetscInt)PetscCeilReal(((PetscReal)sizeof(PetscFVFaceGeom)) / sizeof(PetscScalar))));
3007   PetscCall(PetscSectionSetUp(sectionFace));
3008   PetscCall(DMSetLocalSection(dmFace, sectionFace));
3009   PetscCall(PetscSectionDestroy(&sectionFace));
3010   PetscCall(DMCreateLocalVector(dmFace, facegeom));
3011   PetscCall(VecGetArray(*facegeom, &fgeom));
3012   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3013   minradius = PETSC_MAX_REAL;
3014   for (f = fStart; f < fEnd; ++f) {
3015     PetscFVFaceGeom *fg;
3016     PetscReal        area;
3017     const PetscInt  *cells;
3018     PetscInt         ncells, ghost = -1, d, numChildren;
3019 
3020     if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost));
3021     PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL));
3022     PetscCall(DMPlexGetSupport(dm, f, &cells));
3023     PetscCall(DMPlexGetSupportSize(dm, f, &ncells));
3024     /* It is possible to get a face with no support when using partition overlap */
3025     if (!ncells || ghost >= 0 || numChildren) continue;
3026     PetscCall(DMPlexPointLocalRef(dmFace, f, fgeom, &fg));
3027     PetscCall(DMPlexComputeCellGeometryFVM(dm, f, &area, fg->centroid, fg->normal));
3028     for (d = 0; d < dim; ++d) fg->normal[d] *= area;
3029     /* Flip face orientation if necessary to match ordering in support, and Update minimum radius */
3030     {
3031       PetscFVCellGeom *cL, *cR;
3032       PetscReal       *lcentroid, *rcentroid;
3033       PetscReal        l[3], r[3], v[3];
3034 
3035       PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cgeom, &cL));
3036       lcentroid = cells[0] >= cEndInterior ? fg->centroid : cL->centroid;
3037       if (ncells > 1) {
3038         PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cgeom, &cR));
3039         rcentroid = cells[1] >= cEndInterior ? fg->centroid : cR->centroid;
3040       } else {
3041         rcentroid = fg->centroid;
3042       }
3043       PetscCall(DMLocalizeCoordinateReal_Internal(dm, dim, fg->centroid, lcentroid, l));
3044       PetscCall(DMLocalizeCoordinateReal_Internal(dm, dim, fg->centroid, rcentroid, r));
3045       DMPlex_WaxpyD_Internal(dim, -1, l, r, v);
3046       if (DMPlex_DotRealD_Internal(dim, fg->normal, v) < 0) {
3047         for (d = 0; d < dim; ++d) fg->normal[d] = -fg->normal[d];
3048       }
3049       if (DMPlex_DotRealD_Internal(dim, fg->normal, v) <= 0) {
3050         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]);
3051         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]);
3052         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Direction for face %" PetscInt_FMT " could not be fixed", f);
3053       }
3054       if (cells[0] < cEndInterior) {
3055         DMPlex_WaxpyD_Internal(dim, -1, fg->centroid, cL->centroid, v);
3056         minradius = PetscMin(minradius, DMPlex_NormD_Internal(dim, v));
3057       }
3058       if (ncells > 1 && cells[1] < cEndInterior) {
3059         DMPlex_WaxpyD_Internal(dim, -1, fg->centroid, cR->centroid, v);
3060         minradius = PetscMin(minradius, DMPlex_NormD_Internal(dim, v));
3061       }
3062     }
3063   }
3064   PetscCallMPI(MPIU_Allreduce(&minradius, &gminradius, 1, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
3065   PetscCall(DMPlexSetMinRadius(dm, gminradius));
3066   /* Compute centroids of ghost cells */
3067   for (c = cEndInterior; c < cEnd; ++c) {
3068     PetscFVFaceGeom *fg;
3069     const PetscInt  *cone, *support;
3070     PetscInt         coneSize, supportSize, s;
3071 
3072     PetscCall(DMPlexGetConeSize(dmCell, c, &coneSize));
3073     PetscCheck(coneSize == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Ghost cell %" PetscInt_FMT " has cone size %" PetscInt_FMT " != 1", c, coneSize);
3074     PetscCall(DMPlexGetCone(dmCell, c, &cone));
3075     PetscCall(DMPlexGetSupportSize(dmCell, cone[0], &supportSize));
3076     PetscCheck(supportSize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[0], supportSize);
3077     PetscCall(DMPlexGetSupport(dmCell, cone[0], &support));
3078     PetscCall(DMPlexPointLocalRef(dmFace, cone[0], fgeom, &fg));
3079     for (s = 0; s < 2; ++s) {
3080       /* Reflect ghost centroid across plane of face */
3081       if (support[s] == c) {
3082         PetscFVCellGeom *ci;
3083         PetscFVCellGeom *cg;
3084         PetscReal        c2f[3], a;
3085 
3086         PetscCall(DMPlexPointLocalRead(dmCell, support[(s + 1) % 2], cgeom, &ci));
3087         DMPlex_WaxpyD_Internal(dim, -1, ci->centroid, fg->centroid, c2f); /* cell to face centroid */
3088         a = DMPlex_DotRealD_Internal(dim, c2f, fg->normal) / DMPlex_DotRealD_Internal(dim, fg->normal, fg->normal);
3089         PetscCall(DMPlexPointLocalRef(dmCell, support[s], cgeom, &cg));
3090         DMPlex_WaxpyD_Internal(dim, 2 * a, fg->normal, ci->centroid, cg->centroid);
3091         cg->volume = ci->volume;
3092       }
3093     }
3094   }
3095   PetscCall(VecRestoreArray(*facegeom, &fgeom));
3096   PetscCall(VecRestoreArray(*cellgeom, &cgeom));
3097   PetscCall(DMDestroy(&dmCell));
3098   PetscCall(DMDestroy(&dmFace));
3099   PetscFunctionReturn(PETSC_SUCCESS);
3100 }
3101 
3102 /*@
3103   DMPlexGetMinRadius - Returns the minimum distance from any cell centroid to a face
3104 
3105   Not Collective
3106 
3107   Input Parameter:
3108 . dm - the `DMPLEX`
3109 
3110   Output Parameter:
3111 . minradius - the minimum cell radius
3112 
3113   Level: developer
3114 
3115 .seealso: `DMPLEX`, `DMGetCoordinates()`
3116 @*/
3117 PetscErrorCode DMPlexGetMinRadius(DM dm, PetscReal *minradius)
3118 {
3119   PetscFunctionBegin;
3120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3121   PetscAssertPointer(minradius, 2);
3122   *minradius = ((DM_Plex *)dm->data)->minradius;
3123   PetscFunctionReturn(PETSC_SUCCESS);
3124 }
3125 
3126 /*@
3127   DMPlexSetMinRadius - Sets the minimum distance from the cell centroid to a face
3128 
3129   Logically Collective
3130 
3131   Input Parameters:
3132 + dm        - the `DMPLEX`
3133 - minradius - the minimum cell radius
3134 
3135   Level: developer
3136 
3137 .seealso: `DMPLEX`, `DMSetCoordinates()`
3138 @*/
3139 PetscErrorCode DMPlexSetMinRadius(DM dm, PetscReal minradius)
3140 {
3141   PetscFunctionBegin;
3142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3143   ((DM_Plex *)dm->data)->minradius = minradius;
3144   PetscFunctionReturn(PETSC_SUCCESS);
3145 }
3146 
3147 static PetscErrorCode BuildGradientReconstruction_Internal(DM dm, PetscFV fvm, DM dmFace, PetscScalar *fgeom, DM dmCell, PetscScalar *cgeom)
3148 {
3149   DMLabel      ghostLabel;
3150   PetscScalar *dx, *grad, **gref;
3151   PetscInt     dim, cStart, cEnd, c, cEndInterior, maxNumFaces;
3152 
3153   PetscFunctionBegin;
3154   PetscCall(DMGetDimension(dm, &dim));
3155   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
3156   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
3157   cEndInterior = cEndInterior < 0 ? cEnd : cEndInterior;
3158   PetscCall(DMPlexGetMaxSizes(dm, &maxNumFaces, NULL));
3159   PetscCall(PetscFVLeastSquaresSetMaxFaces(fvm, maxNumFaces));
3160   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3161   PetscCall(PetscMalloc3(maxNumFaces * dim, &dx, maxNumFaces * dim, &grad, maxNumFaces, &gref));
3162   for (c = cStart; c < cEndInterior; c++) {
3163     const PetscInt  *faces;
3164     PetscInt         numFaces, usedFaces, f, d;
3165     PetscFVCellGeom *cg;
3166     PetscBool        boundary;
3167     PetscInt         ghost;
3168 
3169     // do not attempt to compute a gradient reconstruction stencil in a ghost cell.  It will never be used
3170     PetscCall(DMLabelGetValue(ghostLabel, c, &ghost));
3171     if (ghost >= 0) continue;
3172 
3173     PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg));
3174     PetscCall(DMPlexGetConeSize(dm, c, &numFaces));
3175     PetscCall(DMPlexGetCone(dm, c, &faces));
3176     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);
3177     for (f = 0, usedFaces = 0; f < numFaces; ++f) {
3178       PetscFVCellGeom *cg1;
3179       PetscFVFaceGeom *fg;
3180       const PetscInt  *fcells;
3181       PetscInt         ncell, side;
3182 
3183       PetscCall(DMLabelGetValue(ghostLabel, faces[f], &ghost));
3184       PetscCall(DMIsBoundaryPoint(dm, faces[f], &boundary));
3185       if ((ghost >= 0) || boundary) continue;
3186       PetscCall(DMPlexGetSupport(dm, faces[f], &fcells));
3187       side  = (c != fcells[0]); /* c is on left=0 or right=1 of face */
3188       ncell = fcells[!side];    /* the neighbor */
3189       PetscCall(DMPlexPointLocalRef(dmFace, faces[f], fgeom, &fg));
3190       PetscCall(DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1));
3191       for (d = 0; d < dim; ++d) dx[usedFaces * dim + d] = cg1->centroid[d] - cg->centroid[d];
3192       gref[usedFaces++] = fg->grad[side]; /* Gradient reconstruction term will go here */
3193     }
3194     PetscCheck(usedFaces, PETSC_COMM_SELF, PETSC_ERR_USER, "Mesh contains isolated cell (no neighbors). Is it intentional?");
3195     PetscCall(PetscFVComputeGradient(fvm, usedFaces, dx, grad));
3196     for (f = 0, usedFaces = 0; f < numFaces; ++f) {
3197       PetscCall(DMLabelGetValue(ghostLabel, faces[f], &ghost));
3198       PetscCall(DMIsBoundaryPoint(dm, faces[f], &boundary));
3199       if ((ghost >= 0) || boundary) continue;
3200       for (d = 0; d < dim; ++d) gref[usedFaces][d] = grad[usedFaces * dim + d];
3201       ++usedFaces;
3202     }
3203   }
3204   PetscCall(PetscFree3(dx, grad, gref));
3205   PetscFunctionReturn(PETSC_SUCCESS);
3206 }
3207 
3208 static PetscErrorCode BuildGradientReconstruction_Internal_Tree(DM dm, PetscFV fvm, DM dmFace, PetscScalar *fgeom, DM dmCell, PetscScalar *cgeom)
3209 {
3210   DMLabel      ghostLabel;
3211   PetscScalar *dx, *grad, **gref;
3212   PetscInt     dim, cStart, cEnd, c, cEndInterior, fStart, fEnd, f, nStart, nEnd, maxNumFaces = 0;
3213   PetscSection neighSec;
3214   PetscInt (*neighbors)[2];
3215   PetscInt *counter;
3216 
3217   PetscFunctionBegin;
3218   PetscCall(DMGetDimension(dm, &dim));
3219   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
3220   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
3221   if (cEndInterior < 0) cEndInterior = cEnd;
3222   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &neighSec));
3223   PetscCall(PetscSectionSetChart(neighSec, cStart, cEndInterior));
3224   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
3225   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3226   for (f = fStart; f < fEnd; f++) {
3227     const PetscInt *fcells;
3228     PetscBool       boundary;
3229     PetscInt        ghost = -1;
3230     PetscInt        numChildren, numCells, c;
3231 
3232     if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost));
3233     PetscCall(DMIsBoundaryPoint(dm, f, &boundary));
3234     PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL));
3235     if ((ghost >= 0) || boundary || numChildren) continue;
3236     PetscCall(DMPlexGetSupportSize(dm, f, &numCells));
3237     if (numCells == 2) {
3238       PetscCall(DMPlexGetSupport(dm, f, &fcells));
3239       for (c = 0; c < 2; c++) {
3240         PetscInt cell = fcells[c];
3241 
3242         if (cell >= cStart && cell < cEndInterior) PetscCall(PetscSectionAddDof(neighSec, cell, 1));
3243       }
3244     }
3245   }
3246   PetscCall(PetscSectionSetUp(neighSec));
3247   PetscCall(PetscSectionGetMaxDof(neighSec, &maxNumFaces));
3248   PetscCall(PetscFVLeastSquaresSetMaxFaces(fvm, maxNumFaces));
3249   nStart = 0;
3250   PetscCall(PetscSectionGetStorageSize(neighSec, &nEnd));
3251   PetscCall(PetscMalloc1(nEnd - nStart, &neighbors));
3252   PetscCall(PetscCalloc1(cEndInterior - cStart, &counter));
3253   for (f = fStart; f < fEnd; f++) {
3254     const PetscInt *fcells;
3255     PetscBool       boundary;
3256     PetscInt        ghost = -1;
3257     PetscInt        numChildren, numCells, c;
3258 
3259     if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost));
3260     PetscCall(DMIsBoundaryPoint(dm, f, &boundary));
3261     PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL));
3262     if ((ghost >= 0) || boundary || numChildren) continue;
3263     PetscCall(DMPlexGetSupportSize(dm, f, &numCells));
3264     if (numCells == 2) {
3265       PetscCall(DMPlexGetSupport(dm, f, &fcells));
3266       for (c = 0; c < 2; c++) {
3267         PetscInt cell = fcells[c], off;
3268 
3269         if (cell >= cStart && cell < cEndInterior) {
3270           PetscCall(PetscSectionGetOffset(neighSec, cell, &off));
3271           off += counter[cell - cStart]++;
3272           neighbors[off][0] = f;
3273           neighbors[off][1] = fcells[1 - c];
3274         }
3275       }
3276     }
3277   }
3278   PetscCall(PetscFree(counter));
3279   PetscCall(PetscMalloc3(maxNumFaces * dim, &dx, maxNumFaces * dim, &grad, maxNumFaces, &gref));
3280   for (c = cStart; c < cEndInterior; c++) {
3281     PetscInt         numFaces, f, d, off, ghost = -1;
3282     PetscFVCellGeom *cg;
3283 
3284     PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg));
3285     PetscCall(PetscSectionGetDof(neighSec, c, &numFaces));
3286     PetscCall(PetscSectionGetOffset(neighSec, c, &off));
3287 
3288     // do not attempt to compute a gradient reconstruction stencil in a ghost cell.  It will never be used
3289     if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, c, &ghost));
3290     if (ghost >= 0) continue;
3291 
3292     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);
3293     for (f = 0; f < numFaces; ++f) {
3294       PetscFVCellGeom *cg1;
3295       PetscFVFaceGeom *fg;
3296       const PetscInt  *fcells;
3297       PetscInt         ncell, side, nface;
3298 
3299       nface = neighbors[off + f][0];
3300       ncell = neighbors[off + f][1];
3301       PetscCall(DMPlexGetSupport(dm, nface, &fcells));
3302       side = (c != fcells[0]);
3303       PetscCall(DMPlexPointLocalRef(dmFace, nface, fgeom, &fg));
3304       PetscCall(DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1));
3305       for (d = 0; d < dim; ++d) dx[f * dim + d] = cg1->centroid[d] - cg->centroid[d];
3306       gref[f] = fg->grad[side]; /* Gradient reconstruction term will go here */
3307     }
3308     PetscCall(PetscFVComputeGradient(fvm, numFaces, dx, grad));
3309     for (f = 0; f < numFaces; ++f) {
3310       for (d = 0; d < dim; ++d) gref[f][d] = grad[f * dim + d];
3311     }
3312   }
3313   PetscCall(PetscFree3(dx, grad, gref));
3314   PetscCall(PetscSectionDestroy(&neighSec));
3315   PetscCall(PetscFree(neighbors));
3316   PetscFunctionReturn(PETSC_SUCCESS);
3317 }
3318 
3319 /*@
3320   DMPlexComputeGradientFVM - Compute geometric factors for gradient reconstruction, which are stored in the geometry data, and compute layout for gradient data
3321 
3322   Collective
3323 
3324   Input Parameters:
3325 + dm           - The `DMPLEX`
3326 . fvm          - The `PetscFV`
3327 - cellGeometry - The face geometry from `DMPlexComputeCellGeometryFVM()`
3328 
3329   Input/Output Parameter:
3330 . faceGeometry - The face geometry from `DMPlexComputeFaceGeometryFVM()`; on output
3331                  the geometric factors for gradient calculation are inserted
3332 
3333   Output Parameter:
3334 . dmGrad - The `DM` describing the layout of gradient data
3335 
3336   Level: developer
3337 
3338 .seealso: `DMPLEX`, `DMPlexGetFaceGeometryFVM()`, `DMPlexGetCellGeometryFVM()`
3339 @*/
3340 PetscErrorCode DMPlexComputeGradientFVM(DM dm, PetscFV fvm, Vec faceGeometry, Vec cellGeometry, DM *dmGrad)
3341 {
3342   DM           dmFace, dmCell;
3343   PetscScalar *fgeom, *cgeom;
3344   PetscSection sectionGrad, parentSection;
3345   PetscInt     dim, pdim, cStart, cEnd, cEndInterior, c;
3346 
3347   PetscFunctionBegin;
3348   PetscCall(DMGetDimension(dm, &dim));
3349   PetscCall(PetscFVGetNumComponents(fvm, &pdim));
3350   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
3351   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
3352   /* Construct the interpolant corresponding to each face from the least-square solution over the cell neighborhood */
3353   PetscCall(VecGetDM(faceGeometry, &dmFace));
3354   PetscCall(VecGetDM(cellGeometry, &dmCell));
3355   PetscCall(VecGetArray(faceGeometry, &fgeom));
3356   PetscCall(VecGetArray(cellGeometry, &cgeom));
3357   PetscCall(DMPlexGetTree(dm, &parentSection, NULL, NULL, NULL, NULL));
3358   if (!parentSection) {
3359     PetscCall(BuildGradientReconstruction_Internal(dm, fvm, dmFace, fgeom, dmCell, cgeom));
3360   } else {
3361     PetscCall(BuildGradientReconstruction_Internal_Tree(dm, fvm, dmFace, fgeom, dmCell, cgeom));
3362   }
3363   PetscCall(VecRestoreArray(faceGeometry, &fgeom));
3364   PetscCall(VecRestoreArray(cellGeometry, &cgeom));
3365   /* Create storage for gradients */
3366   PetscCall(DMClone(dm, dmGrad));
3367   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &sectionGrad));
3368   PetscCall(PetscSectionSetChart(sectionGrad, cStart, cEnd));
3369   for (c = cStart; c < cEnd; ++c) PetscCall(PetscSectionSetDof(sectionGrad, c, pdim * dim));
3370   PetscCall(PetscSectionSetUp(sectionGrad));
3371   PetscCall(DMSetLocalSection(*dmGrad, sectionGrad));
3372   PetscCall(PetscSectionDestroy(&sectionGrad));
3373   PetscFunctionReturn(PETSC_SUCCESS);
3374 }
3375 
3376 /*@
3377   DMPlexGetDataFVM - Retrieve precomputed cell geometry
3378 
3379   Collective
3380 
3381   Input Parameters:
3382 + dm - The `DM`
3383 - fv - The `PetscFV`
3384 
3385   Output Parameters:
3386 + cellgeom - The cell geometry
3387 . facegeom - The face geometry
3388 - gradDM   - The gradient matrices
3389 
3390   Level: developer
3391 
3392 .seealso: `DMPLEX`, `DMPlexComputeGeometryFVM()`
3393 @*/
3394 PetscErrorCode DMPlexGetDataFVM(DM dm, PetscFV fv, Vec *cellgeom, Vec *facegeom, DM *gradDM)
3395 {
3396   PetscObject cellgeomobj, facegeomobj;
3397 
3398   PetscFunctionBegin;
3399   PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_cellgeom_fvm", &cellgeomobj));
3400   if (!cellgeomobj) {
3401     Vec cellgeomInt, facegeomInt;
3402 
3403     PetscCall(DMPlexComputeGeometryFVM(dm, &cellgeomInt, &facegeomInt));
3404     PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_cellgeom_fvm", (PetscObject)cellgeomInt));
3405     PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_facegeom_fvm", (PetscObject)facegeomInt));
3406     PetscCall(VecDestroy(&cellgeomInt));
3407     PetscCall(VecDestroy(&facegeomInt));
3408     PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_cellgeom_fvm", &cellgeomobj));
3409   }
3410   PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_facegeom_fvm", &facegeomobj));
3411   if (cellgeom) *cellgeom = (Vec)cellgeomobj;
3412   if (facegeom) *facegeom = (Vec)facegeomobj;
3413   if (gradDM) {
3414     PetscObject gradobj;
3415     PetscBool   computeGradients;
3416 
3417     PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
3418     if (!computeGradients) {
3419       *gradDM = NULL;
3420       PetscFunctionReturn(PETSC_SUCCESS);
3421     }
3422     PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_dmgrad_fvm", &gradobj));
3423     if (!gradobj) {
3424       DM dmGradInt;
3425 
3426       PetscCall(DMPlexComputeGradientFVM(dm, fv, (Vec)facegeomobj, (Vec)cellgeomobj, &dmGradInt));
3427       PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_dmgrad_fvm", (PetscObject)dmGradInt));
3428       PetscCall(DMDestroy(&dmGradInt));
3429       PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_dmgrad_fvm", &gradobj));
3430     }
3431     *gradDM = (DM)gradobj;
3432   }
3433   PetscFunctionReturn(PETSC_SUCCESS);
3434 }
3435 
3436 static PetscErrorCode DMPlexCoordinatesToReference_NewtonUpdate(PetscInt dimC, PetscInt dimR, PetscScalar *J, PetscScalar *invJ, PetscScalar *work, PetscReal *resNeg, PetscReal *guess)
3437 {
3438   PetscInt l, m;
3439 
3440   PetscFunctionBeginHot;
3441   if (dimC == dimR && dimR <= 3) {
3442     /* invert Jacobian, multiply */
3443     PetscScalar det, idet;
3444 
3445     switch (dimR) {
3446     case 1:
3447       invJ[0] = 1. / J[0];
3448       break;
3449     case 2:
3450       det     = J[0] * J[3] - J[1] * J[2];
3451       idet    = 1. / det;
3452       invJ[0] = J[3] * idet;
3453       invJ[1] = -J[1] * idet;
3454       invJ[2] = -J[2] * idet;
3455       invJ[3] = J[0] * idet;
3456       break;
3457     case 3: {
3458       invJ[0] = J[4] * J[8] - J[5] * J[7];
3459       invJ[1] = J[2] * J[7] - J[1] * J[8];
3460       invJ[2] = J[1] * J[5] - J[2] * J[4];
3461       det     = invJ[0] * J[0] + invJ[1] * J[3] + invJ[2] * J[6];
3462       idet    = 1. / det;
3463       invJ[0] *= idet;
3464       invJ[1] *= idet;
3465       invJ[2] *= idet;
3466       invJ[3] = idet * (J[5] * J[6] - J[3] * J[8]);
3467       invJ[4] = idet * (J[0] * J[8] - J[2] * J[6]);
3468       invJ[5] = idet * (J[2] * J[3] - J[0] * J[5]);
3469       invJ[6] = idet * (J[3] * J[7] - J[4] * J[6]);
3470       invJ[7] = idet * (J[1] * J[6] - J[0] * J[7]);
3471       invJ[8] = idet * (J[0] * J[4] - J[1] * J[3]);
3472     } break;
3473     }
3474     for (l = 0; l < dimR; l++) {
3475       for (m = 0; m < dimC; m++) guess[l] += PetscRealPart(invJ[l * dimC + m]) * resNeg[m];
3476     }
3477   } else {
3478 #if defined(PETSC_USE_COMPLEX)
3479     char transpose = 'C';
3480 #else
3481     char transpose = 'T';
3482 #endif
3483     PetscBLASInt m, n, one = 1, worksize, info;
3484 
3485     PetscCall(PetscBLASIntCast(dimR, &m));
3486     PetscCall(PetscBLASIntCast(dimC, &n));
3487     PetscCall(PetscBLASIntCast(dimC * dimC, &worksize));
3488     for (l = 0; l < dimC; l++) invJ[l] = resNeg[l];
3489 
3490     PetscCallBLAS("LAPACKgels", LAPACKgels_(&transpose, &m, &n, &one, J, &m, invJ, &n, work, &worksize, &info));
3491     PetscCheck(info == 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "Bad argument to GELS %" PetscBLASInt_FMT, info);
3492 
3493     for (l = 0; l < dimR; l++) guess[l] += PetscRealPart(invJ[l]);
3494   }
3495   PetscFunctionReturn(PETSC_SUCCESS);
3496 }
3497 
3498 static PetscErrorCode DMPlexCoordinatesToReference_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[], Vec coords, PetscInt dimC, PetscInt dimR)
3499 {
3500   PetscInt     coordSize, i, j, k, l, m, maxIts = 7, numV = (1 << dimR);
3501   PetscScalar *coordsScalar = NULL;
3502   PetscReal   *cellData, *cellCoords, *cellCoeffs, *extJ, *resNeg;
3503   PetscScalar *J, *invJ, *work;
3504 
3505   PetscFunctionBegin;
3506   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3507   PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3508   PetscCheck(coordSize >= dimC * numV, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expecting at least %" PetscInt_FMT " coordinates, got %" PetscInt_FMT, dimC * (1 << dimR), coordSize);
3509   PetscCall(DMGetWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData));
3510   PetscCall(DMGetWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J));
3511   cellCoords = &cellData[0];
3512   cellCoeffs = &cellData[coordSize];
3513   extJ       = &cellData[2 * coordSize];
3514   resNeg     = &cellData[2 * coordSize + dimR];
3515   invJ       = &J[dimR * dimC];
3516   work       = &J[2 * dimR * dimC];
3517   if (dimR == 2) {
3518     const PetscInt zToPlex[4] = {0, 1, 3, 2};
3519 
3520     for (i = 0; i < 4; i++) {
3521       PetscInt plexI = zToPlex[i];
3522 
3523       for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3524     }
3525   } else if (dimR == 3) {
3526     const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
3527 
3528     for (i = 0; i < 8; i++) {
3529       PetscInt plexI = zToPlex[i];
3530 
3531       for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3532     }
3533   } else {
3534     for (i = 0; i < coordSize; i++) cellCoords[i] = PetscRealPart(coordsScalar[i]);
3535   }
3536   /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */
3537   for (i = 0; i < dimR; i++) {
3538     PetscReal *swap;
3539 
3540     for (j = 0; j < (numV / 2); j++) {
3541       for (k = 0; k < dimC; k++) {
3542         cellCoeffs[dimC * j + k]                = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]);
3543         cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]);
3544       }
3545     }
3546 
3547     if (i < dimR - 1) {
3548       swap       = cellCoeffs;
3549       cellCoeffs = cellCoords;
3550       cellCoords = swap;
3551     }
3552   }
3553   PetscCall(PetscArrayzero(refCoords, numPoints * dimR));
3554   for (j = 0; j < numPoints; j++) {
3555     for (i = 0; i < maxIts; i++) {
3556       PetscReal *guess = &refCoords[dimR * j];
3557 
3558       /* compute -residual and Jacobian */
3559       for (k = 0; k < dimC; k++) resNeg[k] = realCoords[dimC * j + k];
3560       for (k = 0; k < dimC * dimR; k++) J[k] = 0.;
3561       for (k = 0; k < numV; k++) {
3562         PetscReal extCoord = 1.;
3563         for (l = 0; l < dimR; l++) {
3564           PetscReal coord = guess[l];
3565           PetscInt  dep   = (k & (1 << l)) >> l;
3566 
3567           extCoord *= dep * coord + !dep;
3568           extJ[l] = dep;
3569 
3570           for (m = 0; m < dimR; m++) {
3571             PetscReal coord = guess[m];
3572             PetscInt  dep   = ((k & (1 << m)) >> m) && (m != l);
3573             PetscReal mult  = dep * coord + !dep;
3574 
3575             extJ[l] *= mult;
3576           }
3577         }
3578         for (l = 0; l < dimC; l++) {
3579           PetscReal coeff = cellCoeffs[dimC * k + l];
3580 
3581           resNeg[l] -= coeff * extCoord;
3582           for (m = 0; m < dimR; m++) J[dimR * l + m] += coeff * extJ[m];
3583         }
3584       }
3585       if (0 && PetscDefined(USE_DEBUG)) {
3586         PetscReal maxAbs = 0.;
3587 
3588         for (l = 0; l < dimC; l++) maxAbs = PetscMax(maxAbs, PetscAbsReal(resNeg[l]));
3589         PetscCall(PetscInfo(dm, "cell %" PetscInt_FMT ", point %" PetscInt_FMT ", iter %" PetscInt_FMT ": res %g\n", cell, j, i, (double)maxAbs));
3590       }
3591 
3592       PetscCall(DMPlexCoordinatesToReference_NewtonUpdate(dimC, dimR, J, invJ, work, resNeg, guess));
3593     }
3594   }
3595   PetscCall(DMRestoreWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J));
3596   PetscCall(DMRestoreWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData));
3597   PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3598   PetscFunctionReturn(PETSC_SUCCESS);
3599 }
3600 
3601 static PetscErrorCode DMPlexReferenceToCoordinates_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt dimC, PetscInt dimR)
3602 {
3603   PetscInt     coordSize, i, j, k, l, numV = (1 << dimR);
3604   PetscScalar *coordsScalar = NULL;
3605   PetscReal   *cellData, *cellCoords, *cellCoeffs;
3606 
3607   PetscFunctionBegin;
3608   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3609   PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3610   PetscCheck(coordSize >= dimC * numV, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expecting at least %" PetscInt_FMT " coordinates, got %" PetscInt_FMT, dimC * (1 << dimR), coordSize);
3611   PetscCall(DMGetWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData));
3612   cellCoords = &cellData[0];
3613   cellCoeffs = &cellData[coordSize];
3614   if (dimR == 2) {
3615     const PetscInt zToPlex[4] = {0, 1, 3, 2};
3616 
3617     for (i = 0; i < 4; i++) {
3618       PetscInt plexI = zToPlex[i];
3619 
3620       for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3621     }
3622   } else if (dimR == 3) {
3623     const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
3624 
3625     for (i = 0; i < 8; i++) {
3626       PetscInt plexI = zToPlex[i];
3627 
3628       for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3629     }
3630   } else {
3631     for (i = 0; i < coordSize; i++) cellCoords[i] = PetscRealPart(coordsScalar[i]);
3632   }
3633   /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */
3634   for (i = 0; i < dimR; i++) {
3635     PetscReal *swap;
3636 
3637     for (j = 0; j < (numV / 2); j++) {
3638       for (k = 0; k < dimC; k++) {
3639         cellCoeffs[dimC * j + k]                = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]);
3640         cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]);
3641       }
3642     }
3643 
3644     if (i < dimR - 1) {
3645       swap       = cellCoeffs;
3646       cellCoeffs = cellCoords;
3647       cellCoords = swap;
3648     }
3649   }
3650   PetscCall(PetscArrayzero(realCoords, numPoints * dimC));
3651   for (j = 0; j < numPoints; j++) {
3652     const PetscReal *guess  = &refCoords[dimR * j];
3653     PetscReal       *mapped = &realCoords[dimC * j];
3654 
3655     for (k = 0; k < numV; k++) {
3656       PetscReal extCoord = 1.;
3657       for (l = 0; l < dimR; l++) {
3658         PetscReal coord = guess[l];
3659         PetscInt  dep   = (k & (1 << l)) >> l;
3660 
3661         extCoord *= dep * coord + !dep;
3662       }
3663       for (l = 0; l < dimC; l++) {
3664         PetscReal coeff = cellCoeffs[dimC * k + l];
3665 
3666         mapped[l] += coeff * extCoord;
3667       }
3668     }
3669   }
3670   PetscCall(DMRestoreWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData));
3671   PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3672   PetscFunctionReturn(PETSC_SUCCESS);
3673 }
3674 
3675 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)
3676 {
3677   PetscInt     numComp, pdim, i, j, k, l, m, coordSize;
3678   PetscScalar *nodes = NULL;
3679   PetscReal   *invV, *modes;
3680   PetscReal   *B, *D, *resNeg;
3681   PetscScalar *J, *invJ, *work;
3682   PetscReal    tolerance = tol == NULL ? 0.0 : *tol;
3683 
3684   PetscFunctionBegin;
3685   PetscCall(PetscFEGetDimension(fe, &pdim));
3686   PetscCall(PetscFEGetNumComponents(fe, &numComp));
3687   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);
3688   /* we shouldn't apply inverse closure permutation, if one exists */
3689   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, coords, cell, 0, &coordSize, &nodes));
3690   /* convert nodes to values in the stable evaluation basis */
3691   PetscCall(DMGetWorkArray(dm, pdim, MPIU_REAL, &modes));
3692   invV = fe->invV;
3693   for (i = 0; i < pdim; ++i) {
3694     modes[i] = 0.;
3695     for (j = 0; j < pdim; ++j) modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]);
3696   }
3697   PetscCall(DMGetWorkArray(dm, pdim * Nc + pdim * Nc * dimR + Nc, MPIU_REAL, &B));
3698   D      = &B[pdim * Nc];
3699   resNeg = &D[pdim * Nc * dimR];
3700   PetscCall(DMGetWorkArray(dm, 3 * Nc * dimR, MPIU_SCALAR, &J));
3701   invJ = &J[Nc * dimR];
3702   work = &invJ[Nc * dimR];
3703   for (i = 0; i < numPoints * dimR; i++) refCoords[i] = 0.;
3704   for (j = 0; j < numPoints; j++) {
3705     PetscReal normPoint = DMPlex_NormD_Internal(Nc, &realCoords[j * Nc]);
3706     normPoint           = normPoint > PETSC_SMALL ? normPoint : 1.0;
3707     for (i = 0; i < maxIter; i++) { /* we could batch this so that we're not making big B and D arrays all the time */
3708       PetscReal *guess = &refCoords[j * dimR], error = 0;
3709       PetscCall(PetscSpaceEvaluate(fe->basisSpace, 1, guess, B, D, NULL));
3710       for (k = 0; k < Nc; k++) resNeg[k] = realCoords[j * Nc + k];
3711       for (k = 0; k < Nc * dimR; k++) J[k] = 0.;
3712       for (k = 0; k < pdim; k++) {
3713         for (l = 0; l < Nc; l++) {
3714           resNeg[l] -= modes[k] * B[k * Nc + l];
3715           for (m = 0; m < dimR; m++) J[l * dimR + m] += modes[k] * D[(k * Nc + l) * dimR + m];
3716         }
3717       }
3718       if (0 && PetscDefined(USE_DEBUG)) {
3719         PetscReal maxAbs = 0.;
3720 
3721         for (l = 0; l < Nc; l++) maxAbs = PetscMax(maxAbs, PetscAbsReal(resNeg[l]));
3722         PetscCall(PetscInfo(dm, "cell %" PetscInt_FMT ", point %" PetscInt_FMT ", iter %" PetscInt_FMT ": res %g\n", cell, j, i, (double)maxAbs));
3723       }
3724       error = DMPlex_NormD_Internal(Nc, resNeg);
3725       if (error < tolerance * normPoint) {
3726         if (tol) *tol = error / normPoint;
3727         break;
3728       }
3729       PetscCall(DMPlexCoordinatesToReference_NewtonUpdate(Nc, dimR, J, invJ, work, resNeg, guess));
3730     }
3731   }
3732   PetscCall(DMRestoreWorkArray(dm, 3 * Nc * dimR, MPIU_SCALAR, &J));
3733   PetscCall(DMRestoreWorkArray(dm, pdim * Nc + pdim * Nc * dimR + Nc, MPIU_REAL, &B));
3734   PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_REAL, &modes));
3735   PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes));
3736   PetscFunctionReturn(PETSC_SUCCESS);
3737 }
3738 
3739 /* TODO: TOBY please fix this for Nc > 1 */
3740 PetscErrorCode DMPlexReferenceToCoordinates_FE(DM dm, PetscFE fe, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt Nc, PetscInt dimR)
3741 {
3742   PetscInt     numComp, pdim, i, j, k, l, coordSize;
3743   PetscScalar *nodes = NULL;
3744   PetscReal   *invV, *modes;
3745   PetscReal   *B;
3746 
3747   PetscFunctionBegin;
3748   PetscCall(PetscFEGetDimension(fe, &pdim));
3749   PetscCall(PetscFEGetNumComponents(fe, &numComp));
3750   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);
3751   /* we shouldn't apply inverse closure permutation, if one exists */
3752   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, coords, cell, 0, &coordSize, &nodes));
3753   /* convert nodes to values in the stable evaluation basis */
3754   PetscCall(DMGetWorkArray(dm, pdim, MPIU_REAL, &modes));
3755   invV = fe->invV;
3756   for (i = 0; i < pdim; ++i) {
3757     modes[i] = 0.;
3758     for (j = 0; j < pdim; ++j) modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]);
3759   }
3760   PetscCall(DMGetWorkArray(dm, numPoints * pdim * Nc, MPIU_REAL, &B));
3761   PetscCall(PetscSpaceEvaluate(fe->basisSpace, numPoints, refCoords, B, NULL, NULL));
3762   for (i = 0; i < numPoints * Nc; i++) realCoords[i] = 0.;
3763   for (j = 0; j < numPoints; j++) {
3764     PetscReal *mapped = &realCoords[j * Nc];
3765 
3766     for (k = 0; k < pdim; k++) {
3767       for (l = 0; l < Nc; l++) mapped[l] += modes[k] * B[(j * pdim + k) * Nc + l];
3768     }
3769   }
3770   PetscCall(DMRestoreWorkArray(dm, numPoints * pdim * Nc, MPIU_REAL, &B));
3771   PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_REAL, &modes));
3772   PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes));
3773   PetscFunctionReturn(PETSC_SUCCESS);
3774 }
3775 
3776 /*@
3777   DMPlexCoordinatesToReference - Pull coordinates back from the mesh to the reference element
3778   using a single element map.
3779 
3780   Not Collective
3781 
3782   Input Parameters:
3783 + dm         - The mesh, with coordinate maps defined either by a `PetscDS` for the coordinate `DM` (see `DMGetCoordinateDM()`) or
3784                implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or
3785                as a multilinear map for tensor-product elements
3786 . cell       - the cell whose map is used.
3787 . numPoints  - the number of points to locate
3788 - realCoords - (numPoints x coordinate dimension) array of coordinates (see `DMGetCoordinateDim()`)
3789 
3790   Output Parameter:
3791 . refCoords - (`numPoints` x `dimension`) array of reference coordinates (see `DMGetDimension()`)
3792 
3793   Level: intermediate
3794 
3795   Notes:
3796   This inversion will be accurate inside the reference element, but may be inaccurate for
3797   mappings that do not extend uniquely outside the reference cell (e.g, most non-affine maps)
3798 
3799 .seealso: `DMPLEX`, `DMPlexReferenceToCoordinates()`
3800 @*/
3801 PetscErrorCode DMPlexCoordinatesToReference(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[])
3802 {
3803   PetscInt dimC, dimR, depth, cStart, cEnd, i;
3804   DM       coordDM = NULL;
3805   Vec      coords;
3806   PetscFE  fe = NULL;
3807 
3808   PetscFunctionBegin;
3809   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3810   PetscCall(DMGetDimension(dm, &dimR));
3811   PetscCall(DMGetCoordinateDim(dm, &dimC));
3812   if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(PETSC_SUCCESS);
3813   PetscCall(DMPlexGetDepth(dm, &depth));
3814   PetscCall(DMGetCoordinatesLocal(dm, &coords));
3815   PetscCall(DMGetCoordinateDM(dm, &coordDM));
3816   if (coordDM) {
3817     PetscInt coordFields;
3818 
3819     PetscCall(DMGetNumFields(coordDM, &coordFields));
3820     if (coordFields) {
3821       PetscClassId id;
3822       PetscObject  disc;
3823 
3824       PetscCall(DMGetField(coordDM, 0, NULL, &disc));
3825       PetscCall(PetscObjectGetClassId(disc, &id));
3826       if (id == PETSCFE_CLASSID) fe = (PetscFE)disc;
3827     }
3828   }
3829   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
3830   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);
3831   if (!fe) { /* implicit discretization: affine or multilinear */
3832     PetscInt  coneSize;
3833     PetscBool isSimplex, isTensor;
3834 
3835     PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
3836     isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE;
3837     isTensor  = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE;
3838     if (isSimplex) {
3839       PetscReal detJ, *v0, *J, *invJ;
3840 
3841       PetscCall(DMGetWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3842       J    = &v0[dimC];
3843       invJ = &J[dimC * dimC];
3844       PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, invJ, &detJ));
3845       for (i = 0; i < numPoints; i++) { /* Apply the inverse affine transformation for each point */
3846         const PetscReal x0[3] = {-1., -1., -1.};
3847 
3848         CoordinatesRealToRef(dimC, dimR, x0, v0, invJ, &realCoords[dimC * i], &refCoords[dimR * i]);
3849       }
3850       PetscCall(DMRestoreWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3851     } else if (isTensor) {
3852       PetscCall(DMPlexCoordinatesToReference_Tensor(coordDM, cell, numPoints, realCoords, refCoords, coords, dimC, dimR));
3853     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unrecognized cone size %" PetscInt_FMT, coneSize);
3854   } else {
3855     PetscCall(DMPlexCoordinatesToReference_FE(coordDM, fe, cell, numPoints, realCoords, refCoords, coords, dimC, dimR, 7, NULL));
3856   }
3857   PetscFunctionReturn(PETSC_SUCCESS);
3858 }
3859 
3860 /*@
3861   DMPlexReferenceToCoordinates - Map references coordinates to coordinates in the mesh for a single element map.
3862 
3863   Not Collective
3864 
3865   Input Parameters:
3866 + dm        - The mesh, with coordinate maps defined either by a PetscDS for the coordinate `DM` (see `DMGetCoordinateDM()`) or
3867                implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or
3868                as a multilinear map for tensor-product elements
3869 . cell      - the cell whose map is used.
3870 . numPoints - the number of points to locate
3871 - refCoords - (numPoints x dimension) array of reference coordinates (see `DMGetDimension()`)
3872 
3873   Output Parameter:
3874 . realCoords - (numPoints x coordinate dimension) array of coordinates (see `DMGetCoordinateDim()`)
3875 
3876   Level: intermediate
3877 
3878 .seealso: `DMPLEX`, `DMPlexCoordinatesToReference()`
3879 @*/
3880 PetscErrorCode DMPlexReferenceToCoordinates(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[])
3881 {
3882   PetscInt dimC, dimR, depth, cStart, cEnd, i;
3883   DM       coordDM = NULL;
3884   Vec      coords;
3885   PetscFE  fe = NULL;
3886 
3887   PetscFunctionBegin;
3888   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3889   PetscCall(DMGetDimension(dm, &dimR));
3890   PetscCall(DMGetCoordinateDim(dm, &dimC));
3891   if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(PETSC_SUCCESS);
3892   PetscCall(DMPlexGetDepth(dm, &depth));
3893   PetscCall(DMGetCoordinatesLocal(dm, &coords));
3894   PetscCall(DMGetCoordinateDM(dm, &coordDM));
3895   if (coordDM) {
3896     PetscInt coordFields;
3897 
3898     PetscCall(DMGetNumFields(coordDM, &coordFields));
3899     if (coordFields) {
3900       PetscClassId id;
3901       PetscObject  disc;
3902 
3903       PetscCall(DMGetField(coordDM, 0, NULL, &disc));
3904       PetscCall(PetscObjectGetClassId(disc, &id));
3905       if (id == PETSCFE_CLASSID) fe = (PetscFE)disc;
3906     }
3907   }
3908   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
3909   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);
3910   if (!fe) { /* implicit discretization: affine or multilinear */
3911     PetscInt  coneSize;
3912     PetscBool isSimplex, isTensor;
3913 
3914     PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
3915     isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE;
3916     isTensor  = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE;
3917     if (isSimplex) {
3918       PetscReal detJ, *v0, *J;
3919 
3920       PetscCall(DMGetWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3921       J = &v0[dimC];
3922       PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, NULL, &detJ));
3923       for (i = 0; i < numPoints; i++) { /* Apply the affine transformation for each point */
3924         const PetscReal xi0[3] = {-1., -1., -1.};
3925 
3926         CoordinatesRefToReal(dimC, dimR, xi0, v0, J, &refCoords[dimR * i], &realCoords[dimC * i]);
3927       }
3928       PetscCall(DMRestoreWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3929     } else if (isTensor) {
3930       PetscCall(DMPlexReferenceToCoordinates_Tensor(coordDM, cell, numPoints, refCoords, realCoords, coords, dimC, dimR));
3931     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unrecognized cone size %" PetscInt_FMT, coneSize);
3932   } else {
3933     PetscCall(DMPlexReferenceToCoordinates_FE(coordDM, fe, cell, numPoints, refCoords, realCoords, coords, dimC, dimR));
3934   }
3935   PetscFunctionReturn(PETSC_SUCCESS);
3936 }
3937 
3938 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[])
3939 {
3940   const PetscInt Nc = uOff[1] - uOff[0];
3941   PetscInt       c;
3942 
3943   for (c = 0; c < Nc; ++c) f0[c] = u[c];
3944 }
3945 
3946 /* Shear applies the transformation, assuming we fix z,
3947   / 1  0  m_0 \
3948   | 0  1  m_1 |
3949   \ 0  0   1  /
3950 */
3951 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[])
3952 {
3953   const PetscInt Nc = uOff[1] - uOff[0];
3954   const PetscInt ax = (PetscInt)PetscRealPart(constants[0]);
3955   PetscInt       c;
3956 
3957   for (c = 0; c < Nc; ++c) coords[c] = u[c] + constants[c + 1] * u[ax];
3958 }
3959 
3960 /* Flare applies the transformation, assuming we fix x_f,
3961 
3962    x_i = x_i * alpha_i x_f
3963 */
3964 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[])
3965 {
3966   const PetscInt Nc = uOff[1] - uOff[0];
3967   const PetscInt cf = (PetscInt)PetscRealPart(constants[0]);
3968   PetscInt       c;
3969 
3970   for (c = 0; c < Nc; ++c) coords[c] = u[c] * (c == cf ? 1.0 : constants[c + 1] * u[cf]);
3971 }
3972 
3973 /*
3974   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
3975   will correspond to the top and bottom of our square. So
3976 
3977     (0,0)--(1,0)  ==>  (1,0)--(2,0)      Just a shift of (1,0)
3978     (0,1)--(1,1)  ==>  (0,1)--(0,2)      Switch x and y
3979 
3980   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:
3981 
3982     (x, y)  ==>  (x+1, \pi/2 y)                           in (r', \theta') space
3983             ==>  ((x+1) cos(\pi/2 y), (x+1) sin(\pi/2 y)) in (x', y') space
3984 */
3985 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[])
3986 {
3987   const PetscReal ri = PetscRealPart(constants[0]);
3988   const PetscReal ro = PetscRealPart(constants[1]);
3989 
3990   xp[0] = (x[0] * (ro - ri) + ri) * PetscCosReal(0.5 * PETSC_PI * x[1]);
3991   xp[1] = (x[0] * (ro - ri) + ri) * PetscSinReal(0.5 * PETSC_PI * x[1]);
3992 }
3993 
3994 /*
3995   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
3996   lower hemisphere and the upper surface onto the top, letting z be the radius.
3997 
3998     (x, y)  ==>  ((z+3)/2, \pi/2 (|x| or |y|), arctan y/x)                                                  in (r', \theta', \phi') space
3999             ==>  ((z+3)/2 \cos(\theta') cos(\phi'), (z+3)/2 \cos(\theta') sin(\phi'), (z+3)/2 sin(\theta')) in (x', y', z') space
4000 */
4001 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[])
4002 {
4003   const PetscReal pi4    = PETSC_PI / 4.0;
4004   const PetscReal ri     = PetscRealPart(constants[0]);
4005   const PetscReal ro     = PetscRealPart(constants[1]);
4006   const PetscReal rp     = (x[2] + 1) * 0.5 * (ro - ri) + ri;
4007   const PetscReal phip   = PetscAtan2Real(x[1], x[0]);
4008   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])));
4009 
4010   xp[0] = rp * PetscCosReal(thetap) * PetscCosReal(phip);
4011   xp[1] = rp * PetscCosReal(thetap) * PetscSinReal(phip);
4012   xp[2] = rp * PetscSinReal(thetap);
4013 }
4014 
4015 void coordMap_sinusoid(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[])
4016 {
4017   const PetscReal c = PetscRealPart(constants[0]);
4018   const PetscReal m = PetscRealPart(constants[1]);
4019   const PetscReal n = PetscRealPart(constants[2]);
4020 
4021   xp[0] = x[0];
4022   xp[1] = x[1];
4023   if (dim > 2) xp[2] = c * PetscCosReal(2. * m * PETSC_PI * x[0]) * PetscCosReal(2. * n * PETSC_PI * x[1]);
4024 }
4025 
4026 /*@C
4027   DMPlexRemapGeometry - This function maps the original `DM` coordinates to new coordinates.
4028 
4029   Not Collective
4030 
4031   Input Parameters:
4032 + dm   - The `DM`
4033 . time - The time
4034 - func - The function transforming current coordinates to new coordinates
4035 
4036   Calling sequence of `func`:
4037 + dim          - The spatial dimension
4038 . Nf           - The number of input fields (here 1)
4039 . NfAux        - The number of input auxiliary fields
4040 . uOff         - The offset of the coordinates in u[] (here 0)
4041 . uOff_x       - The offset of the coordinates in u_x[] (here 0)
4042 . u            - The coordinate values at this point in space
4043 . u_t          - The coordinate time derivative at this point in space (here `NULL`)
4044 . u_x          - The coordinate derivatives at this point in space
4045 . aOff         - The offset of each auxiliary field in u[]
4046 . aOff_x       - The offset of each auxiliary field in u_x[]
4047 . a            - The auxiliary field values at this point in space
4048 . a_t          - The auxiliary field time derivative at this point in space (or `NULL`)
4049 . a_x          - The auxiliary field derivatives at this point in space
4050 . t            - The current time
4051 . x            - The coordinates of this point (here not used)
4052 . numConstants - The number of constants
4053 . constants    - The value of each constant
4054 - f            - The new coordinates at this point in space
4055 
4056   Level: intermediate
4057 
4058 .seealso: `DMPLEX`, `DMGetCoordinates()`, `DMGetCoordinatesLocal()`, `DMGetCoordinateDM()`, `DMProjectFieldLocal()`, `DMProjectFieldLabelLocal()`
4059 @*/
4060 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[]))
4061 {
4062   DM           cdm;
4063   PetscDS      cds;
4064   DMField      cf;
4065   PetscObject  obj;
4066   PetscClassId id;
4067   Vec          lCoords, tmpCoords;
4068 
4069   PetscFunctionBegin;
4070   PetscCall(DMGetCoordinateDM(dm, &cdm));
4071   PetscCall(DMGetCoordinatesLocal(dm, &lCoords));
4072   PetscCall(DMGetDS(cdm, &cds));
4073   PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
4074   PetscCall(PetscObjectGetClassId(obj, &id));
4075   if (id != PETSCFE_CLASSID) {
4076     PetscSection       cSection;
4077     const PetscScalar *constants;
4078     PetscScalar       *coords, f[16];
4079     PetscInt           dim, cdim, Nc, vStart, vEnd;
4080 
4081     PetscCall(DMGetDimension(dm, &dim));
4082     PetscCall(DMGetCoordinateDim(dm, &cdim));
4083     PetscCheck(cdim <= 16, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Affine version of DMPlexRemapGeometry is currently limited to dimensions <= 16, not %" PetscInt_FMT, cdim);
4084     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
4085     PetscCall(DMGetCoordinateSection(dm, &cSection));
4086     PetscCall(PetscDSGetConstants(cds, &Nc, &constants));
4087     PetscCall(VecGetArrayWrite(lCoords, &coords));
4088     for (PetscInt v = vStart; v < vEnd; ++v) {
4089       PetscInt uOff[2] = {0, cdim};
4090       PetscInt off, c;
4091 
4092       PetscCall(PetscSectionGetOffset(cSection, v, &off));
4093       (*func)(dim, 1, 0, uOff, NULL, &coords[off], NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0.0, NULL, Nc, constants, f);
4094       for (c = 0; c < cdim; ++c) coords[off + c] = f[c];
4095     }
4096     PetscCall(VecRestoreArrayWrite(lCoords, &coords));
4097   } else {
4098     PetscCall(DMGetLocalVector(cdm, &tmpCoords));
4099     PetscCall(VecCopy(lCoords, tmpCoords));
4100     /* We have to do the coordinate field manually right now since the coordinate DM will not have its own */
4101     PetscCall(DMGetCoordinateField(dm, &cf));
4102     cdm->coordinates[0].field = cf;
4103     PetscCall(DMProjectFieldLocal(cdm, time, tmpCoords, &func, INSERT_VALUES, lCoords));
4104     cdm->coordinates[0].field = NULL;
4105     PetscCall(DMRestoreLocalVector(cdm, &tmpCoords));
4106     PetscCall(DMSetCoordinatesLocal(dm, lCoords));
4107   }
4108   PetscFunctionReturn(PETSC_SUCCESS);
4109 }
4110 
4111 /*@
4112   DMPlexShearGeometry - This shears the domain, meaning adds a multiple of the shear coordinate to all other coordinates.
4113 
4114   Not Collective
4115 
4116   Input Parameters:
4117 + dm          - The `DMPLEX`
4118 . direction   - The shear coordinate direction, e.g. `DM_X` is the x-axis
4119 - multipliers - The multiplier m for each direction which is not the shear direction
4120 
4121   Level: intermediate
4122 
4123 .seealso: `DMPLEX`, `DMPlexRemapGeometry()`, `DMDirection`, `DM_X`, `DM_Y`, `DM_Z`
4124 @*/
4125 PetscErrorCode DMPlexShearGeometry(DM dm, DMDirection direction, PetscReal multipliers[])
4126 {
4127   DM             cdm;
4128   PetscDS        cds;
4129   PetscScalar   *moduli;
4130   const PetscInt dir = (PetscInt)direction;
4131   PetscInt       dE, d, e;
4132 
4133   PetscFunctionBegin;
4134   PetscCall(DMGetCoordinateDM(dm, &cdm));
4135   PetscCall(DMGetCoordinateDim(dm, &dE));
4136   PetscCall(PetscMalloc1(dE + 1, &moduli));
4137   moduli[0] = dir;
4138   for (d = 0, e = 0; d < dE; ++d) moduli[d + 1] = d == dir ? 0.0 : (multipliers ? multipliers[e++] : 1.0);
4139   PetscCall(DMGetDS(cdm, &cds));
4140   PetscCall(PetscDSSetConstants(cds, dE + 1, moduli));
4141   PetscCall(DMPlexRemapGeometry(dm, 0.0, coordMap_shear));
4142   PetscCall(PetscFree(moduli));
4143   PetscFunctionReturn(PETSC_SUCCESS);
4144 }
4145