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