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