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