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