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