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