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