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