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