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