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