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