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