xref: /petsc/src/dm/impls/plex/plexgeometry.c (revision be39004295a98ced168607de69334a8b204d0588)
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_Internal(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
2598 . J    - the Jacobian of the transform from the reference element at each quadrature point
2599 . invJ - the inverse of the Jacobian at each quadrature point
2600 - detJ - the Jacobian determinant at each quadrature point
2601 
2602   Level: advanced
2603 
2604   Note:
2605   Implicit cell geometry must be used when the topological mesh dimension is not equal to the coordinate dimension, for instance for embedded manifolds.
2606 
2607 .seealso: `DMPLEX`, `DMGetCoordinateSection()`, `DMGetCoordinates()`
2608 @*/
2609 PetscErrorCode DMPlexComputeCellGeometryFEM(DM dm, PetscInt cell, PetscQuadrature quad, PetscReal *v, PetscReal J[], PetscReal invJ[], PetscReal *detJ)
2610 {
2611   DM       cdm;
2612   PetscFE  fe = NULL;
2613   PetscInt dim, cdim;
2614 
2615   PetscFunctionBegin;
2616   PetscAssertPointer(detJ, 7);
2617   PetscCall(DMGetDimension(dm, &dim));
2618   PetscCall(DMGetCoordinateDim(dm, &cdim));
2619   PetscCall(DMGetCoordinateDM(dm, &cdm));
2620   if (cdm) {
2621     PetscClassId id;
2622     PetscInt     numFields;
2623     PetscDS      prob;
2624     PetscObject  disc;
2625 
2626     PetscCall(DMGetNumFields(cdm, &numFields));
2627     if (numFields) {
2628       PetscCall(DMGetDS(cdm, &prob));
2629       PetscCall(PetscDSGetDiscretization(prob, 0, &disc));
2630       PetscCall(PetscObjectGetClassId(disc, &id));
2631       if (id == PETSCFE_CLASSID) fe = (PetscFE)disc;
2632     }
2633   }
2634   if (!fe || (dim != cdim)) PetscCall(DMPlexComputeCellGeometryFEM_Implicit(dm, cell, quad, v, J, invJ, detJ));
2635   else PetscCall(DMPlexComputeCellGeometryFEM_FE(dm, fe, cell, quad, v, J, invJ, detJ));
2636   PetscFunctionReturn(PETSC_SUCCESS);
2637 }
2638 
2639 static PetscErrorCode DMPlexComputeGeometryFVM_0D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2640 {
2641   PetscSection       coordSection;
2642   Vec                coordinates;
2643   const PetscScalar *coords = NULL;
2644   PetscInt           d, dof, off;
2645 
2646   PetscFunctionBegin;
2647   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
2648   PetscCall(DMGetCoordinateSection(dm, &coordSection));
2649   PetscCall(VecGetArrayRead(coordinates, &coords));
2650 
2651   /* for a point the centroid is just the coord */
2652   if (centroid) {
2653     PetscCall(PetscSectionGetDof(coordSection, cell, &dof));
2654     PetscCall(PetscSectionGetOffset(coordSection, cell, &off));
2655     for (d = 0; d < dof; d++) centroid[d] = PetscRealPart(coords[off + d]);
2656   }
2657   if (normal) {
2658     const PetscInt *support, *cones;
2659     PetscInt        supportSize;
2660     PetscReal       norm, sign;
2661 
2662     /* compute the norm based upon the support centroids */
2663     PetscCall(DMPlexGetSupportSize(dm, cell, &supportSize));
2664     PetscCall(DMPlexGetSupport(dm, cell, &support));
2665     PetscCall(DMPlexComputeCellGeometryFVM(dm, support[0], NULL, normal, NULL));
2666 
2667     /* Take the normal from the centroid of the support to the vertex*/
2668     PetscCall(PetscSectionGetDof(coordSection, cell, &dof));
2669     PetscCall(PetscSectionGetOffset(coordSection, cell, &off));
2670     for (d = 0; d < dof; d++) normal[d] -= PetscRealPart(coords[off + d]);
2671 
2672     /* Determine the sign of the normal based upon its location in the support */
2673     PetscCall(DMPlexGetCone(dm, support[0], &cones));
2674     sign = cones[0] == cell ? 1.0 : -1.0;
2675 
2676     norm = DMPlex_NormD_Internal(dim, normal);
2677     for (d = 0; d < dim; ++d) normal[d] /= (norm * sign);
2678   }
2679   if (vol) *vol = 1.0;
2680   PetscCall(VecRestoreArrayRead(coordinates, &coords));
2681   PetscFunctionReturn(PETSC_SUCCESS);
2682 }
2683 
2684 static PetscErrorCode DMPlexComputeGeometryFVM_1D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2685 {
2686   const PetscScalar *array;
2687   PetscScalar       *coords = NULL;
2688   PetscInt           cdim, coordSize, d;
2689   PetscBool          isDG;
2690 
2691   PetscFunctionBegin;
2692   PetscCall(DMGetCoordinateDim(dm, &cdim));
2693   PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2694   PetscCheck(coordSize == cdim * 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge has %" PetscInt_FMT " coordinates != %" PetscInt_FMT, coordSize, cdim * 2);
2695   if (centroid) {
2696     for (d = 0; d < cdim; ++d) centroid[d] = 0.5 * PetscRealPart(coords[d] + coords[cdim + d]);
2697   }
2698   if (normal) {
2699     PetscReal norm;
2700 
2701     switch (cdim) {
2702     case 3:
2703       normal[2] = 0.; /* fall through */
2704     case 2:
2705       normal[0] = -PetscRealPart(coords[1] - coords[cdim + 1]);
2706       normal[1] = PetscRealPart(coords[0] - coords[cdim + 0]);
2707       break;
2708     case 1:
2709       normal[0] = 1.0;
2710       break;
2711     default:
2712       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Dimension %" PetscInt_FMT " not supported", cdim);
2713     }
2714     norm = DMPlex_NormD_Internal(cdim, normal);
2715     for (d = 0; d < cdim; ++d) normal[d] /= norm;
2716   }
2717   if (vol) {
2718     *vol = 0.0;
2719     for (d = 0; d < cdim; ++d) *vol += PetscSqr(PetscRealPart(coords[d] - coords[cdim + d]));
2720     *vol = PetscSqrtReal(*vol);
2721   }
2722   PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2723   PetscFunctionReturn(PETSC_SUCCESS);
2724 }
2725 
2726 /* Centroid_i = (\sum_n A_n Cn_i) / A */
2727 static PetscErrorCode DMPlexComputeGeometryFVM_2D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2728 {
2729   DMPolytopeType     ct;
2730   const PetscScalar *array;
2731   PetscScalar       *coords = NULL;
2732   PetscInt           coordSize;
2733   PetscBool          isDG;
2734   PetscInt           fv[4] = {0, 1, 2, 3};
2735   PetscInt           cdim, numCorners, p, d;
2736 
2737   PetscFunctionBegin;
2738   /* Must check for hybrid cells because prisms have a different orientation scheme */
2739   PetscCall(DMPlexGetCellType(dm, cell, &ct));
2740   switch (ct) {
2741   case DM_POLYTOPE_SEG_PRISM_TENSOR:
2742     fv[2] = 3;
2743     fv[3] = 2;
2744     break;
2745   default:
2746     break;
2747   }
2748   PetscCall(DMGetCoordinateDim(dm, &cdim));
2749   PetscCall(DMPlexGetConeSize(dm, cell, &numCorners));
2750   PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2751   {
2752     PetscReal c[3] = {0., 0., 0.}, n[3] = {0., 0., 0.}, origin[3] = {0., 0., 0.}, norm;
2753 
2754     for (d = 0; d < cdim; d++) origin[d] = PetscRealPart(coords[d]);
2755     for (p = 0; p < numCorners - 2; ++p) {
2756       PetscReal e0[3] = {0., 0., 0.}, e1[3] = {0., 0., 0.};
2757       for (d = 0; d < cdim; d++) {
2758         e0[d] = PetscRealPart(coords[cdim * fv[p + 1] + d]) - origin[d];
2759         e1[d] = PetscRealPart(coords[cdim * fv[p + 2] + d]) - origin[d];
2760       }
2761       const PetscReal dx = e0[1] * e1[2] - e0[2] * e1[1];
2762       const PetscReal dy = e0[2] * e1[0] - e0[0] * e1[2];
2763       const PetscReal dz = e0[0] * e1[1] - e0[1] * e1[0];
2764       const PetscReal a  = PetscSqrtReal(dx * dx + dy * dy + dz * dz);
2765 
2766       n[0] += dx;
2767       n[1] += dy;
2768       n[2] += dz;
2769       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.;
2770     }
2771     norm = PetscSqrtReal(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
2772     // Allow zero volume cells
2773     if (norm != 0) {
2774       n[0] /= norm;
2775       n[1] /= norm;
2776       n[2] /= norm;
2777       c[0] /= norm;
2778       c[1] /= norm;
2779       c[2] /= norm;
2780     }
2781     if (vol) *vol = 0.5 * norm;
2782     if (centroid)
2783       for (d = 0; d < cdim; ++d) centroid[d] = c[d];
2784     if (normal)
2785       for (d = 0; d < cdim; ++d) normal[d] = n[d];
2786   }
2787   PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2788   PetscFunctionReturn(PETSC_SUCCESS);
2789 }
2790 
2791 /* Centroid_i = (\sum_n V_n Cn_i) / V */
2792 static PetscErrorCode DMPlexComputeGeometryFVM_3D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2793 {
2794   DMPolytopeType        ct;
2795   const PetscScalar    *array;
2796   PetscScalar          *coords = NULL;
2797   PetscInt              coordSize;
2798   PetscBool             isDG;
2799   PetscReal             vsum      = 0.0, vtmp, coordsTmp[3 * 3], origin[3];
2800   const PetscInt        order[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
2801   const PetscInt       *cone, *faceSizes, *faces;
2802   const DMPolytopeType *faceTypes;
2803   PetscBool             isHybrid = PETSC_FALSE;
2804   PetscInt              numFaces, f, fOff = 0, p, d;
2805 
2806   PetscFunctionBegin;
2807   PetscCheck(dim <= 3, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No support for dim %" PetscInt_FMT " > 3", dim);
2808   /* Must check for hybrid cells because prisms have a different orientation scheme */
2809   PetscCall(DMPlexGetCellType(dm, cell, &ct));
2810   switch (ct) {
2811   case DM_POLYTOPE_POINT_PRISM_TENSOR:
2812   case DM_POLYTOPE_SEG_PRISM_TENSOR:
2813   case DM_POLYTOPE_TRI_PRISM_TENSOR:
2814   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
2815     isHybrid = PETSC_TRUE;
2816   default:
2817     break;
2818   }
2819 
2820   if (centroid)
2821     for (d = 0; d < dim; ++d) centroid[d] = 0.0;
2822   PetscCall(DMPlexGetCone(dm, cell, &cone));
2823 
2824   // Using the closure of faces for coordinates does not work in periodic geometries, so we index into the cell coordinates
2825   PetscCall(DMPlexGetRawFaces_Internal(dm, ct, order, &numFaces, &faceTypes, &faceSizes, &faces));
2826   PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2827   for (f = 0; f < numFaces; ++f) {
2828     PetscBool flip = isHybrid && f == 0 ? PETSC_TRUE : PETSC_FALSE; /* The first hybrid face is reversed */
2829 
2830     // If using zero as the origin vertex for each tetrahedron, an element far from the origin will have positive and
2831     // negative volumes that nearly cancel, thus incurring rounding error. Here we define origin[] as the first vertex
2832     // so that all tetrahedra have positive volume.
2833     if (f == 0)
2834       for (d = 0; d < dim; d++) origin[d] = PetscRealPart(coords[d]);
2835     switch (faceTypes[f]) {
2836     case DM_POLYTOPE_TRIANGLE:
2837       for (d = 0; d < dim; ++d) {
2838         coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + 0] * dim + d]) - origin[d];
2839         coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + 1] * dim + d]) - origin[d];
2840         coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + 2] * dim + d]) - origin[d];
2841       }
2842       Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2843       if (flip) vtmp = -vtmp;
2844       vsum += vtmp;
2845       if (centroid) { /* Centroid of OABC = (a+b+c)/4 */
2846         for (d = 0; d < dim; ++d) {
2847           for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp;
2848         }
2849       }
2850       break;
2851     case DM_POLYTOPE_QUADRILATERAL:
2852     case DM_POLYTOPE_SEG_PRISM_TENSOR: {
2853       PetscInt fv[4] = {0, 1, 2, 3};
2854 
2855       /* Side faces for hybrid cells are stored as tensor products */
2856       if (isHybrid && f > 1) {
2857         fv[2] = 3;
2858         fv[3] = 2;
2859       }
2860       /* DO FOR PYRAMID */
2861       /* First tet */
2862       for (d = 0; d < dim; ++d) {
2863         coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + fv[0]] * dim + d]) - origin[d];
2864         coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + fv[1]] * dim + d]) - origin[d];
2865         coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + fv[3]] * dim + d]) - origin[d];
2866       }
2867       Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2868       if (flip) vtmp = -vtmp;
2869       vsum += vtmp;
2870       if (centroid) {
2871         for (d = 0; d < dim; ++d) {
2872           for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp;
2873         }
2874       }
2875       /* Second tet */
2876       for (d = 0; d < dim; ++d) {
2877         coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + fv[1]] * dim + d]) - origin[d];
2878         coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + fv[2]] * dim + d]) - origin[d];
2879         coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + fv[3]] * dim + d]) - origin[d];
2880       }
2881       Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2882       if (flip) vtmp = -vtmp;
2883       vsum += vtmp;
2884       if (centroid) {
2885         for (d = 0; d < dim; ++d) {
2886           for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp;
2887         }
2888       }
2889       break;
2890     }
2891     default:
2892       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle face %" PetscInt_FMT " of type %s", cone[f], DMPolytopeTypes[ct]);
2893     }
2894     fOff += faceSizes[f];
2895   }
2896   PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, order, &numFaces, &faceTypes, &faceSizes, &faces));
2897   PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2898   if (vol) *vol = PetscAbsReal(vsum);
2899   if (normal)
2900     for (d = 0; d < dim; ++d) normal[d] = 0.0;
2901   if (centroid)
2902     for (d = 0; d < dim; ++d) centroid[d] = centroid[d] / (vsum * 4) + origin[d];
2903   PetscFunctionReturn(PETSC_SUCCESS);
2904 }
2905 
2906 /*@C
2907   DMPlexComputeCellGeometryFVM - Compute the volume for a given cell
2908 
2909   Collective
2910 
2911   Input Parameters:
2912 + dm   - the `DMPLEX`
2913 - cell - the cell
2914 
2915   Output Parameters:
2916 + vol      - the cell volume
2917 . centroid - the cell centroid
2918 - normal   - the cell normal, if appropriate
2919 
2920   Level: advanced
2921 
2922 .seealso: `DMPLEX`, `DMGetCoordinateSection()`, `DMGetCoordinates()`
2923 @*/
2924 PetscErrorCode DMPlexComputeCellGeometryFVM(DM dm, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2925 {
2926   PetscInt depth, dim;
2927 
2928   PetscFunctionBegin;
2929   PetscCall(DMPlexGetDepth(dm, &depth));
2930   PetscCall(DMGetDimension(dm, &dim));
2931   PetscCheck(depth == dim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh must be interpolated");
2932   PetscCall(DMPlexGetPointDepth(dm, cell, &depth));
2933   switch (depth) {
2934   case 0:
2935     PetscCall(DMPlexComputeGeometryFVM_0D_Internal(dm, dim, cell, vol, centroid, normal));
2936     break;
2937   case 1:
2938     PetscCall(DMPlexComputeGeometryFVM_1D_Internal(dm, dim, cell, vol, centroid, normal));
2939     break;
2940   case 2:
2941     PetscCall(DMPlexComputeGeometryFVM_2D_Internal(dm, dim, cell, vol, centroid, normal));
2942     break;
2943   case 3:
2944     PetscCall(DMPlexComputeGeometryFVM_3D_Internal(dm, dim, cell, vol, centroid, normal));
2945     break;
2946   default:
2947     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unsupported dimension %" PetscInt_FMT " (depth %" PetscInt_FMT ") for element geometry computation", dim, depth);
2948   }
2949   PetscFunctionReturn(PETSC_SUCCESS);
2950 }
2951 
2952 /*@
2953   DMPlexComputeGeometryFVM - Computes the cell and face geometry for a finite volume method
2954 
2955   Input Parameter:
2956 . dm - The `DMPLEX`
2957 
2958   Output Parameters:
2959 + cellgeom - A `Vec` of `PetscFVCellGeom` data
2960 - facegeom - A `Vec` of `PetscFVFaceGeom` data
2961 
2962   Level: developer
2963 
2964 .seealso: `DMPLEX`, `PetscFVFaceGeom`, `PetscFVCellGeom`
2965 @*/
2966 PetscErrorCode DMPlexComputeGeometryFVM(DM dm, Vec *cellgeom, Vec *facegeom)
2967 {
2968   DM           dmFace, dmCell;
2969   DMLabel      ghostLabel;
2970   PetscSection sectionFace, sectionCell;
2971   PetscSection coordSection;
2972   Vec          coordinates;
2973   PetscScalar *fgeom, *cgeom;
2974   PetscReal    minradius, gminradius;
2975   PetscInt     dim, cStart, cEnd, cEndInterior, c, fStart, fEnd, f;
2976 
2977   PetscFunctionBegin;
2978   PetscCall(DMGetDimension(dm, &dim));
2979   PetscCall(DMGetCoordinateSection(dm, &coordSection));
2980   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
2981   /* Make cell centroids and volumes */
2982   PetscCall(DMClone(dm, &dmCell));
2983   PetscCall(DMSetCoordinateSection(dmCell, PETSC_DETERMINE, coordSection));
2984   PetscCall(DMSetCoordinatesLocal(dmCell, coordinates));
2985   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &sectionCell));
2986   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2987   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
2988   PetscCall(PetscSectionSetChart(sectionCell, cStart, cEnd));
2989   for (c = cStart; c < cEnd; ++c) PetscCall(PetscSectionSetDof(sectionCell, c, (PetscInt)PetscCeilReal(((PetscReal)sizeof(PetscFVCellGeom)) / sizeof(PetscScalar))));
2990   PetscCall(PetscSectionSetUp(sectionCell));
2991   PetscCall(DMSetLocalSection(dmCell, sectionCell));
2992   PetscCall(PetscSectionDestroy(&sectionCell));
2993   PetscCall(DMCreateLocalVector(dmCell, cellgeom));
2994   if (cEndInterior < 0) cEndInterior = cEnd;
2995   PetscCall(VecGetArray(*cellgeom, &cgeom));
2996   for (c = cStart; c < cEndInterior; ++c) {
2997     PetscFVCellGeom *cg;
2998 
2999     PetscCall(DMPlexPointLocalRef(dmCell, c, cgeom, &cg));
3000     PetscCall(PetscArrayzero(cg, 1));
3001     PetscCall(DMPlexComputeCellGeometryFVM(dmCell, c, &cg->volume, cg->centroid, NULL));
3002   }
3003   /* Compute face normals and minimum cell radius */
3004   PetscCall(DMClone(dm, &dmFace));
3005   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &sectionFace));
3006   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
3007   PetscCall(PetscSectionSetChart(sectionFace, fStart, fEnd));
3008   for (f = fStart; f < fEnd; ++f) PetscCall(PetscSectionSetDof(sectionFace, f, (PetscInt)PetscCeilReal(((PetscReal)sizeof(PetscFVFaceGeom)) / sizeof(PetscScalar))));
3009   PetscCall(PetscSectionSetUp(sectionFace));
3010   PetscCall(DMSetLocalSection(dmFace, sectionFace));
3011   PetscCall(PetscSectionDestroy(&sectionFace));
3012   PetscCall(DMCreateLocalVector(dmFace, facegeom));
3013   PetscCall(VecGetArray(*facegeom, &fgeom));
3014   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3015   minradius = PETSC_MAX_REAL;
3016   for (f = fStart; f < fEnd; ++f) {
3017     PetscFVFaceGeom *fg;
3018     PetscReal        area;
3019     const PetscInt  *cells;
3020     PetscInt         ncells, ghost = -1, d, numChildren;
3021 
3022     if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost));
3023     PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL));
3024     PetscCall(DMPlexGetSupport(dm, f, &cells));
3025     PetscCall(DMPlexGetSupportSize(dm, f, &ncells));
3026     /* It is possible to get a face with no support when using partition overlap */
3027     if (!ncells || ghost >= 0 || numChildren) continue;
3028     PetscCall(DMPlexPointLocalRef(dmFace, f, fgeom, &fg));
3029     PetscCall(DMPlexComputeCellGeometryFVM(dm, f, &area, fg->centroid, fg->normal));
3030     for (d = 0; d < dim; ++d) fg->normal[d] *= area;
3031     /* Flip face orientation if necessary to match ordering in support, and Update minimum radius */
3032     {
3033       PetscFVCellGeom *cL, *cR;
3034       PetscReal       *lcentroid, *rcentroid;
3035       PetscReal        l[3], r[3], v[3];
3036 
3037       PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cgeom, &cL));
3038       lcentroid = cells[0] >= cEndInterior ? fg->centroid : cL->centroid;
3039       if (ncells > 1) {
3040         PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cgeom, &cR));
3041         rcentroid = cells[1] >= cEndInterior ? fg->centroid : cR->centroid;
3042       } else {
3043         rcentroid = fg->centroid;
3044       }
3045       PetscCall(DMLocalizeCoordinateReal_Internal(dm, dim, fg->centroid, lcentroid, l));
3046       PetscCall(DMLocalizeCoordinateReal_Internal(dm, dim, fg->centroid, rcentroid, r));
3047       DMPlex_WaxpyD_Internal(dim, -1, l, r, v);
3048       if (DMPlex_DotRealD_Internal(dim, fg->normal, v) < 0) {
3049         for (d = 0; d < dim; ++d) fg->normal[d] = -fg->normal[d];
3050       }
3051       if (DMPlex_DotRealD_Internal(dim, fg->normal, v) <= 0) {
3052         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]);
3053         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]);
3054         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Direction for face %" PetscInt_FMT " could not be fixed", f);
3055       }
3056       if (cells[0] < cEndInterior) {
3057         DMPlex_WaxpyD_Internal(dim, -1, fg->centroid, cL->centroid, v);
3058         minradius = PetscMin(minradius, DMPlex_NormD_Internal(dim, v));
3059       }
3060       if (ncells > 1 && cells[1] < cEndInterior) {
3061         DMPlex_WaxpyD_Internal(dim, -1, fg->centroid, cR->centroid, v);
3062         minradius = PetscMin(minradius, DMPlex_NormD_Internal(dim, v));
3063       }
3064     }
3065   }
3066   PetscCallMPI(MPIU_Allreduce(&minradius, &gminradius, 1, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
3067   PetscCall(DMPlexSetMinRadius(dm, gminradius));
3068   /* Compute centroids of ghost cells */
3069   for (c = cEndInterior; c < cEnd; ++c) {
3070     PetscFVFaceGeom *fg;
3071     const PetscInt  *cone, *support;
3072     PetscInt         coneSize, supportSize, s;
3073 
3074     PetscCall(DMPlexGetConeSize(dmCell, c, &coneSize));
3075     PetscCheck(coneSize == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Ghost cell %" PetscInt_FMT " has cone size %" PetscInt_FMT " != 1", c, coneSize);
3076     PetscCall(DMPlexGetCone(dmCell, c, &cone));
3077     PetscCall(DMPlexGetSupportSize(dmCell, cone[0], &supportSize));
3078     PetscCheck(supportSize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[0], supportSize);
3079     PetscCall(DMPlexGetSupport(dmCell, cone[0], &support));
3080     PetscCall(DMPlexPointLocalRef(dmFace, cone[0], fgeom, &fg));
3081     for (s = 0; s < 2; ++s) {
3082       /* Reflect ghost centroid across plane of face */
3083       if (support[s] == c) {
3084         PetscFVCellGeom *ci;
3085         PetscFVCellGeom *cg;
3086         PetscReal        c2f[3], a;
3087 
3088         PetscCall(DMPlexPointLocalRead(dmCell, support[(s + 1) % 2], cgeom, &ci));
3089         DMPlex_WaxpyD_Internal(dim, -1, ci->centroid, fg->centroid, c2f); /* cell to face centroid */
3090         a = DMPlex_DotRealD_Internal(dim, c2f, fg->normal) / DMPlex_DotRealD_Internal(dim, fg->normal, fg->normal);
3091         PetscCall(DMPlexPointLocalRef(dmCell, support[s], cgeom, &cg));
3092         DMPlex_WaxpyD_Internal(dim, 2 * a, fg->normal, ci->centroid, cg->centroid);
3093         cg->volume = ci->volume;
3094       }
3095     }
3096   }
3097   PetscCall(VecRestoreArray(*facegeom, &fgeom));
3098   PetscCall(VecRestoreArray(*cellgeom, &cgeom));
3099   PetscCall(DMDestroy(&dmCell));
3100   PetscCall(DMDestroy(&dmFace));
3101   PetscFunctionReturn(PETSC_SUCCESS);
3102 }
3103 
3104 /*@
3105   DMPlexGetMinRadius - Returns the minimum distance from any cell centroid to a face
3106 
3107   Not Collective
3108 
3109   Input Parameter:
3110 . dm - the `DMPLEX`
3111 
3112   Output Parameter:
3113 . minradius - the minimum cell radius
3114 
3115   Level: developer
3116 
3117 .seealso: `DMPLEX`, `DMGetCoordinates()`
3118 @*/
3119 PetscErrorCode DMPlexGetMinRadius(DM dm, PetscReal *minradius)
3120 {
3121   PetscFunctionBegin;
3122   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3123   PetscAssertPointer(minradius, 2);
3124   *minradius = ((DM_Plex *)dm->data)->minradius;
3125   PetscFunctionReturn(PETSC_SUCCESS);
3126 }
3127 
3128 /*@
3129   DMPlexSetMinRadius - Sets the minimum distance from the cell centroid to a face
3130 
3131   Logically Collective
3132 
3133   Input Parameters:
3134 + dm        - the `DMPLEX`
3135 - minradius - the minimum cell radius
3136 
3137   Level: developer
3138 
3139 .seealso: `DMPLEX`, `DMSetCoordinates()`
3140 @*/
3141 PetscErrorCode DMPlexSetMinRadius(DM dm, PetscReal minradius)
3142 {
3143   PetscFunctionBegin;
3144   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3145   ((DM_Plex *)dm->data)->minradius = minradius;
3146   PetscFunctionReturn(PETSC_SUCCESS);
3147 }
3148 
3149 /*@C
3150   DMPlexGetCoordinateMap - Returns the function used to map coordinates of newly generated mesh points
3151 
3152   Not Collective
3153 
3154   Input Parameter:
3155 . dm - the `DMPLEX`
3156 
3157   Output Parameter:
3158 . coordFunc - the mapping function
3159 
3160   Level: developer
3161 
3162   Note:
3163   This function maps from the gnerated 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,
3164 
3165 .seealso: `DMPLEX`, `DMGetCoordinates()`, `DMPlexSetCoordinateMap()`, `PetscPointFn`
3166 @*/
3167 PetscErrorCode DMPlexGetCoordinateMap(DM dm, PetscPointFn **coordFunc)
3168 {
3169   PetscFunctionBegin;
3170   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3171   PetscAssertPointer(coordFunc, 2);
3172   *coordFunc = ((DM_Plex *)dm->data)->coordFunc;
3173   PetscFunctionReturn(PETSC_SUCCESS);
3174 }
3175 
3176 /*@C
3177   DMPlexSetCoordinateMap - Sets the function used to map coordinates of newly generated mesh points
3178 
3179   Logically Collective
3180 
3181   Input Parameters:
3182 + dm        - the `DMPLEX`
3183 - coordFunc - the mapping function
3184 
3185   Level: developer
3186 
3187   Note:
3188   This function maps from the gnerated 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,
3189 
3190 .seealso: `DMPLEX`, `DMSetCoordinates()`, `DMPlexGetCoordinateMap()`, `PetscPointFn`
3191 @*/
3192 PetscErrorCode DMPlexSetCoordinateMap(DM dm, PetscPointFn *coordFunc)
3193 {
3194   PetscFunctionBegin;
3195   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3196   ((DM_Plex *)dm->data)->coordFunc = coordFunc;
3197   PetscFunctionReturn(PETSC_SUCCESS);
3198 }
3199 
3200 static PetscErrorCode BuildGradientReconstruction_Internal(DM dm, PetscFV fvm, DM dmFace, PetscScalar *fgeom, DM dmCell, PetscScalar *cgeom)
3201 {
3202   DMLabel      ghostLabel;
3203   PetscScalar *dx, *grad, **gref;
3204   PetscInt     dim, cStart, cEnd, c, cEndInterior, maxNumFaces;
3205 
3206   PetscFunctionBegin;
3207   PetscCall(DMGetDimension(dm, &dim));
3208   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
3209   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
3210   cEndInterior = cEndInterior < 0 ? cEnd : cEndInterior;
3211   PetscCall(DMPlexGetMaxSizes(dm, &maxNumFaces, NULL));
3212   PetscCall(PetscFVLeastSquaresSetMaxFaces(fvm, maxNumFaces));
3213   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3214   PetscCall(PetscMalloc3(maxNumFaces * dim, &dx, maxNumFaces * dim, &grad, maxNumFaces, &gref));
3215   for (c = cStart; c < cEndInterior; c++) {
3216     const PetscInt  *faces;
3217     PetscInt         numFaces, usedFaces, f, d;
3218     PetscFVCellGeom *cg;
3219     PetscBool        boundary;
3220     PetscInt         ghost;
3221 
3222     // do not attempt to compute a gradient reconstruction stencil in a ghost cell.  It will never be used
3223     PetscCall(DMLabelGetValue(ghostLabel, c, &ghost));
3224     if (ghost >= 0) continue;
3225 
3226     PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg));
3227     PetscCall(DMPlexGetConeSize(dm, c, &numFaces));
3228     PetscCall(DMPlexGetCone(dm, c, &faces));
3229     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);
3230     for (f = 0, usedFaces = 0; f < numFaces; ++f) {
3231       PetscFVCellGeom *cg1;
3232       PetscFVFaceGeom *fg;
3233       const PetscInt  *fcells;
3234       PetscInt         ncell, side;
3235 
3236       PetscCall(DMLabelGetValue(ghostLabel, faces[f], &ghost));
3237       PetscCall(DMIsBoundaryPoint(dm, faces[f], &boundary));
3238       if ((ghost >= 0) || boundary) continue;
3239       PetscCall(DMPlexGetSupport(dm, faces[f], &fcells));
3240       side  = (c != fcells[0]); /* c is on left=0 or right=1 of face */
3241       ncell = fcells[!side];    /* the neighbor */
3242       PetscCall(DMPlexPointLocalRef(dmFace, faces[f], fgeom, &fg));
3243       PetscCall(DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1));
3244       for (d = 0; d < dim; ++d) dx[usedFaces * dim + d] = cg1->centroid[d] - cg->centroid[d];
3245       gref[usedFaces++] = fg->grad[side]; /* Gradient reconstruction term will go here */
3246     }
3247     PetscCheck(usedFaces, PETSC_COMM_SELF, PETSC_ERR_USER, "Mesh contains isolated cell (no neighbors). Is it intentional?");
3248     PetscCall(PetscFVComputeGradient(fvm, usedFaces, dx, grad));
3249     for (f = 0, usedFaces = 0; f < numFaces; ++f) {
3250       PetscCall(DMLabelGetValue(ghostLabel, faces[f], &ghost));
3251       PetscCall(DMIsBoundaryPoint(dm, faces[f], &boundary));
3252       if ((ghost >= 0) || boundary) continue;
3253       for (d = 0; d < dim; ++d) gref[usedFaces][d] = grad[usedFaces * dim + d];
3254       ++usedFaces;
3255     }
3256   }
3257   PetscCall(PetscFree3(dx, grad, gref));
3258   PetscFunctionReturn(PETSC_SUCCESS);
3259 }
3260 
3261 static PetscErrorCode BuildGradientReconstruction_Internal_Tree(DM dm, PetscFV fvm, DM dmFace, PetscScalar *fgeom, DM dmCell, PetscScalar *cgeom)
3262 {
3263   DMLabel      ghostLabel;
3264   PetscScalar *dx, *grad, **gref;
3265   PetscInt     dim, cStart, cEnd, c, cEndInterior, fStart, fEnd, f, nStart, nEnd, maxNumFaces = 0;
3266   PetscSection neighSec;
3267   PetscInt (*neighbors)[2];
3268   PetscInt *counter;
3269 
3270   PetscFunctionBegin;
3271   PetscCall(DMGetDimension(dm, &dim));
3272   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
3273   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
3274   if (cEndInterior < 0) cEndInterior = cEnd;
3275   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &neighSec));
3276   PetscCall(PetscSectionSetChart(neighSec, cStart, cEndInterior));
3277   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
3278   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3279   for (f = fStart; f < fEnd; f++) {
3280     const PetscInt *fcells;
3281     PetscBool       boundary;
3282     PetscInt        ghost = -1;
3283     PetscInt        numChildren, numCells, c;
3284 
3285     if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost));
3286     PetscCall(DMIsBoundaryPoint(dm, f, &boundary));
3287     PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL));
3288     if ((ghost >= 0) || boundary || numChildren) continue;
3289     PetscCall(DMPlexGetSupportSize(dm, f, &numCells));
3290     if (numCells == 2) {
3291       PetscCall(DMPlexGetSupport(dm, f, &fcells));
3292       for (c = 0; c < 2; c++) {
3293         PetscInt cell = fcells[c];
3294 
3295         if (cell >= cStart && cell < cEndInterior) PetscCall(PetscSectionAddDof(neighSec, cell, 1));
3296       }
3297     }
3298   }
3299   PetscCall(PetscSectionSetUp(neighSec));
3300   PetscCall(PetscSectionGetMaxDof(neighSec, &maxNumFaces));
3301   PetscCall(PetscFVLeastSquaresSetMaxFaces(fvm, maxNumFaces));
3302   nStart = 0;
3303   PetscCall(PetscSectionGetStorageSize(neighSec, &nEnd));
3304   PetscCall(PetscMalloc1(nEnd - nStart, &neighbors));
3305   PetscCall(PetscCalloc1(cEndInterior - cStart, &counter));
3306   for (f = fStart; f < fEnd; f++) {
3307     const PetscInt *fcells;
3308     PetscBool       boundary;
3309     PetscInt        ghost = -1;
3310     PetscInt        numChildren, numCells, c;
3311 
3312     if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost));
3313     PetscCall(DMIsBoundaryPoint(dm, f, &boundary));
3314     PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL));
3315     if ((ghost >= 0) || boundary || numChildren) continue;
3316     PetscCall(DMPlexGetSupportSize(dm, f, &numCells));
3317     if (numCells == 2) {
3318       PetscCall(DMPlexGetSupport(dm, f, &fcells));
3319       for (c = 0; c < 2; c++) {
3320         PetscInt cell = fcells[c], off;
3321 
3322         if (cell >= cStart && cell < cEndInterior) {
3323           PetscCall(PetscSectionGetOffset(neighSec, cell, &off));
3324           off += counter[cell - cStart]++;
3325           neighbors[off][0] = f;
3326           neighbors[off][1] = fcells[1 - c];
3327         }
3328       }
3329     }
3330   }
3331   PetscCall(PetscFree(counter));
3332   PetscCall(PetscMalloc3(maxNumFaces * dim, &dx, maxNumFaces * dim, &grad, maxNumFaces, &gref));
3333   for (c = cStart; c < cEndInterior; c++) {
3334     PetscInt         numFaces, f, d, off, ghost = -1;
3335     PetscFVCellGeom *cg;
3336 
3337     PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg));
3338     PetscCall(PetscSectionGetDof(neighSec, c, &numFaces));
3339     PetscCall(PetscSectionGetOffset(neighSec, c, &off));
3340 
3341     // do not attempt to compute a gradient reconstruction stencil in a ghost cell.  It will never be used
3342     if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, c, &ghost));
3343     if (ghost >= 0) continue;
3344 
3345     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);
3346     for (f = 0; f < numFaces; ++f) {
3347       PetscFVCellGeom *cg1;
3348       PetscFVFaceGeom *fg;
3349       const PetscInt  *fcells;
3350       PetscInt         ncell, side, nface;
3351 
3352       nface = neighbors[off + f][0];
3353       ncell = neighbors[off + f][1];
3354       PetscCall(DMPlexGetSupport(dm, nface, &fcells));
3355       side = (c != fcells[0]);
3356       PetscCall(DMPlexPointLocalRef(dmFace, nface, fgeom, &fg));
3357       PetscCall(DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1));
3358       for (d = 0; d < dim; ++d) dx[f * dim + d] = cg1->centroid[d] - cg->centroid[d];
3359       gref[f] = fg->grad[side]; /* Gradient reconstruction term will go here */
3360     }
3361     PetscCall(PetscFVComputeGradient(fvm, numFaces, dx, grad));
3362     for (f = 0; f < numFaces; ++f) {
3363       for (d = 0; d < dim; ++d) gref[f][d] = grad[f * dim + d];
3364     }
3365   }
3366   PetscCall(PetscFree3(dx, grad, gref));
3367   PetscCall(PetscSectionDestroy(&neighSec));
3368   PetscCall(PetscFree(neighbors));
3369   PetscFunctionReturn(PETSC_SUCCESS);
3370 }
3371 
3372 /*@
3373   DMPlexComputeGradientFVM - Compute geometric factors for gradient reconstruction, which are stored in the geometry data, and compute layout for gradient data
3374 
3375   Collective
3376 
3377   Input Parameters:
3378 + dm           - The `DMPLEX`
3379 . fvm          - The `PetscFV`
3380 - cellGeometry - The face geometry from `DMPlexComputeCellGeometryFVM()`
3381 
3382   Input/Output Parameter:
3383 . faceGeometry - The face geometry from `DMPlexComputeFaceGeometryFVM()`; on output
3384                  the geometric factors for gradient calculation are inserted
3385 
3386   Output Parameter:
3387 . dmGrad - The `DM` describing the layout of gradient data
3388 
3389   Level: developer
3390 
3391 .seealso: `DMPLEX`, `DMPlexGetFaceGeometryFVM()`, `DMPlexGetCellGeometryFVM()`
3392 @*/
3393 PetscErrorCode DMPlexComputeGradientFVM(DM dm, PetscFV fvm, Vec faceGeometry, Vec cellGeometry, DM *dmGrad)
3394 {
3395   DM           dmFace, dmCell;
3396   PetscScalar *fgeom, *cgeom;
3397   PetscSection sectionGrad, parentSection;
3398   PetscInt     dim, pdim, cStart, cEnd, cEndInterior, c;
3399 
3400   PetscFunctionBegin;
3401   PetscCall(DMGetDimension(dm, &dim));
3402   PetscCall(PetscFVGetNumComponents(fvm, &pdim));
3403   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
3404   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
3405   /* Construct the interpolant corresponding to each face from the least-square solution over the cell neighborhood */
3406   PetscCall(VecGetDM(faceGeometry, &dmFace));
3407   PetscCall(VecGetDM(cellGeometry, &dmCell));
3408   PetscCall(VecGetArray(faceGeometry, &fgeom));
3409   PetscCall(VecGetArray(cellGeometry, &cgeom));
3410   PetscCall(DMPlexGetTree(dm, &parentSection, NULL, NULL, NULL, NULL));
3411   if (!parentSection) {
3412     PetscCall(BuildGradientReconstruction_Internal(dm, fvm, dmFace, fgeom, dmCell, cgeom));
3413   } else {
3414     PetscCall(BuildGradientReconstruction_Internal_Tree(dm, fvm, dmFace, fgeom, dmCell, cgeom));
3415   }
3416   PetscCall(VecRestoreArray(faceGeometry, &fgeom));
3417   PetscCall(VecRestoreArray(cellGeometry, &cgeom));
3418   /* Create storage for gradients */
3419   PetscCall(DMClone(dm, dmGrad));
3420   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &sectionGrad));
3421   PetscCall(PetscSectionSetChart(sectionGrad, cStart, cEnd));
3422   for (c = cStart; c < cEnd; ++c) PetscCall(PetscSectionSetDof(sectionGrad, c, pdim * dim));
3423   PetscCall(PetscSectionSetUp(sectionGrad));
3424   PetscCall(DMSetLocalSection(*dmGrad, sectionGrad));
3425   PetscCall(PetscSectionDestroy(&sectionGrad));
3426   PetscFunctionReturn(PETSC_SUCCESS);
3427 }
3428 
3429 /*@
3430   DMPlexGetDataFVM - Retrieve precomputed cell geometry
3431 
3432   Collective
3433 
3434   Input Parameters:
3435 + dm - The `DM`
3436 - fv - The `PetscFV`
3437 
3438   Output Parameters:
3439 + cellgeom - The cell geometry
3440 . facegeom - The face geometry
3441 - gradDM   - The gradient matrices
3442 
3443   Level: developer
3444 
3445 .seealso: `DMPLEX`, `DMPlexComputeGeometryFVM()`
3446 @*/
3447 PetscErrorCode DMPlexGetDataFVM(DM dm, PetscFV fv, Vec *cellgeom, Vec *facegeom, DM *gradDM)
3448 {
3449   PetscObject cellgeomobj, facegeomobj;
3450 
3451   PetscFunctionBegin;
3452   PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_cellgeom_fvm", &cellgeomobj));
3453   if (!cellgeomobj) {
3454     Vec cellgeomInt, facegeomInt;
3455 
3456     PetscCall(DMPlexComputeGeometryFVM(dm, &cellgeomInt, &facegeomInt));
3457     PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_cellgeom_fvm", (PetscObject)cellgeomInt));
3458     PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_facegeom_fvm", (PetscObject)facegeomInt));
3459     PetscCall(VecDestroy(&cellgeomInt));
3460     PetscCall(VecDestroy(&facegeomInt));
3461     PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_cellgeom_fvm", &cellgeomobj));
3462   }
3463   PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_facegeom_fvm", &facegeomobj));
3464   if (cellgeom) *cellgeom = (Vec)cellgeomobj;
3465   if (facegeom) *facegeom = (Vec)facegeomobj;
3466   if (gradDM) {
3467     PetscObject gradobj;
3468     PetscBool   computeGradients;
3469 
3470     PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
3471     if (!computeGradients) {
3472       *gradDM = NULL;
3473       PetscFunctionReturn(PETSC_SUCCESS);
3474     }
3475     PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_dmgrad_fvm", &gradobj));
3476     if (!gradobj) {
3477       DM dmGradInt;
3478 
3479       PetscCall(DMPlexComputeGradientFVM(dm, fv, (Vec)facegeomobj, (Vec)cellgeomobj, &dmGradInt));
3480       PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_dmgrad_fvm", (PetscObject)dmGradInt));
3481       PetscCall(DMDestroy(&dmGradInt));
3482       PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_dmgrad_fvm", &gradobj));
3483     }
3484     *gradDM = (DM)gradobj;
3485   }
3486   PetscFunctionReturn(PETSC_SUCCESS);
3487 }
3488 
3489 static PetscErrorCode DMPlexCoordinatesToReference_NewtonUpdate(PetscInt dimC, PetscInt dimR, PetscScalar *J, PetscScalar *invJ, PetscScalar *work, PetscReal *resNeg, PetscReal *guess)
3490 {
3491   PetscInt l, m;
3492 
3493   PetscFunctionBeginHot;
3494   if (dimC == dimR && dimR <= 3) {
3495     /* invert Jacobian, multiply */
3496     PetscScalar det, idet;
3497 
3498     switch (dimR) {
3499     case 1:
3500       invJ[0] = 1. / J[0];
3501       break;
3502     case 2:
3503       det     = J[0] * J[3] - J[1] * J[2];
3504       idet    = 1. / det;
3505       invJ[0] = J[3] * idet;
3506       invJ[1] = -J[1] * idet;
3507       invJ[2] = -J[2] * idet;
3508       invJ[3] = J[0] * idet;
3509       break;
3510     case 3: {
3511       invJ[0] = J[4] * J[8] - J[5] * J[7];
3512       invJ[1] = J[2] * J[7] - J[1] * J[8];
3513       invJ[2] = J[1] * J[5] - J[2] * J[4];
3514       det     = invJ[0] * J[0] + invJ[1] * J[3] + invJ[2] * J[6];
3515       idet    = 1. / det;
3516       invJ[0] *= idet;
3517       invJ[1] *= idet;
3518       invJ[2] *= idet;
3519       invJ[3] = idet * (J[5] * J[6] - J[3] * J[8]);
3520       invJ[4] = idet * (J[0] * J[8] - J[2] * J[6]);
3521       invJ[5] = idet * (J[2] * J[3] - J[0] * J[5]);
3522       invJ[6] = idet * (J[3] * J[7] - J[4] * J[6]);
3523       invJ[7] = idet * (J[1] * J[6] - J[0] * J[7]);
3524       invJ[8] = idet * (J[0] * J[4] - J[1] * J[3]);
3525     } break;
3526     }
3527     for (l = 0; l < dimR; l++) {
3528       for (m = 0; m < dimC; m++) guess[l] += PetscRealPart(invJ[l * dimC + m]) * resNeg[m];
3529     }
3530   } else {
3531 #if defined(PETSC_USE_COMPLEX)
3532     char transpose = 'C';
3533 #else
3534     char transpose = 'T';
3535 #endif
3536     PetscBLASInt m, n, one = 1, worksize, info;
3537 
3538     PetscCall(PetscBLASIntCast(dimR, &m));
3539     PetscCall(PetscBLASIntCast(dimC, &n));
3540     PetscCall(PetscBLASIntCast(dimC * dimC, &worksize));
3541     for (l = 0; l < dimC; l++) invJ[l] = resNeg[l];
3542 
3543     PetscCallBLAS("LAPACKgels", LAPACKgels_(&transpose, &m, &n, &one, J, &m, invJ, &n, work, &worksize, &info));
3544     PetscCheck(info == 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "Bad argument to GELS %" PetscBLASInt_FMT, info);
3545 
3546     for (l = 0; l < dimR; l++) guess[l] += PetscRealPart(invJ[l]);
3547   }
3548   PetscFunctionReturn(PETSC_SUCCESS);
3549 }
3550 
3551 static PetscErrorCode DMPlexCoordinatesToReference_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[], Vec coords, PetscInt dimC, PetscInt dimR)
3552 {
3553   PetscInt     coordSize, i, j, k, l, m, maxIts = 7, numV = (1 << dimR);
3554   PetscScalar *coordsScalar = NULL;
3555   PetscReal   *cellData, *cellCoords, *cellCoeffs, *extJ, *resNeg;
3556   PetscScalar *J, *invJ, *work;
3557 
3558   PetscFunctionBegin;
3559   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3560   PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3561   PetscCheck(coordSize >= dimC * numV, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expecting at least %" PetscInt_FMT " coordinates, got %" PetscInt_FMT, dimC * (1 << dimR), coordSize);
3562   PetscCall(DMGetWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData));
3563   PetscCall(DMGetWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J));
3564   cellCoords = &cellData[0];
3565   cellCoeffs = &cellData[coordSize];
3566   extJ       = &cellData[2 * coordSize];
3567   resNeg     = &cellData[2 * coordSize + dimR];
3568   invJ       = &J[dimR * dimC];
3569   work       = &J[2 * dimR * dimC];
3570   if (dimR == 2) {
3571     const PetscInt zToPlex[4] = {0, 1, 3, 2};
3572 
3573     for (i = 0; i < 4; i++) {
3574       PetscInt plexI = zToPlex[i];
3575 
3576       for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3577     }
3578   } else if (dimR == 3) {
3579     const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
3580 
3581     for (i = 0; i < 8; i++) {
3582       PetscInt plexI = zToPlex[i];
3583 
3584       for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3585     }
3586   } else {
3587     for (i = 0; i < coordSize; i++) cellCoords[i] = PetscRealPart(coordsScalar[i]);
3588   }
3589   /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */
3590   for (i = 0; i < dimR; i++) {
3591     PetscReal *swap;
3592 
3593     for (j = 0; j < (numV / 2); j++) {
3594       for (k = 0; k < dimC; k++) {
3595         cellCoeffs[dimC * j + k]                = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]);
3596         cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]);
3597       }
3598     }
3599 
3600     if (i < dimR - 1) {
3601       swap       = cellCoeffs;
3602       cellCoeffs = cellCoords;
3603       cellCoords = swap;
3604     }
3605   }
3606   PetscCall(PetscArrayzero(refCoords, numPoints * dimR));
3607   for (j = 0; j < numPoints; j++) {
3608     for (i = 0; i < maxIts; i++) {
3609       PetscReal *guess = &refCoords[dimR * j];
3610 
3611       /* compute -residual and Jacobian */
3612       for (k = 0; k < dimC; k++) resNeg[k] = realCoords[dimC * j + k];
3613       for (k = 0; k < dimC * dimR; k++) J[k] = 0.;
3614       for (k = 0; k < numV; k++) {
3615         PetscReal extCoord = 1.;
3616         for (l = 0; l < dimR; l++) {
3617           PetscReal coord = guess[l];
3618           PetscInt  dep   = (k & (1 << l)) >> l;
3619 
3620           extCoord *= dep * coord + !dep;
3621           extJ[l] = dep;
3622 
3623           for (m = 0; m < dimR; m++) {
3624             PetscReal coord = guess[m];
3625             PetscInt  dep   = ((k & (1 << m)) >> m) && (m != l);
3626             PetscReal mult  = dep * coord + !dep;
3627 
3628             extJ[l] *= mult;
3629           }
3630         }
3631         for (l = 0; l < dimC; l++) {
3632           PetscReal coeff = cellCoeffs[dimC * k + l];
3633 
3634           resNeg[l] -= coeff * extCoord;
3635           for (m = 0; m < dimR; m++) J[dimR * l + m] += coeff * extJ[m];
3636         }
3637       }
3638       if (0 && PetscDefined(USE_DEBUG)) {
3639         PetscReal maxAbs = 0.;
3640 
3641         for (l = 0; l < dimC; l++) maxAbs = PetscMax(maxAbs, PetscAbsReal(resNeg[l]));
3642         PetscCall(PetscInfo(dm, "cell %" PetscInt_FMT ", point %" PetscInt_FMT ", iter %" PetscInt_FMT ": res %g\n", cell, j, i, (double)maxAbs));
3643       }
3644 
3645       PetscCall(DMPlexCoordinatesToReference_NewtonUpdate(dimC, dimR, J, invJ, work, resNeg, guess));
3646     }
3647   }
3648   PetscCall(DMRestoreWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J));
3649   PetscCall(DMRestoreWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData));
3650   PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3651   PetscFunctionReturn(PETSC_SUCCESS);
3652 }
3653 
3654 static PetscErrorCode DMPlexReferenceToCoordinates_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt dimC, PetscInt dimR)
3655 {
3656   PetscInt     coordSize, i, j, k, l, numV = (1 << dimR);
3657   PetscScalar *coordsScalar = NULL;
3658   PetscReal   *cellData, *cellCoords, *cellCoeffs;
3659 
3660   PetscFunctionBegin;
3661   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3662   PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3663   PetscCheck(coordSize >= dimC * numV, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expecting at least %" PetscInt_FMT " coordinates, got %" PetscInt_FMT, dimC * (1 << dimR), coordSize);
3664   PetscCall(DMGetWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData));
3665   cellCoords = &cellData[0];
3666   cellCoeffs = &cellData[coordSize];
3667   if (dimR == 2) {
3668     const PetscInt zToPlex[4] = {0, 1, 3, 2};
3669 
3670     for (i = 0; i < 4; i++) {
3671       PetscInt plexI = zToPlex[i];
3672 
3673       for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3674     }
3675   } else if (dimR == 3) {
3676     const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
3677 
3678     for (i = 0; i < 8; i++) {
3679       PetscInt plexI = zToPlex[i];
3680 
3681       for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3682     }
3683   } else {
3684     for (i = 0; i < coordSize; i++) cellCoords[i] = PetscRealPart(coordsScalar[i]);
3685   }
3686   /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */
3687   for (i = 0; i < dimR; i++) {
3688     PetscReal *swap;
3689 
3690     for (j = 0; j < (numV / 2); j++) {
3691       for (k = 0; k < dimC; k++) {
3692         cellCoeffs[dimC * j + k]                = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]);
3693         cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]);
3694       }
3695     }
3696 
3697     if (i < dimR - 1) {
3698       swap       = cellCoeffs;
3699       cellCoeffs = cellCoords;
3700       cellCoords = swap;
3701     }
3702   }
3703   PetscCall(PetscArrayzero(realCoords, numPoints * dimC));
3704   for (j = 0; j < numPoints; j++) {
3705     const PetscReal *guess  = &refCoords[dimR * j];
3706     PetscReal       *mapped = &realCoords[dimC * j];
3707 
3708     for (k = 0; k < numV; k++) {
3709       PetscReal extCoord = 1.;
3710       for (l = 0; l < dimR; l++) {
3711         PetscReal coord = guess[l];
3712         PetscInt  dep   = (k & (1 << l)) >> l;
3713 
3714         extCoord *= dep * coord + !dep;
3715       }
3716       for (l = 0; l < dimC; l++) {
3717         PetscReal coeff = cellCoeffs[dimC * k + l];
3718 
3719         mapped[l] += coeff * extCoord;
3720       }
3721     }
3722   }
3723   PetscCall(DMRestoreWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData));
3724   PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3725   PetscFunctionReturn(PETSC_SUCCESS);
3726 }
3727 
3728 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)
3729 {
3730   PetscInt     numComp, pdim, i, j, k, l, m, coordSize;
3731   PetscScalar *nodes = NULL;
3732   PetscReal   *invV, *modes;
3733   PetscReal   *B, *D, *resNeg;
3734   PetscScalar *J, *invJ, *work;
3735   PetscReal    tolerance = tol == NULL ? 0.0 : *tol;
3736 
3737   PetscFunctionBegin;
3738   PetscCall(PetscFEGetDimension(fe, &pdim));
3739   PetscCall(PetscFEGetNumComponents(fe, &numComp));
3740   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);
3741   /* we shouldn't apply inverse closure permutation, if one exists */
3742   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, coords, cell, 0, &coordSize, &nodes));
3743   /* convert nodes to values in the stable evaluation basis */
3744   PetscCall(DMGetWorkArray(dm, pdim, MPIU_REAL, &modes));
3745   invV = fe->invV;
3746   for (i = 0; i < pdim; ++i) {
3747     modes[i] = 0.;
3748     for (j = 0; j < pdim; ++j) modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]);
3749   }
3750   PetscCall(DMGetWorkArray(dm, pdim * Nc + pdim * Nc * dimR + Nc, MPIU_REAL, &B));
3751   D      = &B[pdim * Nc];
3752   resNeg = &D[pdim * Nc * dimR];
3753   PetscCall(DMGetWorkArray(dm, 3 * Nc * dimR, MPIU_SCALAR, &J));
3754   invJ = &J[Nc * dimR];
3755   work = &invJ[Nc * dimR];
3756   for (i = 0; i < numPoints * dimR; i++) refCoords[i] = 0.;
3757   for (j = 0; j < numPoints; j++) {
3758     PetscReal normPoint = DMPlex_NormD_Internal(Nc, &realCoords[j * Nc]);
3759     normPoint           = normPoint > PETSC_SMALL ? normPoint : 1.0;
3760     for (i = 0; i < maxIter; i++) { /* we could batch this so that we're not making big B and D arrays all the time */
3761       PetscReal *guess = &refCoords[j * dimR], error = 0;
3762       PetscCall(PetscSpaceEvaluate(fe->basisSpace, 1, guess, B, D, NULL));
3763       for (k = 0; k < Nc; k++) resNeg[k] = realCoords[j * Nc + k];
3764       for (k = 0; k < Nc * dimR; k++) J[k] = 0.;
3765       for (k = 0; k < pdim; k++) {
3766         for (l = 0; l < Nc; l++) {
3767           resNeg[l] -= modes[k] * B[k * Nc + l];
3768           for (m = 0; m < dimR; m++) J[l * dimR + m] += modes[k] * D[(k * Nc + l) * dimR + m];
3769         }
3770       }
3771       if (0 && PetscDefined(USE_DEBUG)) {
3772         PetscReal maxAbs = 0.;
3773 
3774         for (l = 0; l < Nc; l++) maxAbs = PetscMax(maxAbs, PetscAbsReal(resNeg[l]));
3775         PetscCall(PetscInfo(dm, "cell %" PetscInt_FMT ", point %" PetscInt_FMT ", iter %" PetscInt_FMT ": res %g\n", cell, j, i, (double)maxAbs));
3776       }
3777       error = DMPlex_NormD_Internal(Nc, resNeg);
3778       if (error < tolerance * normPoint) {
3779         if (tol) *tol = error / normPoint;
3780         break;
3781       }
3782       PetscCall(DMPlexCoordinatesToReference_NewtonUpdate(Nc, dimR, J, invJ, work, resNeg, guess));
3783     }
3784   }
3785   PetscCall(DMRestoreWorkArray(dm, 3 * Nc * dimR, MPIU_SCALAR, &J));
3786   PetscCall(DMRestoreWorkArray(dm, pdim * Nc + pdim * Nc * dimR + Nc, MPIU_REAL, &B));
3787   PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_REAL, &modes));
3788   PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes));
3789   PetscFunctionReturn(PETSC_SUCCESS);
3790 }
3791 
3792 /* TODO: TOBY please fix this for Nc > 1 */
3793 PetscErrorCode DMPlexReferenceToCoordinates_FE(DM dm, PetscFE fe, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt Nc, PetscInt dimR)
3794 {
3795   PetscInt     numComp, pdim, i, j, k, l, coordSize;
3796   PetscScalar *nodes = NULL;
3797   PetscReal   *invV, *modes;
3798   PetscReal   *B;
3799 
3800   PetscFunctionBegin;
3801   PetscCall(PetscFEGetDimension(fe, &pdim));
3802   PetscCall(PetscFEGetNumComponents(fe, &numComp));
3803   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);
3804   /* we shouldn't apply inverse closure permutation, if one exists */
3805   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, coords, cell, 0, &coordSize, &nodes));
3806   /* convert nodes to values in the stable evaluation basis */
3807   PetscCall(DMGetWorkArray(dm, pdim, MPIU_REAL, &modes));
3808   invV = fe->invV;
3809   for (i = 0; i < pdim; ++i) {
3810     modes[i] = 0.;
3811     for (j = 0; j < pdim; ++j) modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]);
3812   }
3813   PetscCall(DMGetWorkArray(dm, numPoints * pdim * Nc, MPIU_REAL, &B));
3814   PetscCall(PetscSpaceEvaluate(fe->basisSpace, numPoints, refCoords, B, NULL, NULL));
3815   for (i = 0; i < numPoints * Nc; i++) realCoords[i] = 0.;
3816   for (j = 0; j < numPoints; j++) {
3817     PetscReal *mapped = &realCoords[j * Nc];
3818 
3819     for (k = 0; k < pdim; k++) {
3820       for (l = 0; l < Nc; l++) mapped[l] += modes[k] * B[(j * pdim + k) * Nc + l];
3821     }
3822   }
3823   PetscCall(DMRestoreWorkArray(dm, numPoints * pdim * Nc, MPIU_REAL, &B));
3824   PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_REAL, &modes));
3825   PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes));
3826   PetscFunctionReturn(PETSC_SUCCESS);
3827 }
3828 
3829 /*@
3830   DMPlexCoordinatesToReference - Pull coordinates back from the mesh to the reference element
3831   using a single element map.
3832 
3833   Not Collective
3834 
3835   Input Parameters:
3836 + dm         - The mesh, with coordinate maps defined either by a `PetscDS` for the coordinate `DM` (see `DMGetCoordinateDM()`) or
3837                implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or
3838                as a multilinear map for tensor-product elements
3839 . cell       - the cell whose map is used.
3840 . numPoints  - the number of points to locate
3841 - realCoords - (numPoints x coordinate dimension) array of coordinates (see `DMGetCoordinateDim()`)
3842 
3843   Output Parameter:
3844 . refCoords - (`numPoints` x `dimension`) array of reference coordinates (see `DMGetDimension()`)
3845 
3846   Level: intermediate
3847 
3848   Notes:
3849   This inversion will be accurate inside the reference element, but may be inaccurate for
3850   mappings that do not extend uniquely outside the reference cell (e.g, most non-affine maps)
3851 
3852 .seealso: `DMPLEX`, `DMPlexReferenceToCoordinates()`
3853 @*/
3854 PetscErrorCode DMPlexCoordinatesToReference(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[])
3855 {
3856   PetscInt dimC, dimR, depth, cStart, cEnd, i;
3857   DM       coordDM = NULL;
3858   Vec      coords;
3859   PetscFE  fe = NULL;
3860 
3861   PetscFunctionBegin;
3862   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3863   PetscCall(DMGetDimension(dm, &dimR));
3864   PetscCall(DMGetCoordinateDim(dm, &dimC));
3865   if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(PETSC_SUCCESS);
3866   PetscCall(DMPlexGetDepth(dm, &depth));
3867   PetscCall(DMGetCoordinatesLocal(dm, &coords));
3868   PetscCall(DMGetCoordinateDM(dm, &coordDM));
3869   if (coordDM) {
3870     PetscInt coordFields;
3871 
3872     PetscCall(DMGetNumFields(coordDM, &coordFields));
3873     if (coordFields) {
3874       PetscClassId id;
3875       PetscObject  disc;
3876 
3877       PetscCall(DMGetField(coordDM, 0, NULL, &disc));
3878       PetscCall(PetscObjectGetClassId(disc, &id));
3879       if (id == PETSCFE_CLASSID) fe = (PetscFE)disc;
3880     }
3881   }
3882   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
3883   PetscCheck(cell >= cStart && cell < cEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "point %" PetscInt_FMT " not in cell range [%" PetscInt_FMT ",%" PetscInt_FMT ")", cell, cStart, cEnd);
3884   if (!fe) { /* implicit discretization: affine or multilinear */
3885     PetscInt  coneSize;
3886     PetscBool isSimplex, isTensor;
3887 
3888     PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
3889     isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE;
3890     isTensor  = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE;
3891     if (isSimplex) {
3892       PetscReal detJ, *v0, *J, *invJ;
3893 
3894       PetscCall(DMGetWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3895       J    = &v0[dimC];
3896       invJ = &J[dimC * dimC];
3897       PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, invJ, &detJ));
3898       for (i = 0; i < numPoints; i++) { /* Apply the inverse affine transformation for each point */
3899         const PetscReal x0[3] = {-1., -1., -1.};
3900 
3901         CoordinatesRealToRef(dimC, dimR, x0, v0, invJ, &realCoords[dimC * i], &refCoords[dimR * i]);
3902       }
3903       PetscCall(DMRestoreWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3904     } else if (isTensor) {
3905       PetscCall(DMPlexCoordinatesToReference_Tensor(coordDM, cell, numPoints, realCoords, refCoords, coords, dimC, dimR));
3906     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unrecognized cone size %" PetscInt_FMT, coneSize);
3907   } else {
3908     PetscCall(DMPlexCoordinatesToReference_FE(coordDM, fe, cell, numPoints, realCoords, refCoords, coords, dimC, dimR, 7, NULL));
3909   }
3910   PetscFunctionReturn(PETSC_SUCCESS);
3911 }
3912 
3913 /*@
3914   DMPlexReferenceToCoordinates - Map references coordinates to coordinates in the mesh for a single element map.
3915 
3916   Not Collective
3917 
3918   Input Parameters:
3919 + dm        - The mesh, with coordinate maps defined either by a PetscDS for the coordinate `DM` (see `DMGetCoordinateDM()`) or
3920                implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or
3921                as a multilinear map for tensor-product elements
3922 . cell      - the cell whose map is used.
3923 . numPoints - the number of points to locate
3924 - refCoords - (numPoints x dimension) array of reference coordinates (see `DMGetDimension()`)
3925 
3926   Output Parameter:
3927 . realCoords - (numPoints x coordinate dimension) array of coordinates (see `DMGetCoordinateDim()`)
3928 
3929   Level: intermediate
3930 
3931 .seealso: `DMPLEX`, `DMPlexCoordinatesToReference()`
3932 @*/
3933 PetscErrorCode DMPlexReferenceToCoordinates(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[])
3934 {
3935   PetscInt dimC, dimR, depth, cStart, cEnd, i;
3936   DM       coordDM = NULL;
3937   Vec      coords;
3938   PetscFE  fe = NULL;
3939 
3940   PetscFunctionBegin;
3941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3942   PetscCall(DMGetDimension(dm, &dimR));
3943   PetscCall(DMGetCoordinateDim(dm, &dimC));
3944   if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(PETSC_SUCCESS);
3945   PetscCall(DMPlexGetDepth(dm, &depth));
3946   PetscCall(DMGetCoordinatesLocal(dm, &coords));
3947   PetscCall(DMGetCoordinateDM(dm, &coordDM));
3948   if (coordDM) {
3949     PetscInt coordFields;
3950 
3951     PetscCall(DMGetNumFields(coordDM, &coordFields));
3952     if (coordFields) {
3953       PetscClassId id;
3954       PetscObject  disc;
3955 
3956       PetscCall(DMGetField(coordDM, 0, NULL, &disc));
3957       PetscCall(PetscObjectGetClassId(disc, &id));
3958       if (id == PETSCFE_CLASSID) fe = (PetscFE)disc;
3959     }
3960   }
3961   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
3962   PetscCheck(cell >= cStart && cell < cEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "point %" PetscInt_FMT " not in cell range [%" PetscInt_FMT ",%" PetscInt_FMT ")", cell, cStart, cEnd);
3963   if (!fe) { /* implicit discretization: affine or multilinear */
3964     PetscInt  coneSize;
3965     PetscBool isSimplex, isTensor;
3966 
3967     PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
3968     isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE;
3969     isTensor  = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE;
3970     if (isSimplex) {
3971       PetscReal detJ, *v0, *J;
3972 
3973       PetscCall(DMGetWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3974       J = &v0[dimC];
3975       PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, NULL, &detJ));
3976       for (i = 0; i < numPoints; i++) { /* Apply the affine transformation for each point */
3977         const PetscReal xi0[3] = {-1., -1., -1.};
3978 
3979         CoordinatesRefToReal(dimC, dimR, xi0, v0, J, &refCoords[dimR * i], &realCoords[dimC * i]);
3980       }
3981       PetscCall(DMRestoreWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3982     } else if (isTensor) {
3983       PetscCall(DMPlexReferenceToCoordinates_Tensor(coordDM, cell, numPoints, refCoords, realCoords, coords, dimC, dimR));
3984     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unrecognized cone size %" PetscInt_FMT, coneSize);
3985   } else {
3986     PetscCall(DMPlexReferenceToCoordinates_FE(coordDM, fe, cell, numPoints, refCoords, realCoords, coords, dimC, dimR));
3987   }
3988   PetscFunctionReturn(PETSC_SUCCESS);
3989 }
3990 
3991 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[])
3992 {
3993   const PetscInt Nc = uOff[1] - uOff[0];
3994   PetscInt       c;
3995 
3996   for (c = 0; c < Nc; ++c) f0[c] = u[c];
3997 }
3998 
3999 /* Shear applies the transformation, assuming we fix z,
4000   / 1  0  m_0 \
4001   | 0  1  m_1 |
4002   \ 0  0   1  /
4003 */
4004 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[])
4005 {
4006   const PetscInt Nc = uOff[1] - uOff[0];
4007   const PetscInt ax = (PetscInt)PetscRealPart(constants[0]);
4008   PetscInt       c;
4009 
4010   for (c = 0; c < Nc; ++c) coords[c] = u[c] + constants[c + 1] * u[ax];
4011 }
4012 
4013 /* Flare applies the transformation, assuming we fix x_f,
4014 
4015    x_i = x_i * alpha_i x_f
4016 */
4017 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[])
4018 {
4019   const PetscInt Nc = uOff[1] - uOff[0];
4020   const PetscInt cf = (PetscInt)PetscRealPart(constants[0]);
4021   PetscInt       c;
4022 
4023   for (c = 0; c < Nc; ++c) coords[c] = u[c] * (c == cf ? 1.0 : constants[c + 1] * u[cf]);
4024 }
4025 
4026 /*
4027   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
4028   will correspond to the top and bottom of our square. So
4029 
4030     (0,0)--(1,0)  ==>  (1,0)--(2,0)      Just a shift of (1,0)
4031     (0,1)--(1,1)  ==>  (0,1)--(0,2)      Switch x and y
4032 
4033   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:
4034 
4035     (x, y)  ==>  (x+1, \pi/2 y)                           in (r', \theta') space
4036             ==>  ((x+1) cos(\pi/2 y), (x+1) sin(\pi/2 y)) in (x', y') space
4037 */
4038 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[])
4039 {
4040   const PetscReal ri = PetscRealPart(constants[0]);
4041   const PetscReal ro = PetscRealPart(constants[1]);
4042 
4043   xp[0] = (x[0] * (ro - ri) + ri) * PetscCosReal(0.5 * PETSC_PI * x[1]);
4044   xp[1] = (x[0] * (ro - ri) + ri) * PetscSinReal(0.5 * PETSC_PI * x[1]);
4045 }
4046 
4047 /*
4048   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
4049   lower hemisphere and the upper surface onto the top, letting z be the radius.
4050 
4051     (x, y)  ==>  ((z+3)/2, \pi/2 (|x| or |y|), arctan y/x)                                                  in (r', \theta', \phi') space
4052             ==>  ((z+3)/2 \cos(\theta') cos(\phi'), (z+3)/2 \cos(\theta') sin(\phi'), (z+3)/2 sin(\theta')) in (x', y', z') space
4053 */
4054 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[])
4055 {
4056   const PetscReal pi4    = PETSC_PI / 4.0;
4057   const PetscReal ri     = PetscRealPart(constants[0]);
4058   const PetscReal ro     = PetscRealPart(constants[1]);
4059   const PetscReal rp     = (x[2] + 1) * 0.5 * (ro - ri) + ri;
4060   const PetscReal phip   = PetscAtan2Real(x[1], x[0]);
4061   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])));
4062 
4063   xp[0] = rp * PetscCosReal(thetap) * PetscCosReal(phip);
4064   xp[1] = rp * PetscCosReal(thetap) * PetscSinReal(phip);
4065   xp[2] = rp * PetscSinReal(thetap);
4066 }
4067 
4068 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[])
4069 {
4070   const PetscReal c = PetscRealPart(constants[0]);
4071   const PetscReal m = PetscRealPart(constants[1]);
4072   const PetscReal n = PetscRealPart(constants[2]);
4073 
4074   xp[0] = x[0];
4075   xp[1] = x[1];
4076   if (dim > 2) xp[2] = c * PetscCosReal(2. * m * PETSC_PI * x[0]) * PetscCosReal(2. * n * PETSC_PI * x[1]);
4077 }
4078 
4079 /*@C
4080   DMPlexRemapGeometry - This function maps the original `DM` coordinates to new coordinates.
4081 
4082   Not Collective
4083 
4084   Input Parameters:
4085 + dm   - The `DM`
4086 . time - The time
4087 - func - The function transforming current coordinates to new coordinates
4088 
4089   Calling sequence of `func`:
4090 + dim          - The spatial dimension
4091 . Nf           - The number of input fields (here 1)
4092 . NfAux        - The number of input auxiliary fields
4093 . uOff         - The offset of the coordinates in u[] (here 0)
4094 . uOff_x       - The offset of the coordinates in u_x[] (here 0)
4095 . u            - The coordinate values at this point in space
4096 . u_t          - The coordinate time derivative at this point in space (here `NULL`)
4097 . u_x          - The coordinate derivatives at this point in space
4098 . aOff         - The offset of each auxiliary field in u[]
4099 . aOff_x       - The offset of each auxiliary field in u_x[]
4100 . a            - The auxiliary field values at this point in space
4101 . a_t          - The auxiliary field time derivative at this point in space (or `NULL`)
4102 . a_x          - The auxiliary field derivatives at this point in space
4103 . t            - The current time
4104 . x            - The coordinates of this point (here not used)
4105 . numConstants - The number of constants
4106 . constants    - The value of each constant
4107 - f            - The new coordinates at this point in space
4108 
4109   Level: intermediate
4110 
4111 .seealso: `DMPLEX`, `DMGetCoordinates()`, `DMGetCoordinatesLocal()`, `DMGetCoordinateDM()`, `DMProjectFieldLocal()`, `DMProjectFieldLabelLocal()`
4112 @*/
4113 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[]))
4114 {
4115   DM           cdm;
4116   PetscDS      cds;
4117   DMField      cf;
4118   PetscObject  obj;
4119   PetscClassId id;
4120   Vec          lCoords, tmpCoords;
4121 
4122   PetscFunctionBegin;
4123   if (!func) PetscCall(DMPlexGetCoordinateMap(dm, &func));
4124   PetscCall(DMGetCoordinateDM(dm, &cdm));
4125   PetscCall(DMGetCoordinatesLocal(dm, &lCoords));
4126   PetscCall(DMGetDS(cdm, &cds));
4127   PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
4128   PetscCall(PetscObjectGetClassId(obj, &id));
4129   if (id != PETSCFE_CLASSID) {
4130     PetscSection       cSection;
4131     const PetscScalar *constants;
4132     PetscScalar       *coords, f[16];
4133     PetscInt           dim, cdim, Nc, vStart, vEnd;
4134 
4135     PetscCall(DMGetDimension(dm, &dim));
4136     PetscCall(DMGetCoordinateDim(dm, &cdim));
4137     PetscCheck(cdim <= 16, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Affine version of DMPlexRemapGeometry is currently limited to dimensions <= 16, not %" PetscInt_FMT, cdim);
4138     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
4139     PetscCall(DMGetCoordinateSection(dm, &cSection));
4140     PetscCall(PetscDSGetConstants(cds, &Nc, &constants));
4141     PetscCall(VecGetArrayWrite(lCoords, &coords));
4142     for (PetscInt v = vStart; v < vEnd; ++v) {
4143       PetscInt uOff[2] = {0, cdim};
4144       PetscInt off, c;
4145 
4146       PetscCall(PetscSectionGetOffset(cSection, v, &off));
4147       (*func)(dim, 1, 0, uOff, NULL, &coords[off], NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0.0, NULL, Nc, constants, f);
4148       for (c = 0; c < cdim; ++c) coords[off + c] = f[c];
4149     }
4150     PetscCall(VecRestoreArrayWrite(lCoords, &coords));
4151   } else {
4152     PetscCall(DMGetLocalVector(cdm, &tmpCoords));
4153     PetscCall(VecCopy(lCoords, tmpCoords));
4154     /* We have to do the coordinate field manually right now since the coordinate DM will not have its own */
4155     PetscCall(DMGetCoordinateField(dm, &cf));
4156     cdm->coordinates[0].field = cf;
4157     PetscCall(DMProjectFieldLocal(cdm, time, tmpCoords, &func, INSERT_VALUES, lCoords));
4158     cdm->coordinates[0].field = NULL;
4159     PetscCall(DMRestoreLocalVector(cdm, &tmpCoords));
4160     PetscCall(DMSetCoordinatesLocal(dm, lCoords));
4161   }
4162   PetscFunctionReturn(PETSC_SUCCESS);
4163 }
4164 
4165 /*@
4166   DMPlexShearGeometry - This shears the domain, meaning adds a multiple of the shear coordinate to all other coordinates.
4167 
4168   Not Collective
4169 
4170   Input Parameters:
4171 + dm          - The `DMPLEX`
4172 . direction   - The shear coordinate direction, e.g. `DM_X` is the x-axis
4173 - multipliers - The multiplier m for each direction which is not the shear direction
4174 
4175   Level: intermediate
4176 
4177 .seealso: `DMPLEX`, `DMPlexRemapGeometry()`, `DMDirection`, `DM_X`, `DM_Y`, `DM_Z`
4178 @*/
4179 PetscErrorCode DMPlexShearGeometry(DM dm, DMDirection direction, PetscReal multipliers[])
4180 {
4181   DM             cdm;
4182   PetscDS        cds;
4183   PetscScalar   *moduli;
4184   const PetscInt dir = (PetscInt)direction;
4185   PetscInt       dE, d, e;
4186 
4187   PetscFunctionBegin;
4188   PetscCall(DMGetCoordinateDM(dm, &cdm));
4189   PetscCall(DMGetCoordinateDim(dm, &dE));
4190   PetscCall(PetscMalloc1(dE + 1, &moduli));
4191   moduli[0] = dir;
4192   for (d = 0, e = 0; d < dE; ++d) moduli[d + 1] = d == dir ? 0.0 : (multipliers ? multipliers[e++] : 1.0);
4193   PetscCall(DMGetDS(cdm, &cds));
4194   PetscCall(PetscDSSetConstants(cds, dE + 1, moduli));
4195   PetscCall(DMPlexRemapGeometry(dm, 0.0, coordMap_shear));
4196   PetscCall(PetscFree(moduli));
4197   PetscFunctionReturn(PETSC_SUCCESS);
4198 }
4199