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