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