xref: /petsc/src/dm/impls/plex/plexgeometry.c (revision ccb4e88a40f0b86eaeca07ff64c64e4de2fae686)
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], best[3] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MAX_REAL}, dist, distMax = PETSC_MAX_REAL;
905       PetscInt           dbin[3] = {-1,-1,-1}, bin, cellOffset, d, bestc = -1;
906 
907       if (cells[p].index < 0) {
908         ierr = PetscGridHashGetEnclosingBox(mesh->lbox, 1, point, dbin, &bin);CHKERRQ(ierr);
909         ierr = PetscSectionGetDof(mesh->lbox->cellSection, bin, &numCells);CHKERRQ(ierr);
910         ierr = PetscSectionGetOffset(mesh->lbox->cellSection, bin, &cellOffset);CHKERRQ(ierr);
911         for (c = cellOffset; c < cellOffset + numCells; ++c) {
912           ierr = DMPlexClosestPoint_Internal(dm, dim, point, boxCells[c], cpoint);CHKERRQ(ierr);
913           for (d = 0; d < dim; ++d) diff[d] = cpoint[d] - PetscRealPart(point[d]);
914           dist = DMPlex_NormD_Internal(dim, diff);
915           if (dist < distMax) {
916             for (d = 0; d < dim; ++d) best[d] = cpoint[d];
917             bestc = boxCells[c];
918             distMax = dist;
919           }
920         }
921         if (distMax < PETSC_MAX_REAL) {
922           ++numFound;
923           cells[p].rank  = 0;
924           cells[p].index = bestc;
925           for (d = 0; d < dim; ++d) a[p*bs+d] = best[d];
926         }
927       }
928     }
929   }
930   /* This code is only be relevant when interfaced to parallel point location */
931   /* Check for highest numbered proc that claims a point (do we care?) */
932   if (ltype == DM_POINTLOCATION_REMOVE && numFound < numPoints) {
933     ierr = PetscMalloc1(numFound,&found);CHKERRQ(ierr);
934     for (p = 0, numFound = 0; p < numPoints; p++) {
935       if (cells[p].rank >= 0 && cells[p].index >= 0) {
936         if (numFound < p) {
937           cells[numFound] = cells[p];
938         }
939         found[numFound++] = p;
940       }
941     }
942   }
943   ierr = VecRestoreArray(v, &a);CHKERRQ(ierr);
944   if (!reuse) {
945     ierr = PetscSFSetGraph(cellSF, cEnd - cStart, numFound, found, PETSC_OWN_POINTER, cells, PETSC_OWN_POINTER);CHKERRQ(ierr);
946   }
947   ierr = PetscTime(&t1);CHKERRQ(ierr);
948   if (hash) {
949     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);
950   } else {
951     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);
952   }
953   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);
954   ierr = PetscLogEventEnd(DMPLEX_LocatePoints,0,0,0,0);CHKERRQ(ierr);
955   PetscFunctionReturn(0);
956 }
957 
958 /*@C
959   DMPlexComputeProjection2Dto1D - Rewrite coordinates to be the 1D projection of the 2D coordinates
960 
961   Not collective
962 
963   Input/Output Parameter:
964 . coords - The coordinates of a segment, on output the new y-coordinate, and 0 for x
965 
966   Output Parameter:
967 . R - The rotation which accomplishes the projection
968 
969   Level: developer
970 
971 .seealso: DMPlexComputeProjection3Dto1D(), DMPlexComputeProjection3Dto2D()
972 @*/
973 PetscErrorCode DMPlexComputeProjection2Dto1D(PetscScalar coords[], PetscReal R[])
974 {
975   const PetscReal x = PetscRealPart(coords[2] - coords[0]);
976   const PetscReal y = PetscRealPart(coords[3] - coords[1]);
977   const PetscReal r = PetscSqrtReal(x*x + y*y), c = x/r, s = y/r;
978 
979   PetscFunctionBegin;
980   R[0] = c; R[1] = -s;
981   R[2] = s; R[3] =  c;
982   coords[0] = 0.0;
983   coords[1] = r;
984   PetscFunctionReturn(0);
985 }
986 
987 /*@C
988   DMPlexComputeProjection3Dto1D - Rewrite coordinates to be the 1D projection of the 3D coordinates
989 
990   Not collective
991 
992   Input/Output Parameter:
993 . coords - The coordinates of a segment; on output, the new y-coordinate, and 0 for x and z
994 
995   Output Parameter:
996 . R - The rotation which accomplishes the projection
997 
998   Note: This uses the basis completion described by Frisvad in http://www.imm.dtu.dk/~jerf/papers/abstracts/onb.html, DOI:10.1080/2165347X.2012.689606
999 
1000   Level: developer
1001 
1002 .seealso: DMPlexComputeProjection2Dto1D(), DMPlexComputeProjection3Dto2D()
1003 @*/
1004 PetscErrorCode DMPlexComputeProjection3Dto1D(PetscScalar coords[], PetscReal R[])
1005 {
1006   PetscReal      x    = PetscRealPart(coords[3] - coords[0]);
1007   PetscReal      y    = PetscRealPart(coords[4] - coords[1]);
1008   PetscReal      z    = PetscRealPart(coords[5] - coords[2]);
1009   PetscReal      r    = PetscSqrtReal(x*x + y*y + z*z);
1010   PetscReal      rinv = 1. / r;
1011   PetscFunctionBegin;
1012 
1013   x *= rinv; y *= rinv; z *= rinv;
1014   if (x > 0.) {
1015     PetscReal inv1pX   = 1./ (1. + x);
1016 
1017     R[0] = x; R[1] = -y;              R[2] = -z;
1018     R[3] = y; R[4] = 1. - y*y*inv1pX; R[5] =     -y*z*inv1pX;
1019     R[6] = z; R[7] =     -y*z*inv1pX; R[8] = 1. - z*z*inv1pX;
1020   }
1021   else {
1022     PetscReal inv1mX   = 1./ (1. - x);
1023 
1024     R[0] = x; R[1] = z;               R[2] = y;
1025     R[3] = y; R[4] =     -y*z*inv1mX; R[5] = 1. - y*y*inv1mX;
1026     R[6] = z; R[7] = 1. - z*z*inv1mX; R[8] =     -y*z*inv1mX;
1027   }
1028   coords[0] = 0.0;
1029   coords[1] = r;
1030   PetscFunctionReturn(0);
1031 }
1032 
1033 /*@
1034   DMPlexComputeProjection3Dto2D - Rewrite coordinates of 3 or more coplanar 3D points to a common 2D basis for the
1035     plane.  The normal is defined by positive orientation of the first 3 points.
1036 
1037   Not collective
1038 
1039   Input Parameter:
1040 . coordSize - Length of coordinate array (3x number of points); must be at least 9 (3 points)
1041 
1042   Input/Output Parameter:
1043 . coords - The interlaced coordinates of each coplanar 3D point; on output the first
1044            2*coordSize/3 entries contain interlaced 2D points, with the rest undefined
1045 
1046   Output Parameter:
1047 . R - 3x3 row-major rotation matrix whose columns are the tangent basis [t1, t2, n].  Multiplying by R^T transforms from original frame to tangent frame.
1048 
1049   Level: developer
1050 
1051 .seealso: DMPlexComputeProjection2Dto1D(), DMPlexComputeProjection3Dto1D()
1052 @*/
1053 PetscErrorCode DMPlexComputeProjection3Dto2D(PetscInt coordSize, PetscScalar coords[], PetscReal R[])
1054 {
1055   PetscReal x1[3], x2[3], n[3], c[3], norm;
1056   const PetscInt dim = 3;
1057   PetscInt       d, p;
1058 
1059   PetscFunctionBegin;
1060   /* 0) Calculate normal vector */
1061   for (d = 0; d < dim; ++d) {
1062     x1[d] = PetscRealPart(coords[1*dim+d] - coords[0*dim+d]);
1063     x2[d] = PetscRealPart(coords[2*dim+d] - coords[0*dim+d]);
1064   }
1065   // n = x1 \otimes x2
1066   n[0] = x1[1]*x2[2] - x1[2]*x2[1];
1067   n[1] = x1[2]*x2[0] - x1[0]*x2[2];
1068   n[2] = x1[0]*x2[1] - x1[1]*x2[0];
1069   norm = PetscSqrtReal(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
1070   for (d = 0; d < dim; d++) n[d] /= norm;
1071   norm = PetscSqrtReal(x1[0] * x1[0] + x1[1] * x1[1] + x1[2] * x1[2]);
1072   for (d = 0; d < dim; d++) x1[d] /= norm;
1073   // x2 = n \otimes x1
1074   x2[0] = n[1] * x1[2] - n[2] * x1[1];
1075   x2[1] = n[2] * x1[0] - n[0] * x1[2];
1076   x2[2] = n[0] * x1[1] - n[1] * x1[0];
1077   for (d=0; d<dim; d++) {
1078     R[d * dim + 0] = x1[d];
1079     R[d * dim + 1] = x2[d];
1080     R[d * dim + 2] = n[d];
1081     c[d] = PetscRealPart(coords[0*dim + d]);
1082   }
1083   for (p=0; p<coordSize/dim; p++) {
1084     PetscReal y[3];
1085     for (d=0; d<dim; d++) y[d] = PetscRealPart(coords[p*dim + d]) - c[d];
1086     for (d=0; d<2; d++) coords[p*2+d] = R[0*dim + d] * y[0] + R[1*dim + d] * y[1] + R[2*dim + d] * y[2];
1087   }
1088   PetscFunctionReturn(0);
1089 }
1090 
1091 PETSC_UNUSED
1092 PETSC_STATIC_INLINE void Volume_Triangle_Internal(PetscReal *vol, PetscReal coords[])
1093 {
1094   /* Signed volume is 1/2 the determinant
1095 
1096    |  1  1  1 |
1097    | x0 x1 x2 |
1098    | y0 y1 y2 |
1099 
1100      but if x0,y0 is the origin, we have
1101 
1102    | x1 x2 |
1103    | y1 y2 |
1104   */
1105   const PetscReal x1 = coords[2] - coords[0], y1 = coords[3] - coords[1];
1106   const PetscReal x2 = coords[4] - coords[0], y2 = coords[5] - coords[1];
1107   PetscReal       M[4], detM;
1108   M[0] = x1; M[1] = x2;
1109   M[2] = y1; M[3] = y2;
1110   DMPlex_Det2D_Internal(&detM, M);
1111   *vol = 0.5*detM;
1112   (void)PetscLogFlops(5.0);
1113 }
1114 
1115 PETSC_STATIC_INLINE void Volume_Triangle_Origin_Internal(PetscReal *vol, PetscReal coords[])
1116 {
1117   DMPlex_Det2D_Internal(vol, coords);
1118   *vol *= 0.5;
1119 }
1120 
1121 PETSC_UNUSED
1122 PETSC_STATIC_INLINE void Volume_Tetrahedron_Internal(PetscReal *vol, PetscReal coords[])
1123 {
1124   /* Signed volume is 1/6th of the determinant
1125 
1126    |  1  1  1  1 |
1127    | x0 x1 x2 x3 |
1128    | y0 y1 y2 y3 |
1129    | z0 z1 z2 z3 |
1130 
1131      but if x0,y0,z0 is the origin, we have
1132 
1133    | x1 x2 x3 |
1134    | y1 y2 y3 |
1135    | z1 z2 z3 |
1136   */
1137   const PetscReal x1 = coords[3] - coords[0], y1 = coords[4]  - coords[1], z1 = coords[5]  - coords[2];
1138   const PetscReal x2 = coords[6] - coords[0], y2 = coords[7]  - coords[1], z2 = coords[8]  - coords[2];
1139   const PetscReal x3 = coords[9] - coords[0], y3 = coords[10] - coords[1], z3 = coords[11] - coords[2];
1140   const PetscReal onesixth = ((PetscReal)1./(PetscReal)6.);
1141   PetscReal       M[9], detM;
1142   M[0] = x1; M[1] = x2; M[2] = x3;
1143   M[3] = y1; M[4] = y2; M[5] = y3;
1144   M[6] = z1; M[7] = z2; M[8] = z3;
1145   DMPlex_Det3D_Internal(&detM, M);
1146   *vol = -onesixth*detM;
1147   (void)PetscLogFlops(10.0);
1148 }
1149 
1150 PETSC_STATIC_INLINE void Volume_Tetrahedron_Origin_Internal(PetscReal *vol, PetscReal coords[])
1151 {
1152   const PetscReal onesixth = ((PetscReal)1./(PetscReal)6.);
1153   DMPlex_Det3D_Internal(vol, coords);
1154   *vol *= -onesixth;
1155 }
1156 
1157 static PetscErrorCode DMPlexComputePointGeometry_Internal(DM dm, PetscInt e, PetscReal v0[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1158 {
1159   PetscSection   coordSection;
1160   Vec            coordinates;
1161   const PetscScalar *coords;
1162   PetscInt       dim, d, off;
1163   PetscErrorCode ierr;
1164 
1165   PetscFunctionBegin;
1166   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
1167   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
1168   ierr = PetscSectionGetDof(coordSection,e,&dim);CHKERRQ(ierr);
1169   if (!dim) PetscFunctionReturn(0);
1170   ierr = PetscSectionGetOffset(coordSection,e,&off);CHKERRQ(ierr);
1171   ierr = VecGetArrayRead(coordinates,&coords);CHKERRQ(ierr);
1172   if (v0) {for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[off + d]);}
1173   ierr = VecRestoreArrayRead(coordinates,&coords);CHKERRQ(ierr);
1174   *detJ = 1.;
1175   if (J) {
1176     for (d = 0; d < dim * dim; d++) J[d] = 0.;
1177     for (d = 0; d < dim; d++) J[d * dim + d] = 1.;
1178     if (invJ) {
1179       for (d = 0; d < dim * dim; d++) invJ[d] = 0.;
1180       for (d = 0; d < dim; d++) invJ[d * dim + d] = 1.;
1181     }
1182   }
1183   PetscFunctionReturn(0);
1184 }
1185 
1186 static PetscErrorCode DMPlexComputeLineGeometry_Internal(DM dm, PetscInt e, PetscReal v0[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1187 {
1188   PetscSection   coordSection;
1189   Vec            coordinates;
1190   PetscScalar   *coords = NULL;
1191   PetscInt       numCoords, d, pStart, pEnd, numSelfCoords = 0;
1192   PetscErrorCode ierr;
1193 
1194   PetscFunctionBegin;
1195   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
1196   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
1197   ierr = PetscSectionGetChart(coordSection,&pStart,&pEnd);CHKERRQ(ierr);
1198   if (e >= pStart && e < pEnd) {ierr = PetscSectionGetDof(coordSection,e,&numSelfCoords);CHKERRQ(ierr);}
1199   ierr = DMPlexVecGetClosure(dm, coordSection, coordinates, e, &numCoords, &coords);CHKERRQ(ierr);
1200   numCoords = numSelfCoords ? numSelfCoords : numCoords;
1201   if (invJ && !J) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "In order to compute invJ, J must not be NULL");
1202   *detJ = 0.0;
1203   if (numCoords == 6) {
1204     const PetscInt dim = 3;
1205     PetscReal      R[9], J0;
1206 
1207     if (v0)   {for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);}
1208     ierr = DMPlexComputeProjection3Dto1D(coords, R);CHKERRQ(ierr);
1209     if (J)    {
1210       J0   = 0.5*PetscRealPart(coords[1]);
1211       J[0] = R[0]*J0; J[1] = R[1]; J[2] = R[2];
1212       J[3] = R[3]*J0; J[4] = R[4]; J[5] = R[5];
1213       J[6] = R[6]*J0; J[7] = R[7]; J[8] = R[8];
1214       DMPlex_Det3D_Internal(detJ, J);
1215       if (invJ) {DMPlex_Invert2D_Internal(invJ, J, *detJ);}
1216     }
1217   } else if (numCoords == 4) {
1218     const PetscInt dim = 2;
1219     PetscReal      R[4], J0;
1220 
1221     if (v0)   {for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);}
1222     ierr = DMPlexComputeProjection2Dto1D(coords, R);CHKERRQ(ierr);
1223     if (J)    {
1224       J0   = 0.5*PetscRealPart(coords[1]);
1225       J[0] = R[0]*J0; J[1] = R[1];
1226       J[2] = R[2]*J0; J[3] = R[3];
1227       DMPlex_Det2D_Internal(detJ, J);
1228       if (invJ) {DMPlex_Invert2D_Internal(invJ, J, *detJ);}
1229     }
1230   } else if (numCoords == 2) {
1231     const PetscInt dim = 1;
1232 
1233     if (v0)   {for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);}
1234     if (J)    {
1235       J[0]  = 0.5*(PetscRealPart(coords[1]) - PetscRealPart(coords[0]));
1236       *detJ = J[0];
1237       ierr = PetscLogFlops(2.0);CHKERRQ(ierr);
1238       if (invJ) {invJ[0] = 1.0/J[0]; ierr = PetscLogFlops(1.0);CHKERRQ(ierr);}
1239     }
1240   } else SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of coordinates for this segment is %D != 2", numCoords);
1241   ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, e, &numCoords, &coords);CHKERRQ(ierr);
1242   PetscFunctionReturn(0);
1243 }
1244 
1245 static PetscErrorCode DMPlexComputeTriangleGeometry_Internal(DM dm, PetscInt e, PetscReal v0[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1246 {
1247   PetscSection   coordSection;
1248   Vec            coordinates;
1249   PetscScalar   *coords = NULL;
1250   PetscInt       numCoords, numSelfCoords = 0, d, f, g, pStart, pEnd;
1251   PetscErrorCode ierr;
1252 
1253   PetscFunctionBegin;
1254   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
1255   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
1256   ierr = PetscSectionGetChart(coordSection,&pStart,&pEnd);CHKERRQ(ierr);
1257   if (e >= pStart && e < pEnd) {ierr = PetscSectionGetDof(coordSection,e,&numSelfCoords);CHKERRQ(ierr);}
1258   ierr = DMPlexVecGetClosure(dm, coordSection, coordinates, e, &numCoords, &coords);CHKERRQ(ierr);
1259   numCoords = numSelfCoords ? numSelfCoords : numCoords;
1260   *detJ = 0.0;
1261   if (numCoords == 9) {
1262     const PetscInt dim = 3;
1263     PetscReal      R[9], J0[9] = {1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0};
1264 
1265     if (v0)   {for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);}
1266     ierr = DMPlexComputeProjection3Dto2D(numCoords, coords, R);CHKERRQ(ierr);
1267     if (J)    {
1268       const PetscInt pdim = 2;
1269 
1270       for (d = 0; d < pdim; d++) {
1271         for (f = 0; f < pdim; f++) {
1272           J0[d*dim+f] = 0.5*(PetscRealPart(coords[(f+1)*pdim+d]) - PetscRealPart(coords[0*pdim+d]));
1273         }
1274       }
1275       ierr = PetscLogFlops(8.0);CHKERRQ(ierr);
1276       DMPlex_Det3D_Internal(detJ, J0);
1277       for (d = 0; d < dim; d++) {
1278         for (f = 0; f < dim; f++) {
1279           J[d*dim+f] = 0.0;
1280           for (g = 0; g < dim; g++) {
1281             J[d*dim+f] += R[d*dim+g]*J0[g*dim+f];
1282           }
1283         }
1284       }
1285       ierr = PetscLogFlops(18.0);CHKERRQ(ierr);
1286     }
1287     if (invJ) {DMPlex_Invert3D_Internal(invJ, J, *detJ);}
1288   } else if (numCoords == 6) {
1289     const PetscInt dim = 2;
1290 
1291     if (v0)   {for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);}
1292     if (J)    {
1293       for (d = 0; d < dim; d++) {
1294         for (f = 0; f < dim; f++) {
1295           J[d*dim+f] = 0.5*(PetscRealPart(coords[(f+1)*dim+d]) - PetscRealPart(coords[0*dim+d]));
1296         }
1297       }
1298       ierr = PetscLogFlops(8.0);CHKERRQ(ierr);
1299       DMPlex_Det2D_Internal(detJ, J);
1300     }
1301     if (invJ) {DMPlex_Invert2D_Internal(invJ, J, *detJ);}
1302   } else SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of coordinates for this triangle is %D != 6 or 9", numCoords);
1303   ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, e, &numCoords, &coords);CHKERRQ(ierr);
1304   PetscFunctionReturn(0);
1305 }
1306 
1307 static PetscErrorCode DMPlexComputeRectangleGeometry_Internal(DM dm, PetscInt e, PetscBool isTensor, PetscInt Nq, const PetscReal points[], PetscReal v[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1308 {
1309   PetscSection   coordSection;
1310   Vec            coordinates;
1311   PetscScalar   *coords = NULL;
1312   PetscInt       numCoords, numSelfCoords = 0, d, f, g, pStart, pEnd;
1313   PetscErrorCode ierr;
1314 
1315   PetscFunctionBegin;
1316   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
1317   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
1318   ierr = PetscSectionGetChart(coordSection,&pStart,&pEnd);CHKERRQ(ierr);
1319   if (e >= pStart && e < pEnd) {ierr = PetscSectionGetDof(coordSection,e,&numSelfCoords);CHKERRQ(ierr);}
1320   ierr = DMPlexVecGetClosure(dm, coordSection, coordinates, e, &numCoords, &coords);CHKERRQ(ierr);
1321   numCoords = numSelfCoords ? numSelfCoords : numCoords;
1322   if (!Nq) {
1323     PetscInt vorder[4] = {0, 1, 2, 3};
1324 
1325     if (isTensor) {vorder[2] = 3; vorder[3] = 2;}
1326     *detJ = 0.0;
1327     if (numCoords == 12) {
1328       const PetscInt dim = 3;
1329       PetscReal      R[9], J0[9] = {1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0};
1330 
1331       if (v)   {for (d = 0; d < dim; d++) v[d] = PetscRealPart(coords[d]);}
1332       ierr = DMPlexComputeProjection3Dto2D(numCoords, coords, R);CHKERRQ(ierr);
1333       if (J)    {
1334         const PetscInt pdim = 2;
1335 
1336         for (d = 0; d < pdim; d++) {
1337           J0[d*dim+0] = 0.5*(PetscRealPart(coords[vorder[1]*pdim+d]) - PetscRealPart(coords[vorder[0]*pdim+d]));
1338           J0[d*dim+1] = 0.5*(PetscRealPart(coords[vorder[2]*pdim+d]) - PetscRealPart(coords[vorder[1]*pdim+d]));
1339         }
1340         ierr = PetscLogFlops(8.0);CHKERRQ(ierr);
1341         DMPlex_Det3D_Internal(detJ, J0);
1342         for (d = 0; d < dim; d++) {
1343           for (f = 0; f < dim; f++) {
1344             J[d*dim+f] = 0.0;
1345             for (g = 0; g < dim; g++) {
1346               J[d*dim+f] += R[d*dim+g]*J0[g*dim+f];
1347             }
1348           }
1349         }
1350         ierr = PetscLogFlops(18.0);CHKERRQ(ierr);
1351       }
1352       if (invJ) {DMPlex_Invert3D_Internal(invJ, J, *detJ);}
1353     } else if (numCoords == 8) {
1354       const PetscInt dim = 2;
1355 
1356       if (v)   {for (d = 0; d < dim; d++) v[d] = PetscRealPart(coords[d]);}
1357       if (J)    {
1358         for (d = 0; d < dim; d++) {
1359           J[d*dim+0] = 0.5*(PetscRealPart(coords[vorder[1]*dim+d]) - PetscRealPart(coords[vorder[0]*dim+d]));
1360           J[d*dim+1] = 0.5*(PetscRealPart(coords[vorder[3]*dim+d]) - PetscRealPart(coords[vorder[0]*dim+d]));
1361         }
1362         ierr = PetscLogFlops(8.0);CHKERRQ(ierr);
1363         DMPlex_Det2D_Internal(detJ, J);
1364       }
1365       if (invJ) {DMPlex_Invert2D_Internal(invJ, J, *detJ);}
1366     } else SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of coordinates for this quadrilateral is %D != 8 or 12", numCoords);
1367   } else {
1368     const PetscInt Nv = 4;
1369     const PetscInt dimR = 2;
1370     PetscInt  zToPlex[4] = {0, 1, 3, 2};
1371     PetscReal zOrder[12];
1372     PetscReal zCoeff[12];
1373     PetscInt  i, j, k, l, dim;
1374 
1375     if (isTensor) {zToPlex[2] = 2; zToPlex[3] = 3;}
1376     if (numCoords == 12) {
1377       dim = 3;
1378     } else if (numCoords == 8) {
1379       dim = 2;
1380     } else SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of coordinates for this quadrilateral is %D != 8 or 12", numCoords);
1381     for (i = 0; i < Nv; i++) {
1382       PetscInt zi = zToPlex[i];
1383 
1384       for (j = 0; j < dim; j++) {
1385         zOrder[dim * i + j] = PetscRealPart(coords[dim * zi + j]);
1386       }
1387     }
1388     for (j = 0; j < dim; j++) {
1389       zCoeff[dim * 0 + j] = 0.25 * (  zOrder[dim * 0 + j] + zOrder[dim * 1 + j] + zOrder[dim * 2 + j] + zOrder[dim * 3 + j]);
1390       zCoeff[dim * 1 + j] = 0.25 * (- zOrder[dim * 0 + j] + zOrder[dim * 1 + j] - zOrder[dim * 2 + j] + zOrder[dim * 3 + j]);
1391       zCoeff[dim * 2 + j] = 0.25 * (- zOrder[dim * 0 + j] - zOrder[dim * 1 + j] + zOrder[dim * 2 + j] + zOrder[dim * 3 + j]);
1392       zCoeff[dim * 3 + j] = 0.25 * (  zOrder[dim * 0 + j] - zOrder[dim * 1 + j] - zOrder[dim * 2 + j] + zOrder[dim * 3 + j]);
1393     }
1394     for (i = 0; i < Nq; i++) {
1395       PetscReal xi = points[dimR * i], eta = points[dimR * i + 1];
1396 
1397       if (v) {
1398         PetscReal extPoint[4];
1399 
1400         extPoint[0] = 1.;
1401         extPoint[1] = xi;
1402         extPoint[2] = eta;
1403         extPoint[3] = xi * eta;
1404         for (j = 0; j < dim; j++) {
1405           PetscReal val = 0.;
1406 
1407           for (k = 0; k < Nv; k++) {
1408             val += extPoint[k] * zCoeff[dim * k + j];
1409           }
1410           v[i * dim + j] = val;
1411         }
1412       }
1413       if (J) {
1414         PetscReal extJ[8];
1415 
1416         extJ[0] = 0.;
1417         extJ[1] = 0.;
1418         extJ[2] = 1.;
1419         extJ[3] = 0.;
1420         extJ[4] = 0.;
1421         extJ[5] = 1.;
1422         extJ[6] = eta;
1423         extJ[7] = xi;
1424         for (j = 0; j < dim; j++) {
1425           for (k = 0; k < dimR; k++) {
1426             PetscReal val = 0.;
1427 
1428             for (l = 0; l < Nv; l++) {
1429               val += zCoeff[dim * l + j] * extJ[dimR * l + k];
1430             }
1431             J[i * dim * dim + dim * j + k] = val;
1432           }
1433         }
1434         if (dim == 3) { /* put the cross product in the third component of the Jacobian */
1435           PetscReal x, y, z;
1436           PetscReal *iJ = &J[i * dim * dim];
1437           PetscReal norm;
1438 
1439           x = iJ[1 * dim + 0] * iJ[2 * dim + 1] - iJ[1 * dim + 1] * iJ[2 * dim + 0];
1440           y = iJ[0 * dim + 1] * iJ[2 * dim + 0] - iJ[0 * dim + 0] * iJ[2 * dim + 1];
1441           z = iJ[0 * dim + 0] * iJ[1 * dim + 1] - iJ[0 * dim + 1] * iJ[1 * dim + 0];
1442           norm = PetscSqrtReal(x * x + y * y + z * z);
1443           iJ[2] = x / norm;
1444           iJ[5] = y / norm;
1445           iJ[8] = z / norm;
1446           DMPlex_Det3D_Internal(&detJ[i], &J[i * dim * dim]);
1447           if (invJ) {DMPlex_Invert3D_Internal(&invJ[i * dim * dim], &J[i * dim * dim], detJ[i]);}
1448         } else {
1449           DMPlex_Det2D_Internal(&detJ[i], &J[i * dim * dim]);
1450           if (invJ) {DMPlex_Invert2D_Internal(&invJ[i * dim * dim], &J[i * dim * dim], detJ[i]);}
1451         }
1452       }
1453     }
1454   }
1455   ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, e, &numCoords, &coords);CHKERRQ(ierr);
1456   PetscFunctionReturn(0);
1457 }
1458 
1459 static PetscErrorCode DMPlexComputeTetrahedronGeometry_Internal(DM dm, PetscInt e, PetscReal v0[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1460 {
1461   PetscSection   coordSection;
1462   Vec            coordinates;
1463   PetscScalar   *coords = NULL;
1464   const PetscInt dim = 3;
1465   PetscInt       d;
1466   PetscErrorCode ierr;
1467 
1468   PetscFunctionBegin;
1469   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
1470   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
1471   ierr = DMPlexVecGetClosure(dm, coordSection, coordinates, e, NULL, &coords);CHKERRQ(ierr);
1472   *detJ = 0.0;
1473   if (v0)   {for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);}
1474   if (J)    {
1475     for (d = 0; d < dim; d++) {
1476       /* I orient with outward face normals */
1477       J[d*dim+0] = 0.5*(PetscRealPart(coords[2*dim+d]) - PetscRealPart(coords[0*dim+d]));
1478       J[d*dim+1] = 0.5*(PetscRealPart(coords[1*dim+d]) - PetscRealPart(coords[0*dim+d]));
1479       J[d*dim+2] = 0.5*(PetscRealPart(coords[3*dim+d]) - PetscRealPart(coords[0*dim+d]));
1480     }
1481     ierr = PetscLogFlops(18.0);CHKERRQ(ierr);
1482     DMPlex_Det3D_Internal(detJ, J);
1483   }
1484   if (invJ) {DMPlex_Invert3D_Internal(invJ, J, *detJ);}
1485   ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, e, NULL, &coords);CHKERRQ(ierr);
1486   PetscFunctionReturn(0);
1487 }
1488 
1489 static PetscErrorCode DMPlexComputeHexahedronGeometry_Internal(DM dm, PetscInt e, PetscInt Nq, const PetscReal points[], PetscReal v[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1490 {
1491   PetscSection   coordSection;
1492   Vec            coordinates;
1493   PetscScalar   *coords = NULL;
1494   const PetscInt dim = 3;
1495   PetscInt       d;
1496   PetscErrorCode ierr;
1497 
1498   PetscFunctionBegin;
1499   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
1500   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
1501   ierr = DMPlexVecGetClosure(dm, coordSection, coordinates, e, NULL, &coords);CHKERRQ(ierr);
1502   if (!Nq) {
1503     *detJ = 0.0;
1504     if (v)   {for (d = 0; d < dim; d++) v[d] = PetscRealPart(coords[d]);}
1505     if (J)    {
1506       for (d = 0; d < dim; d++) {
1507         J[d*dim+0] = 0.5*(PetscRealPart(coords[3*dim+d]) - PetscRealPart(coords[0*dim+d]));
1508         J[d*dim+1] = 0.5*(PetscRealPart(coords[1*dim+d]) - PetscRealPart(coords[0*dim+d]));
1509         J[d*dim+2] = 0.5*(PetscRealPart(coords[4*dim+d]) - PetscRealPart(coords[0*dim+d]));
1510       }
1511       ierr = PetscLogFlops(18.0);CHKERRQ(ierr);
1512       DMPlex_Det3D_Internal(detJ, J);
1513     }
1514     if (invJ) {DMPlex_Invert3D_Internal(invJ, J, *detJ);}
1515   } else {
1516     const PetscInt Nv = 8;
1517     const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
1518     const PetscInt dim = 3;
1519     const PetscInt dimR = 3;
1520     PetscReal zOrder[24];
1521     PetscReal zCoeff[24];
1522     PetscInt  i, j, k, l;
1523 
1524     for (i = 0; i < Nv; i++) {
1525       PetscInt zi = zToPlex[i];
1526 
1527       for (j = 0; j < dim; j++) {
1528         zOrder[dim * i + j] = PetscRealPart(coords[dim * zi + j]);
1529       }
1530     }
1531     for (j = 0; j < dim; j++) {
1532       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]);
1533       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]);
1534       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]);
1535       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]);
1536       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]);
1537       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]);
1538       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]);
1539       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]);
1540     }
1541     for (i = 0; i < Nq; i++) {
1542       PetscReal xi = points[dimR * i], eta = points[dimR * i + 1], theta = points[dimR * i + 2];
1543 
1544       if (v) {
1545         PetscReal extPoint[8];
1546 
1547         extPoint[0] = 1.;
1548         extPoint[1] = xi;
1549         extPoint[2] = eta;
1550         extPoint[3] = xi * eta;
1551         extPoint[4] = theta;
1552         extPoint[5] = theta * xi;
1553         extPoint[6] = theta * eta;
1554         extPoint[7] = theta * eta * xi;
1555         for (j = 0; j < dim; j++) {
1556           PetscReal val = 0.;
1557 
1558           for (k = 0; k < Nv; k++) {
1559             val += extPoint[k] * zCoeff[dim * k + j];
1560           }
1561           v[i * dim + j] = val;
1562         }
1563       }
1564       if (J) {
1565         PetscReal extJ[24];
1566 
1567         extJ[0]  = 0.         ; extJ[1]  = 0.        ; extJ[2]  = 0.      ;
1568         extJ[3]  = 1.         ; extJ[4]  = 0.        ; extJ[5]  = 0.      ;
1569         extJ[6]  = 0.         ; extJ[7]  = 1.        ; extJ[8]  = 0.      ;
1570         extJ[9]  = eta        ; extJ[10] = xi        ; extJ[11] = 0.      ;
1571         extJ[12] = 0.         ; extJ[13] = 0.        ; extJ[14] = 1.      ;
1572         extJ[15] = theta      ; extJ[16] = 0.        ; extJ[17] = xi      ;
1573         extJ[18] = 0.         ; extJ[19] = theta     ; extJ[20] = eta     ;
1574         extJ[21] = theta * eta; extJ[22] = theta * xi; extJ[23] = eta * xi;
1575 
1576         for (j = 0; j < dim; j++) {
1577           for (k = 0; k < dimR; k++) {
1578             PetscReal val = 0.;
1579 
1580             for (l = 0; l < Nv; l++) {
1581               val += zCoeff[dim * l + j] * extJ[dimR * l + k];
1582             }
1583             J[i * dim * dim + dim * j + k] = val;
1584           }
1585         }
1586         DMPlex_Det3D_Internal(&detJ[i], &J[i * dim * dim]);
1587         if (invJ) {DMPlex_Invert3D_Internal(&invJ[i * dim * dim], &J[i * dim * dim], detJ[i]);}
1588       }
1589     }
1590   }
1591   ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, e, NULL, &coords);CHKERRQ(ierr);
1592   PetscFunctionReturn(0);
1593 }
1594 
1595 static PetscErrorCode DMPlexComputeCellGeometryFEM_Implicit(DM dm, PetscInt cell, PetscQuadrature quad, PetscReal *v, PetscReal *J, PetscReal *invJ, PetscReal *detJ)
1596 {
1597   DMPolytopeType  ct;
1598   PetscInt        depth, dim, coordDim, coneSize, i;
1599   PetscInt        Nq = 0;
1600   const PetscReal *points = NULL;
1601   DMLabel         depthLabel;
1602   PetscReal       xi0[3] = {-1.,-1.,-1.}, v0[3], J0[9], detJ0;
1603   PetscBool       isAffine = PETSC_TRUE;
1604   PetscErrorCode  ierr;
1605 
1606   PetscFunctionBegin;
1607   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
1608   ierr = DMPlexGetConeSize(dm, cell, &coneSize);CHKERRQ(ierr);
1609   ierr = DMPlexGetDepthLabel(dm, &depthLabel);CHKERRQ(ierr);
1610   ierr = DMLabelGetValue(depthLabel, cell, &dim);CHKERRQ(ierr);
1611   if (depth == 1 && dim == 1) {
1612     ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1613   }
1614   ierr = DMGetCoordinateDim(dm, &coordDim);CHKERRQ(ierr);
1615   if (coordDim > 3) SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unsupported coordinate dimension %D > 3", coordDim);
1616   if (quad) {ierr = PetscQuadratureGetData(quad, NULL, NULL, &Nq, &points, NULL);CHKERRQ(ierr);}
1617   ierr = DMPlexGetCellType(dm, cell, &ct);CHKERRQ(ierr);
1618   switch (ct) {
1619     case DM_POLYTOPE_POINT:
1620     ierr = DMPlexComputePointGeometry_Internal(dm, cell, v, J, invJ, detJ);CHKERRQ(ierr);
1621     isAffine = PETSC_FALSE;
1622     break;
1623     case DM_POLYTOPE_SEGMENT:
1624     case DM_POLYTOPE_POINT_PRISM_TENSOR:
1625     if (Nq) {ierr = DMPlexComputeLineGeometry_Internal(dm, cell, v0, J0, NULL, &detJ0);CHKERRQ(ierr);}
1626     else    {ierr = DMPlexComputeLineGeometry_Internal(dm, cell, v,  J,  invJ,  detJ);CHKERRQ(ierr);}
1627     break;
1628     case DM_POLYTOPE_TRIANGLE:
1629     if (Nq) {ierr = DMPlexComputeTriangleGeometry_Internal(dm, cell, v0, J0, NULL, &detJ0);CHKERRQ(ierr);}
1630     else    {ierr = DMPlexComputeTriangleGeometry_Internal(dm, cell, v,  J,  invJ,  detJ);CHKERRQ(ierr);}
1631     break;
1632     case DM_POLYTOPE_QUADRILATERAL:
1633     ierr = DMPlexComputeRectangleGeometry_Internal(dm, cell, PETSC_FALSE, Nq, points, v, J, invJ, detJ);CHKERRQ(ierr);
1634     isAffine = PETSC_FALSE;
1635     break;
1636     case DM_POLYTOPE_SEG_PRISM_TENSOR:
1637     ierr = DMPlexComputeRectangleGeometry_Internal(dm, cell, PETSC_TRUE, Nq, points, v, J, invJ, detJ);CHKERRQ(ierr);
1638     isAffine = PETSC_FALSE;
1639     break;
1640     case DM_POLYTOPE_TETRAHEDRON:
1641     if (Nq) {ierr = DMPlexComputeTetrahedronGeometry_Internal(dm, cell, v0, J0, NULL, &detJ0);CHKERRQ(ierr);}
1642     else    {ierr = DMPlexComputeTetrahedronGeometry_Internal(dm, cell, v,  J,  invJ,  detJ);CHKERRQ(ierr);}
1643     break;
1644     case DM_POLYTOPE_HEXAHEDRON:
1645     ierr = DMPlexComputeHexahedronGeometry_Internal(dm, cell, Nq, points, v, J, invJ, detJ);CHKERRQ(ierr);
1646     isAffine = PETSC_FALSE;
1647     break;
1648     default: SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No element geometry for cell %D with type %s", cell, DMPolytopeTypes[ct]);
1649   }
1650   if (isAffine && Nq) {
1651     if (v) {
1652       for (i = 0; i < Nq; i++) {
1653         CoordinatesRefToReal(coordDim, dim, xi0, v0, J0, &points[dim * i], &v[coordDim * i]);
1654       }
1655     }
1656     if (detJ) {
1657       for (i = 0; i < Nq; i++) {
1658         detJ[i] = detJ0;
1659       }
1660     }
1661     if (J) {
1662       PetscInt k;
1663 
1664       for (i = 0, k = 0; i < Nq; i++) {
1665         PetscInt j;
1666 
1667         for (j = 0; j < coordDim * coordDim; j++, k++) {
1668           J[k] = J0[j];
1669         }
1670       }
1671     }
1672     if (invJ) {
1673       PetscInt k;
1674       switch (coordDim) {
1675       case 0:
1676         break;
1677       case 1:
1678         invJ[0] = 1./J0[0];
1679         break;
1680       case 2:
1681         DMPlex_Invert2D_Internal(invJ, J0, detJ0);
1682         break;
1683       case 3:
1684         DMPlex_Invert3D_Internal(invJ, J0, detJ0);
1685         break;
1686       }
1687       for (i = 1, k = coordDim * coordDim; i < Nq; i++) {
1688         PetscInt j;
1689 
1690         for (j = 0; j < coordDim * coordDim; j++, k++) {
1691           invJ[k] = invJ[j];
1692         }
1693       }
1694     }
1695   }
1696   PetscFunctionReturn(0);
1697 }
1698 
1699 /*@C
1700   DMPlexComputeCellGeometryAffineFEM - Assuming an affine map, compute the Jacobian, inverse Jacobian, and Jacobian determinant for a given cell
1701 
1702   Collective on dm
1703 
1704   Input Parameters:
1705 + dm   - the DM
1706 - cell - the cell
1707 
1708   Output Parameters:
1709 + v0   - the translation part of this affine transform
1710 . J    - the Jacobian of the transform from the reference element
1711 . invJ - the inverse of the Jacobian
1712 - detJ - the Jacobian determinant
1713 
1714   Level: advanced
1715 
1716   Fortran Notes:
1717   Since it returns arrays, this routine is only available in Fortran 90, and you must
1718   include petsc.h90 in your code.
1719 
1720 .seealso: DMPlexComputeCellGeometryFEM(), DMGetCoordinateSection(), DMGetCoordinates()
1721 @*/
1722 PetscErrorCode DMPlexComputeCellGeometryAffineFEM(DM dm, PetscInt cell, PetscReal *v0, PetscReal *J, PetscReal *invJ, PetscReal *detJ)
1723 {
1724   PetscErrorCode ierr;
1725 
1726   PetscFunctionBegin;
1727   ierr = DMPlexComputeCellGeometryFEM_Implicit(dm,cell,NULL,v0,J,invJ,detJ);CHKERRQ(ierr);
1728   PetscFunctionReturn(0);
1729 }
1730 
1731 static PetscErrorCode DMPlexComputeCellGeometryFEM_FE(DM dm, PetscFE fe, PetscInt point, PetscQuadrature quad, PetscReal v[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1732 {
1733   PetscQuadrature   feQuad;
1734   PetscSection      coordSection;
1735   Vec               coordinates;
1736   PetscScalar      *coords = NULL;
1737   const PetscReal  *quadPoints;
1738   PetscTabulation T;
1739   PetscInt          dim, cdim, pdim, qdim, Nq, numCoords, q;
1740   PetscErrorCode    ierr;
1741 
1742   PetscFunctionBegin;
1743   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
1744   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
1745   ierr = DMPlexVecGetClosure(dm, coordSection, coordinates, point, &numCoords, &coords);CHKERRQ(ierr);
1746   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1747   ierr = DMGetCoordinateDim(dm, &cdim);CHKERRQ(ierr);
1748   if (!quad) { /* use the first point of the first functional of the dual space */
1749     PetscDualSpace dsp;
1750 
1751     ierr = PetscFEGetDualSpace(fe, &dsp);CHKERRQ(ierr);
1752     ierr = PetscDualSpaceGetFunctional(dsp, 0, &quad);CHKERRQ(ierr);
1753     ierr = PetscQuadratureGetData(quad, &qdim, NULL, &Nq, &quadPoints, NULL);CHKERRQ(ierr);
1754     Nq = 1;
1755   } else {
1756     ierr = PetscQuadratureGetData(quad, &qdim, NULL, &Nq, &quadPoints, NULL);CHKERRQ(ierr);
1757   }
1758   ierr = PetscFEGetDimension(fe, &pdim);CHKERRQ(ierr);
1759   ierr = PetscFEGetQuadrature(fe, &feQuad);CHKERRQ(ierr);
1760   if (feQuad == quad) {
1761     ierr = PetscFEGetCellTabulation(fe, J ? 1 : 0, &T);CHKERRQ(ierr);
1762     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);
1763   } else {
1764     ierr = PetscFECreateTabulation(fe, 1, Nq, quadPoints, J ? 1 : 0, &T);CHKERRQ(ierr);
1765   }
1766   if (qdim != dim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Point dimension %d != quadrature dimension %d", dim, qdim);
1767   {
1768     const PetscReal *basis    = T->T[0];
1769     const PetscReal *basisDer = T->T[1];
1770     PetscReal        detJt;
1771 
1772 #if defined(PETSC_USE_DEBUG)
1773     if (Nq != T->Np)     SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Np %D != %D", Nq, T->Np);
1774     if (pdim != T->Nb)   SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Nb %D != %D", pdim, T->Nb);
1775     if (dim != T->Nc)    SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Nc %D != %D", dim, T->Nc);
1776     if (cdim != T->cdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "cdim %D != %D", cdim, T->cdim);
1777 #endif
1778     if (v) {
1779       ierr = PetscArrayzero(v, Nq*cdim);CHKERRQ(ierr);
1780       for (q = 0; q < Nq; ++q) {
1781         PetscInt i, k;
1782 
1783         for (k = 0; k < pdim; ++k) {
1784           const PetscInt vertex = k/cdim;
1785           for (i = 0; i < cdim; ++i) {
1786             v[q*cdim + i] += basis[(q*pdim + k)*cdim + i] * PetscRealPart(coords[vertex*cdim + i]);
1787           }
1788         }
1789         ierr = PetscLogFlops(2.0*pdim*cdim);CHKERRQ(ierr);
1790       }
1791     }
1792     if (J) {
1793       ierr = PetscArrayzero(J, Nq*cdim*cdim);CHKERRQ(ierr);
1794       for (q = 0; q < Nq; ++q) {
1795         PetscInt i, j, k, c, r;
1796 
1797         /* J = dx_i/d\xi_j = sum[k=0,n-1] dN_k/d\xi_j * x_i(k) */
1798         for (k = 0; k < pdim; ++k) {
1799           const PetscInt vertex = k/cdim;
1800           for (j = 0; j < dim; ++j) {
1801             for (i = 0; i < cdim; ++i) {
1802               J[(q*cdim + i)*cdim + j] += basisDer[((q*pdim + k)*cdim + i)*dim + j] * PetscRealPart(coords[vertex*cdim + i]);
1803             }
1804           }
1805         }
1806         ierr = PetscLogFlops(2.0*pdim*dim*cdim);CHKERRQ(ierr);
1807         if (cdim > dim) {
1808           for (c = dim; c < cdim; ++c)
1809             for (r = 0; r < cdim; ++r)
1810               J[r*cdim+c] = r == c ? 1.0 : 0.0;
1811         }
1812         if (!detJ && !invJ) continue;
1813         detJt = 0.;
1814         switch (cdim) {
1815         case 3:
1816           DMPlex_Det3D_Internal(&detJt, &J[q*cdim*dim]);
1817           if (invJ) {DMPlex_Invert3D_Internal(&invJ[q*cdim*dim], &J[q*cdim*dim], detJt);}
1818           break;
1819         case 2:
1820           DMPlex_Det2D_Internal(&detJt, &J[q*cdim*dim]);
1821           if (invJ) {DMPlex_Invert2D_Internal(&invJ[q*cdim*dim], &J[q*cdim*dim], detJt);}
1822           break;
1823         case 1:
1824           detJt = J[q*cdim*dim];
1825           if (invJ) invJ[q*cdim*dim] = 1.0/detJt;
1826         }
1827         if (detJ) detJ[q] = detJt;
1828       }
1829     } else if (detJ || invJ) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Need J to compute invJ or detJ");
1830   }
1831   if (feQuad != quad) {ierr = PetscTabulationDestroy(&T);CHKERRQ(ierr);}
1832   ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, point, &numCoords, &coords);CHKERRQ(ierr);
1833   PetscFunctionReturn(0);
1834 }
1835 
1836 /*@C
1837   DMPlexComputeCellGeometryFEM - Compute the Jacobian, inverse Jacobian, and Jacobian determinant at each quadrature point in the given cell
1838 
1839   Collective on dm
1840 
1841   Input Parameters:
1842 + dm   - the DM
1843 . cell - the cell
1844 - quad - the quadrature containing the points in the reference element where the geometry will be evaluated.  If quad == NULL, geometry will be
1845          evaluated at the first vertex of the reference element
1846 
1847   Output Parameters:
1848 + v    - the image of the transformed quadrature points, otherwise the image of the first vertex in the closure of the reference element
1849 . J    - the Jacobian of the transform from the reference element at each quadrature point
1850 . invJ - the inverse of the Jacobian at each quadrature point
1851 - detJ - the Jacobian determinant at each quadrature point
1852 
1853   Level: advanced
1854 
1855   Fortran Notes:
1856   Since it returns arrays, this routine is only available in Fortran 90, and you must
1857   include petsc.h90 in your code.
1858 
1859 .seealso: DMGetCoordinateSection(), DMGetCoordinates()
1860 @*/
1861 PetscErrorCode DMPlexComputeCellGeometryFEM(DM dm, PetscInt cell, PetscQuadrature quad, PetscReal *v, PetscReal *J, PetscReal *invJ, PetscReal *detJ)
1862 {
1863   DM             cdm;
1864   PetscFE        fe = NULL;
1865   PetscErrorCode ierr;
1866 
1867   PetscFunctionBegin;
1868   PetscValidPointer(detJ, 7);
1869   ierr = DMGetCoordinateDM(dm, &cdm);CHKERRQ(ierr);
1870   if (cdm) {
1871     PetscClassId id;
1872     PetscInt     numFields;
1873     PetscDS      prob;
1874     PetscObject  disc;
1875 
1876     ierr = DMGetNumFields(cdm, &numFields);CHKERRQ(ierr);
1877     if (numFields) {
1878       ierr = DMGetDS(cdm, &prob);CHKERRQ(ierr);
1879       ierr = PetscDSGetDiscretization(prob,0,&disc);CHKERRQ(ierr);
1880       ierr = PetscObjectGetClassId(disc,&id);CHKERRQ(ierr);
1881       if (id == PETSCFE_CLASSID) {
1882         fe = (PetscFE) disc;
1883       }
1884     }
1885   }
1886   if (!fe) {ierr = DMPlexComputeCellGeometryFEM_Implicit(dm, cell, quad, v, J, invJ, detJ);CHKERRQ(ierr);}
1887   else     {ierr = DMPlexComputeCellGeometryFEM_FE(dm, fe, cell, quad, v, J, invJ, detJ);CHKERRQ(ierr);}
1888   PetscFunctionReturn(0);
1889 }
1890 
1891 static PetscErrorCode DMPlexComputeGeometryFVM_1D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
1892 {
1893   PetscSection   coordSection;
1894   Vec            coordinates;
1895   PetscScalar   *coords = NULL;
1896   PetscScalar    tmp[2];
1897   PetscInt       coordSize, d;
1898   PetscErrorCode ierr;
1899 
1900   PetscFunctionBegin;
1901   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
1902   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
1903   ierr = DMPlexVecGetClosure(dm, coordSection, coordinates, cell, &coordSize, &coords);CHKERRQ(ierr);
1904   ierr = DMLocalizeCoordinate_Internal(dm, dim, coords, &coords[dim], tmp);CHKERRQ(ierr);
1905   if (centroid) {
1906     for (d = 0; d < dim; ++d) centroid[d] = 0.5*PetscRealPart(coords[d] + tmp[d]);
1907   }
1908   if (normal) {
1909     PetscReal norm;
1910 
1911     if (dim != 2) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "We only support 2D edges right now");
1912     normal[0]  = -PetscRealPart(coords[1] - tmp[1]);
1913     normal[1]  =  PetscRealPart(coords[0] - tmp[0]);
1914     norm       = DMPlex_NormD_Internal(dim, normal);
1915     for (d = 0; d < dim; ++d) normal[d] /= norm;
1916   }
1917   if (vol) {
1918     *vol = 0.0;
1919     for (d = 0; d < dim; ++d) *vol += PetscSqr(PetscRealPart(coords[d] - tmp[d]));
1920     *vol = PetscSqrtReal(*vol);
1921   }
1922   ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, cell, &coordSize, &coords);CHKERRQ(ierr);
1923   PetscFunctionReturn(0);
1924 }
1925 
1926 /* Centroid_i = (\sum_n A_n Cn_i) / A */
1927 static PetscErrorCode DMPlexComputeGeometryFVM_2D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
1928 {
1929   DMPolytopeType ct;
1930   PetscSection   coordSection;
1931   Vec            coordinates;
1932   PetscScalar   *coords = NULL;
1933   PetscInt       fv[4] = {0, 1, 2, 3};
1934   PetscInt       cdim, coordSize, numCorners, p, d;
1935   PetscErrorCode ierr;
1936 
1937   PetscFunctionBegin;
1938   /* Must check for hybrid cells because prisms have a different orientation scheme */
1939   ierr = DMPlexGetCellType(dm, cell, &ct);CHKERRQ(ierr);
1940   switch (ct) {
1941     case DM_POLYTOPE_SEG_PRISM_TENSOR: fv[2] = 3; fv[3] = 2;break;
1942     default: break;
1943   }
1944   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
1945   ierr = DMPlexGetConeSize(dm, cell, &numCorners);CHKERRQ(ierr);
1946   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
1947   ierr = DMPlexVecGetClosure(dm, coordSection, coordinates, cell, &coordSize, &coords);CHKERRQ(ierr);
1948   ierr = DMGetCoordinateDim(dm, &cdim);CHKERRQ(ierr);
1949 
1950   if (cdim > 2) {
1951     PetscReal c[3] = {0., 0., 0.}, n[3] = {0., 0., 0.}, norm;
1952 
1953     for (p = 0; p < numCorners-2; ++p) {
1954       const PetscReal x0 = PetscRealPart(coords[cdim*fv[p+1]+0] - coords[0]), x1 = PetscRealPart(coords[cdim*fv[p+2]+0] - coords[0]);
1955       const PetscReal y0 = PetscRealPart(coords[cdim*fv[p+1]+1] - coords[1]), y1 = PetscRealPart(coords[cdim*fv[p+2]+1] - coords[1]);
1956       const PetscReal z0 = PetscRealPart(coords[cdim*fv[p+1]+2] - coords[2]), z1 = PetscRealPart(coords[cdim*fv[p+2]+2] - coords[2]);
1957       const PetscReal dx = y0*z1 - z0*y1;
1958       const PetscReal dy = z0*x1 - x0*z1;
1959       const PetscReal dz = x0*y1 - y0*x1;
1960       PetscReal       a  = PetscSqrtReal(dx*dx + dy*dy + dz*dz);
1961 
1962       n[0] += dx;
1963       n[1] += dy;
1964       n[2] += dz;
1965       c[0] += a * PetscRealPart(coords[0] + coords[cdim*fv[p+1]+0] + coords[cdim*fv[p+2]+0])/3.;
1966       c[1] += a * PetscRealPart(coords[1] + coords[cdim*fv[p+1]+1] + coords[cdim*fv[p+2]+1])/3.;
1967       c[2] += a * PetscRealPart(coords[2] + coords[cdim*fv[p+1]+2] + coords[cdim*fv[p+2]+2])/3.;
1968     }
1969     norm = PetscSqrtReal(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
1970     n[0] /= norm;
1971     n[1] /= norm;
1972     n[2] /= norm;
1973     c[0] /= norm;
1974     c[1] /= norm;
1975     c[2] /= norm;
1976     if (vol) *vol = 0.5*norm;
1977     if (centroid) for (d = 0; d < cdim; ++d) centroid[d] = c[d];
1978     if (normal) for (d = 0; d < cdim; ++d) normal[d] = n[d];
1979   } else {
1980     PetscReal vsum = 0.0, csum[2] = {0.0, 0.0}, vtmp, ctmp[4] = {0., 0., 0., 0.};
1981 
1982     for (p = 0; p < numCorners; ++p) {
1983       const PetscInt pi  = p < 4 ? fv[p] : p;
1984       const PetscInt pin = p < 3 ? fv[(p+1)%numCorners] : (p+1)%numCorners;
1985       /* Need to do this copy to get types right */
1986       for (d = 0; d < cdim; ++d) {
1987         ctmp[d]      = PetscRealPart(coords[pi*cdim+d]);
1988         ctmp[cdim+d] = PetscRealPart(coords[pin*cdim+d]);
1989       }
1990       Volume_Triangle_Origin_Internal(&vtmp, ctmp);
1991       vsum += vtmp;
1992       for (d = 0; d < cdim; ++d) csum[d] += (ctmp[d] + ctmp[cdim+d])*vtmp;
1993     }
1994     if (vol) *vol = PetscAbsReal(vsum);
1995     if (centroid) for (d = 0; d < cdim; ++d) centroid[d] = csum[d] / ((cdim+1)*vsum);
1996     if (normal) for (d = 0; d < cdim; ++d) normal[d] = 0.0;
1997   }
1998   ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, cell, &coordSize, &coords);CHKERRQ(ierr);
1999   PetscFunctionReturn(0);
2000 }
2001 
2002 /* Centroid_i = (\sum_n V_n Cn_i) / V */
2003 static PetscErrorCode DMPlexComputeGeometryFVM_3D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2004 {
2005   DMPolytopeType  ct;
2006   PetscSection    coordSection;
2007   Vec             coordinates;
2008   PetscScalar    *coords = NULL;
2009   PetscReal       vsum = 0.0, vtmp, coordsTmp[3*3];
2010   const PetscInt *faces, *facesO;
2011   PetscBool       isHybrid = PETSC_FALSE;
2012   PetscInt        numFaces, f, coordSize, p, d;
2013   PetscErrorCode  ierr;
2014 
2015   PetscFunctionBegin;
2016   if (PetscUnlikely(dim > 3)) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"No support for dim %D > 3",dim);
2017   /* Must check for hybrid cells because prisms have a different orientation scheme */
2018   ierr = DMPlexGetCellType(dm, cell, &ct);CHKERRQ(ierr);
2019   switch (ct) {
2020     case DM_POLYTOPE_POINT_PRISM_TENSOR:
2021     case DM_POLYTOPE_SEG_PRISM_TENSOR:
2022     case DM_POLYTOPE_TRI_PRISM_TENSOR:
2023     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
2024       isHybrid = PETSC_TRUE;
2025     default: break;
2026   }
2027 
2028   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
2029   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
2030 
2031   if (centroid) for (d = 0; d < dim; ++d) centroid[d] = 0.0;
2032   ierr = DMPlexGetConeSize(dm, cell, &numFaces);CHKERRQ(ierr);
2033   ierr = DMPlexGetCone(dm, cell, &faces);CHKERRQ(ierr);
2034   ierr = DMPlexGetConeOrientation(dm, cell, &facesO);CHKERRQ(ierr);
2035   for (f = 0; f < numFaces; ++f) {
2036     PetscBool      flip = isHybrid && f == 0 ? PETSC_TRUE : PETSC_FALSE; /* The first hybrid face is reversed */
2037     DMPolytopeType ct;
2038 
2039     ierr = DMPlexVecGetClosure(dm, coordSection, coordinates, faces[f], &coordSize, &coords);CHKERRQ(ierr);
2040     ierr = DMPlexGetCellType(dm, faces[f], &ct);CHKERRQ(ierr);
2041     switch (ct) {
2042     case DM_POLYTOPE_TRIANGLE:
2043       for (d = 0; d < dim; ++d) {
2044         coordsTmp[0*dim+d] = PetscRealPart(coords[0*dim+d]);
2045         coordsTmp[1*dim+d] = PetscRealPart(coords[1*dim+d]);
2046         coordsTmp[2*dim+d] = PetscRealPart(coords[2*dim+d]);
2047       }
2048       Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2049       if (facesO[f] < 0 || flip) vtmp = -vtmp;
2050       vsum += vtmp;
2051       if (centroid) {           /* Centroid of OABC = (a+b+c)/4 */
2052         for (d = 0; d < dim; ++d) {
2053           for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p*dim+d]*vtmp;
2054         }
2055       }
2056       break;
2057     case DM_POLYTOPE_QUADRILATERAL:
2058     case DM_POLYTOPE_SEG_PRISM_TENSOR:
2059     {
2060       PetscInt fv[4] = {0, 1, 2, 3};
2061 
2062       /* Side faces for hybrid cells are are stored as tensor products */
2063       if (isHybrid && f > 1) {fv[2] = 3; fv[3] = 2;}
2064       /* DO FOR PYRAMID */
2065       /* First tet */
2066       for (d = 0; d < dim; ++d) {
2067         coordsTmp[0*dim+d] = PetscRealPart(coords[fv[0]*dim+d]);
2068         coordsTmp[1*dim+d] = PetscRealPart(coords[fv[1]*dim+d]);
2069         coordsTmp[2*dim+d] = PetscRealPart(coords[fv[3]*dim+d]);
2070       }
2071       Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2072       if (facesO[f] < 0 || flip) vtmp = -vtmp;
2073       vsum += vtmp;
2074       if (centroid) {
2075         for (d = 0; d < dim; ++d) {
2076           for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p*dim+d]*vtmp;
2077         }
2078       }
2079       /* Second tet */
2080       for (d = 0; d < dim; ++d) {
2081         coordsTmp[0*dim+d] = PetscRealPart(coords[fv[1]*dim+d]);
2082         coordsTmp[1*dim+d] = PetscRealPart(coords[fv[2]*dim+d]);
2083         coordsTmp[2*dim+d] = PetscRealPart(coords[fv[3]*dim+d]);
2084       }
2085       Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2086       if (facesO[f] < 0 || flip) vtmp = -vtmp;
2087       vsum += vtmp;
2088       if (centroid) {
2089         for (d = 0; d < dim; ++d) {
2090           for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p*dim+d]*vtmp;
2091         }
2092       }
2093       break;
2094     }
2095     default:
2096       SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle face %D of type %s", faces[f], DMPolytopeTypes[ct]);
2097     }
2098     ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, faces[f], &coordSize, &coords);CHKERRQ(ierr);
2099   }
2100   if (vol)     *vol = PetscAbsReal(vsum);
2101   if (normal)   for (d = 0; d < dim; ++d) normal[d]    = 0.0;
2102   if (centroid) for (d = 0; d < dim; ++d) centroid[d] /= (vsum*4);
2103   PetscFunctionReturn(0);
2104 }
2105 
2106 /*@C
2107   DMPlexComputeCellGeometryFVM - Compute the volume for a given cell
2108 
2109   Collective on dm
2110 
2111   Input Parameters:
2112 + dm   - the DM
2113 - cell - the cell
2114 
2115   Output Parameters:
2116 + volume   - the cell volume
2117 . centroid - the cell centroid
2118 - normal - the cell normal, if appropriate
2119 
2120   Level: advanced
2121 
2122   Fortran Notes:
2123   Since it returns arrays, this routine is only available in Fortran 90, and you must
2124   include petsc.h90 in your code.
2125 
2126 .seealso: DMGetCoordinateSection(), DMGetCoordinates()
2127 @*/
2128 PetscErrorCode DMPlexComputeCellGeometryFVM(DM dm, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2129 {
2130   PetscInt       depth, dim;
2131   PetscErrorCode ierr;
2132 
2133   PetscFunctionBegin;
2134   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
2135   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
2136   if (depth != dim) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh must be interpolated");
2137   ierr = DMPlexGetPointDepth(dm, cell, &depth);CHKERRQ(ierr);
2138   switch (depth) {
2139   case 1:
2140     ierr = DMPlexComputeGeometryFVM_1D_Internal(dm, dim, cell, vol, centroid, normal);CHKERRQ(ierr);
2141     break;
2142   case 2:
2143     ierr = DMPlexComputeGeometryFVM_2D_Internal(dm, dim, cell, vol, centroid, normal);CHKERRQ(ierr);
2144     break;
2145   case 3:
2146     ierr = DMPlexComputeGeometryFVM_3D_Internal(dm, dim, cell, vol, centroid, normal);CHKERRQ(ierr);
2147     break;
2148   default:
2149     SETERRQ2(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unsupported dimension %D (depth %D) for element geometry computation", dim, depth);
2150   }
2151   PetscFunctionReturn(0);
2152 }
2153 
2154 /*@
2155   DMPlexComputeGeometryFEM - Precompute cell geometry for the entire mesh
2156 
2157   Collective on dm
2158 
2159   Input Parameter:
2160 . dm - The DMPlex
2161 
2162   Output Parameter:
2163 . cellgeom - A vector with the cell geometry data for each cell
2164 
2165   Level: beginner
2166 
2167 @*/
2168 PetscErrorCode DMPlexComputeGeometryFEM(DM dm, Vec *cellgeom)
2169 {
2170   DM             dmCell;
2171   Vec            coordinates;
2172   PetscSection   coordSection, sectionCell;
2173   PetscScalar   *cgeom;
2174   PetscInt       cStart, cEnd, c;
2175   PetscErrorCode ierr;
2176 
2177   PetscFunctionBegin;
2178   ierr = DMClone(dm, &dmCell);CHKERRQ(ierr);
2179   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
2180   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
2181   ierr = DMSetCoordinateSection(dmCell, PETSC_DETERMINE, coordSection);CHKERRQ(ierr);
2182   ierr = DMSetCoordinatesLocal(dmCell, coordinates);CHKERRQ(ierr);
2183   ierr = PetscSectionCreate(PetscObjectComm((PetscObject) dm), &sectionCell);CHKERRQ(ierr);
2184   ierr = DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
2185   ierr = PetscSectionSetChart(sectionCell, cStart, cEnd);CHKERRQ(ierr);
2186   /* TODO This needs to be multiplied by Nq for non-affine */
2187   for (c = cStart; c < cEnd; ++c) {ierr = PetscSectionSetDof(sectionCell, c, (PetscInt) PetscCeilReal(((PetscReal) sizeof(PetscFEGeom))/sizeof(PetscScalar)));CHKERRQ(ierr);}
2188   ierr = PetscSectionSetUp(sectionCell);CHKERRQ(ierr);
2189   ierr = DMSetLocalSection(dmCell, sectionCell);CHKERRQ(ierr);
2190   ierr = PetscSectionDestroy(&sectionCell);CHKERRQ(ierr);
2191   ierr = DMCreateLocalVector(dmCell, cellgeom);CHKERRQ(ierr);
2192   ierr = VecGetArray(*cellgeom, &cgeom);CHKERRQ(ierr);
2193   for (c = cStart; c < cEnd; ++c) {
2194     PetscFEGeom *cg;
2195 
2196     ierr = DMPlexPointLocalRef(dmCell, c, cgeom, &cg);CHKERRQ(ierr);
2197     ierr = PetscArrayzero(cg, 1);CHKERRQ(ierr);
2198     ierr = DMPlexComputeCellGeometryFEM(dmCell, c, NULL, cg->v, cg->J, cg->invJ, cg->detJ);CHKERRQ(ierr);
2199     if (*cg->detJ <= 0.0) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %D", (double) *cg->detJ, c);
2200   }
2201   PetscFunctionReturn(0);
2202 }
2203 
2204 /*@
2205   DMPlexComputeGeometryFVM - Computes the cell and face geometry for a finite volume method
2206 
2207   Input Parameter:
2208 . dm - The DM
2209 
2210   Output Parameters:
2211 + cellgeom - A Vec of PetscFVCellGeom data
2212 - facegeom - A Vec of PetscFVFaceGeom data
2213 
2214   Level: developer
2215 
2216 .seealso: PetscFVFaceGeom, PetscFVCellGeom, DMPlexComputeGeometryFEM()
2217 @*/
2218 PetscErrorCode DMPlexComputeGeometryFVM(DM dm, Vec *cellgeom, Vec *facegeom)
2219 {
2220   DM             dmFace, dmCell;
2221   DMLabel        ghostLabel;
2222   PetscSection   sectionFace, sectionCell;
2223   PetscSection   coordSection;
2224   Vec            coordinates;
2225   PetscScalar   *fgeom, *cgeom;
2226   PetscReal      minradius, gminradius;
2227   PetscInt       dim, cStart, cEnd, cEndInterior, c, fStart, fEnd, f;
2228   PetscErrorCode ierr;
2229 
2230   PetscFunctionBegin;
2231   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
2232   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
2233   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
2234   /* Make cell centroids and volumes */
2235   ierr = DMClone(dm, &dmCell);CHKERRQ(ierr);
2236   ierr = DMSetCoordinateSection(dmCell, PETSC_DETERMINE, coordSection);CHKERRQ(ierr);
2237   ierr = DMSetCoordinatesLocal(dmCell, coordinates);CHKERRQ(ierr);
2238   ierr = PetscSectionCreate(PetscObjectComm((PetscObject) dm), &sectionCell);CHKERRQ(ierr);
2239   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
2240   ierr = DMPlexGetGhostCellStratum(dm, &cEndInterior, NULL);CHKERRQ(ierr);
2241   ierr = PetscSectionSetChart(sectionCell, cStart, cEnd);CHKERRQ(ierr);
2242   for (c = cStart; c < cEnd; ++c) {ierr = PetscSectionSetDof(sectionCell, c, (PetscInt) PetscCeilReal(((PetscReal) sizeof(PetscFVCellGeom))/sizeof(PetscScalar)));CHKERRQ(ierr);}
2243   ierr = PetscSectionSetUp(sectionCell);CHKERRQ(ierr);
2244   ierr = DMSetLocalSection(dmCell, sectionCell);CHKERRQ(ierr);
2245   ierr = PetscSectionDestroy(&sectionCell);CHKERRQ(ierr);
2246   ierr = DMCreateLocalVector(dmCell, cellgeom);CHKERRQ(ierr);
2247   if (cEndInterior < 0) cEndInterior = cEnd;
2248   ierr = VecGetArray(*cellgeom, &cgeom);CHKERRQ(ierr);
2249   for (c = cStart; c < cEndInterior; ++c) {
2250     PetscFVCellGeom *cg;
2251 
2252     ierr = DMPlexPointLocalRef(dmCell, c, cgeom, &cg);CHKERRQ(ierr);
2253     ierr = PetscArrayzero(cg, 1);CHKERRQ(ierr);
2254     ierr = DMPlexComputeCellGeometryFVM(dmCell, c, &cg->volume, cg->centroid, NULL);CHKERRQ(ierr);
2255   }
2256   /* Compute face normals and minimum cell radius */
2257   ierr = DMClone(dm, &dmFace);CHKERRQ(ierr);
2258   ierr = PetscSectionCreate(PetscObjectComm((PetscObject) dm), &sectionFace);CHKERRQ(ierr);
2259   ierr = DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);CHKERRQ(ierr);
2260   ierr = PetscSectionSetChart(sectionFace, fStart, fEnd);CHKERRQ(ierr);
2261   for (f = fStart; f < fEnd; ++f) {ierr = PetscSectionSetDof(sectionFace, f, (PetscInt) PetscCeilReal(((PetscReal) sizeof(PetscFVFaceGeom))/sizeof(PetscScalar)));CHKERRQ(ierr);}
2262   ierr = PetscSectionSetUp(sectionFace);CHKERRQ(ierr);
2263   ierr = DMSetLocalSection(dmFace, sectionFace);CHKERRQ(ierr);
2264   ierr = PetscSectionDestroy(&sectionFace);CHKERRQ(ierr);
2265   ierr = DMCreateLocalVector(dmFace, facegeom);CHKERRQ(ierr);
2266   ierr = VecGetArray(*facegeom, &fgeom);CHKERRQ(ierr);
2267   ierr = DMGetLabel(dm, "ghost", &ghostLabel);CHKERRQ(ierr);
2268   minradius = PETSC_MAX_REAL;
2269   for (f = fStart; f < fEnd; ++f) {
2270     PetscFVFaceGeom *fg;
2271     PetscReal        area;
2272     const PetscInt  *cells;
2273     PetscInt         ncells, ghost = -1, d, numChildren;
2274 
2275     if (ghostLabel) {ierr = DMLabelGetValue(ghostLabel, f, &ghost);CHKERRQ(ierr);}
2276     ierr = DMPlexGetTreeChildren(dm,f,&numChildren,NULL);CHKERRQ(ierr);
2277     ierr = DMPlexGetSupport(dm, f, &cells);CHKERRQ(ierr);
2278     ierr = DMPlexGetSupportSize(dm, f, &ncells);CHKERRQ(ierr);
2279     /* It is possible to get a face with no support when using partition overlap */
2280     if (!ncells || ghost >= 0 || numChildren) continue;
2281     ierr = DMPlexPointLocalRef(dmFace, f, fgeom, &fg);CHKERRQ(ierr);
2282     ierr = DMPlexComputeCellGeometryFVM(dm, f, &area, fg->centroid, fg->normal);CHKERRQ(ierr);
2283     for (d = 0; d < dim; ++d) fg->normal[d] *= area;
2284     /* Flip face orientation if necessary to match ordering in support, and Update minimum radius */
2285     {
2286       PetscFVCellGeom *cL, *cR;
2287       PetscReal       *lcentroid, *rcentroid;
2288       PetscReal        l[3], r[3], v[3];
2289 
2290       ierr = DMPlexPointLocalRead(dmCell, cells[0], cgeom, &cL);CHKERRQ(ierr);
2291       lcentroid = cells[0] >= cEndInterior ? fg->centroid : cL->centroid;
2292       if (ncells > 1) {
2293         ierr = DMPlexPointLocalRead(dmCell, cells[1], cgeom, &cR);CHKERRQ(ierr);
2294         rcentroid = cells[1] >= cEndInterior ? fg->centroid : cR->centroid;
2295       }
2296       else {
2297         rcentroid = fg->centroid;
2298       }
2299       ierr = DMLocalizeCoordinateReal_Internal(dm, dim, fg->centroid, lcentroid, l);CHKERRQ(ierr);
2300       ierr = DMLocalizeCoordinateReal_Internal(dm, dim, fg->centroid, rcentroid, r);CHKERRQ(ierr);
2301       DMPlex_WaxpyD_Internal(dim, -1, l, r, v);
2302       if (DMPlex_DotRealD_Internal(dim, fg->normal, v) < 0) {
2303         for (d = 0; d < dim; ++d) fg->normal[d] = -fg->normal[d];
2304       }
2305       if (DMPlex_DotRealD_Internal(dim, fg->normal, v) <= 0) {
2306         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]);
2307         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]);
2308         SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Direction for face %d could not be fixed", f);
2309       }
2310       if (cells[0] < cEndInterior) {
2311         DMPlex_WaxpyD_Internal(dim, -1, fg->centroid, cL->centroid, v);
2312         minradius = PetscMin(minradius, DMPlex_NormD_Internal(dim, v));
2313       }
2314       if (ncells > 1 && cells[1] < cEndInterior) {
2315         DMPlex_WaxpyD_Internal(dim, -1, fg->centroid, cR->centroid, v);
2316         minradius = PetscMin(minradius, DMPlex_NormD_Internal(dim, v));
2317       }
2318     }
2319   }
2320   ierr = MPIU_Allreduce(&minradius, &gminradius, 1, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm));CHKERRMPI(ierr);
2321   ierr = DMPlexSetMinRadius(dm, gminradius);CHKERRQ(ierr);
2322   /* Compute centroids of ghost cells */
2323   for (c = cEndInterior; c < cEnd; ++c) {
2324     PetscFVFaceGeom *fg;
2325     const PetscInt  *cone,    *support;
2326     PetscInt         coneSize, supportSize, s;
2327 
2328     ierr = DMPlexGetConeSize(dmCell, c, &coneSize);CHKERRQ(ierr);
2329     if (coneSize != 1) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Ghost cell %d has cone size %d != 1", c, coneSize);
2330     ierr = DMPlexGetCone(dmCell, c, &cone);CHKERRQ(ierr);
2331     ierr = DMPlexGetSupportSize(dmCell, cone[0], &supportSize);CHKERRQ(ierr);
2332     if (supportSize != 2) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %d has support size %d != 2", cone[0], supportSize);
2333     ierr = DMPlexGetSupport(dmCell, cone[0], &support);CHKERRQ(ierr);
2334     ierr = DMPlexPointLocalRef(dmFace, cone[0], fgeom, &fg);CHKERRQ(ierr);
2335     for (s = 0; s < 2; ++s) {
2336       /* Reflect ghost centroid across plane of face */
2337       if (support[s] == c) {
2338         PetscFVCellGeom       *ci;
2339         PetscFVCellGeom       *cg;
2340         PetscReal              c2f[3], a;
2341 
2342         ierr = DMPlexPointLocalRead(dmCell, support[(s+1)%2], cgeom, &ci);CHKERRQ(ierr);
2343         DMPlex_WaxpyD_Internal(dim, -1, ci->centroid, fg->centroid, c2f); /* cell to face centroid */
2344         a    = DMPlex_DotRealD_Internal(dim, c2f, fg->normal)/DMPlex_DotRealD_Internal(dim, fg->normal, fg->normal);
2345         ierr = DMPlexPointLocalRef(dmCell, support[s], cgeom, &cg);CHKERRQ(ierr);
2346         DMPlex_WaxpyD_Internal(dim, 2*a, fg->normal, ci->centroid, cg->centroid);
2347         cg->volume = ci->volume;
2348       }
2349     }
2350   }
2351   ierr = VecRestoreArray(*facegeom, &fgeom);CHKERRQ(ierr);
2352   ierr = VecRestoreArray(*cellgeom, &cgeom);CHKERRQ(ierr);
2353   ierr = DMDestroy(&dmCell);CHKERRQ(ierr);
2354   ierr = DMDestroy(&dmFace);CHKERRQ(ierr);
2355   PetscFunctionReturn(0);
2356 }
2357 
2358 /*@C
2359   DMPlexGetMinRadius - Returns the minimum distance from any cell centroid to a face
2360 
2361   Not collective
2362 
2363   Input Parameter:
2364 . dm - the DM
2365 
2366   Output Parameter:
2367 . minradius - the minimum cell radius
2368 
2369   Level: developer
2370 
2371 .seealso: DMGetCoordinates()
2372 @*/
2373 PetscErrorCode DMPlexGetMinRadius(DM dm, PetscReal *minradius)
2374 {
2375   PetscFunctionBegin;
2376   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
2377   PetscValidPointer(minradius,2);
2378   *minradius = ((DM_Plex*) dm->data)->minradius;
2379   PetscFunctionReturn(0);
2380 }
2381 
2382 /*@C
2383   DMPlexSetMinRadius - Sets the minimum distance from the cell centroid to a face
2384 
2385   Logically collective
2386 
2387   Input Parameters:
2388 + dm - the DM
2389 - minradius - the minimum cell radius
2390 
2391   Level: developer
2392 
2393 .seealso: DMSetCoordinates()
2394 @*/
2395 PetscErrorCode DMPlexSetMinRadius(DM dm, PetscReal minradius)
2396 {
2397   PetscFunctionBegin;
2398   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
2399   ((DM_Plex*) dm->data)->minradius = minradius;
2400   PetscFunctionReturn(0);
2401 }
2402 
2403 static PetscErrorCode BuildGradientReconstruction_Internal(DM dm, PetscFV fvm, DM dmFace, PetscScalar *fgeom, DM dmCell, PetscScalar *cgeom)
2404 {
2405   DMLabel        ghostLabel;
2406   PetscScalar   *dx, *grad, **gref;
2407   PetscInt       dim, cStart, cEnd, c, cEndInterior, maxNumFaces;
2408   PetscErrorCode ierr;
2409 
2410   PetscFunctionBegin;
2411   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
2412   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
2413   ierr = DMPlexGetGhostCellStratum(dm, &cEndInterior, NULL);CHKERRQ(ierr);
2414   cEndInterior = cEndInterior < 0 ? cEnd : cEndInterior;
2415   ierr = DMPlexGetMaxSizes(dm, &maxNumFaces, NULL);CHKERRQ(ierr);
2416   ierr = PetscFVLeastSquaresSetMaxFaces(fvm, maxNumFaces);CHKERRQ(ierr);
2417   ierr = DMGetLabel(dm, "ghost", &ghostLabel);CHKERRQ(ierr);
2418   ierr = PetscMalloc3(maxNumFaces*dim, &dx, maxNumFaces*dim, &grad, maxNumFaces, &gref);CHKERRQ(ierr);
2419   for (c = cStart; c < cEndInterior; c++) {
2420     const PetscInt        *faces;
2421     PetscInt               numFaces, usedFaces, f, d;
2422     PetscFVCellGeom        *cg;
2423     PetscBool              boundary;
2424     PetscInt               ghost;
2425 
2426     ierr = DMPlexPointLocalRead(dmCell, c, cgeom, &cg);CHKERRQ(ierr);
2427     ierr = DMPlexGetConeSize(dm, c, &numFaces);CHKERRQ(ierr);
2428     ierr = DMPlexGetCone(dm, c, &faces);CHKERRQ(ierr);
2429     if (numFaces < dim) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Cell %D has only %D faces, not enough for gradient reconstruction", c, numFaces);
2430     for (f = 0, usedFaces = 0; f < numFaces; ++f) {
2431       PetscFVCellGeom       *cg1;
2432       PetscFVFaceGeom       *fg;
2433       const PetscInt        *fcells;
2434       PetscInt               ncell, side;
2435 
2436       ierr = DMLabelGetValue(ghostLabel, faces[f], &ghost);CHKERRQ(ierr);
2437       ierr = DMIsBoundaryPoint(dm, faces[f], &boundary);CHKERRQ(ierr);
2438       if ((ghost >= 0) || boundary) continue;
2439       ierr  = DMPlexGetSupport(dm, faces[f], &fcells);CHKERRQ(ierr);
2440       side  = (c != fcells[0]); /* c is on left=0 or right=1 of face */
2441       ncell = fcells[!side];    /* the neighbor */
2442       ierr  = DMPlexPointLocalRef(dmFace, faces[f], fgeom, &fg);CHKERRQ(ierr);
2443       ierr  = DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1);CHKERRQ(ierr);
2444       for (d = 0; d < dim; ++d) dx[usedFaces*dim+d] = cg1->centroid[d] - cg->centroid[d];
2445       gref[usedFaces++] = fg->grad[side];  /* Gradient reconstruction term will go here */
2446     }
2447     if (!usedFaces) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_USER, "Mesh contains isolated cell (no neighbors). Is it intentional?");
2448     ierr = PetscFVComputeGradient(fvm, usedFaces, dx, grad);CHKERRQ(ierr);
2449     for (f = 0, usedFaces = 0; f < numFaces; ++f) {
2450       ierr = DMLabelGetValue(ghostLabel, faces[f], &ghost);CHKERRQ(ierr);
2451       ierr = DMIsBoundaryPoint(dm, faces[f], &boundary);CHKERRQ(ierr);
2452       if ((ghost >= 0) || boundary) continue;
2453       for (d = 0; d < dim; ++d) gref[usedFaces][d] = grad[usedFaces*dim+d];
2454       ++usedFaces;
2455     }
2456   }
2457   ierr = PetscFree3(dx, grad, gref);CHKERRQ(ierr);
2458   PetscFunctionReturn(0);
2459 }
2460 
2461 static PetscErrorCode BuildGradientReconstruction_Internal_Tree(DM dm, PetscFV fvm, DM dmFace, PetscScalar *fgeom, DM dmCell, PetscScalar *cgeom)
2462 {
2463   DMLabel        ghostLabel;
2464   PetscScalar   *dx, *grad, **gref;
2465   PetscInt       dim, cStart, cEnd, c, cEndInterior, fStart, fEnd, f, nStart, nEnd, maxNumFaces = 0;
2466   PetscSection   neighSec;
2467   PetscInt     (*neighbors)[2];
2468   PetscInt      *counter;
2469   PetscErrorCode ierr;
2470 
2471   PetscFunctionBegin;
2472   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
2473   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
2474   ierr = DMPlexGetGhostCellStratum(dm, &cEndInterior, NULL);CHKERRQ(ierr);
2475   if (cEndInterior < 0) cEndInterior = cEnd;
2476   ierr = PetscSectionCreate(PetscObjectComm((PetscObject)dm),&neighSec);CHKERRQ(ierr);
2477   ierr = PetscSectionSetChart(neighSec,cStart,cEndInterior);CHKERRQ(ierr);
2478   ierr = DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);CHKERRQ(ierr);
2479   ierr = DMGetLabel(dm, "ghost", &ghostLabel);CHKERRQ(ierr);
2480   for (f = fStart; f < fEnd; f++) {
2481     const PetscInt        *fcells;
2482     PetscBool              boundary;
2483     PetscInt               ghost = -1;
2484     PetscInt               numChildren, numCells, c;
2485 
2486     if (ghostLabel) {ierr = DMLabelGetValue(ghostLabel, f, &ghost);CHKERRQ(ierr);}
2487     ierr = DMIsBoundaryPoint(dm, f, &boundary);CHKERRQ(ierr);
2488     ierr = DMPlexGetTreeChildren(dm, f, &numChildren, NULL);CHKERRQ(ierr);
2489     if ((ghost >= 0) || boundary || numChildren) continue;
2490     ierr = DMPlexGetSupportSize(dm, f, &numCells);CHKERRQ(ierr);
2491     if (numCells == 2) {
2492       ierr = DMPlexGetSupport(dm, f, &fcells);CHKERRQ(ierr);
2493       for (c = 0; c < 2; c++) {
2494         PetscInt cell = fcells[c];
2495 
2496         if (cell >= cStart && cell < cEndInterior) {
2497           ierr = PetscSectionAddDof(neighSec,cell,1);CHKERRQ(ierr);
2498         }
2499       }
2500     }
2501   }
2502   ierr = PetscSectionSetUp(neighSec);CHKERRQ(ierr);
2503   ierr = PetscSectionGetMaxDof(neighSec,&maxNumFaces);CHKERRQ(ierr);
2504   ierr = PetscFVLeastSquaresSetMaxFaces(fvm, maxNumFaces);CHKERRQ(ierr);
2505   nStart = 0;
2506   ierr = PetscSectionGetStorageSize(neighSec,&nEnd);CHKERRQ(ierr);
2507   ierr = PetscMalloc1((nEnd-nStart),&neighbors);CHKERRQ(ierr);
2508   ierr = PetscCalloc1((cEndInterior-cStart),&counter);CHKERRQ(ierr);
2509   for (f = fStart; f < fEnd; f++) {
2510     const PetscInt        *fcells;
2511     PetscBool              boundary;
2512     PetscInt               ghost = -1;
2513     PetscInt               numChildren, numCells, c;
2514 
2515     if (ghostLabel) {ierr = DMLabelGetValue(ghostLabel, f, &ghost);CHKERRQ(ierr);}
2516     ierr = DMIsBoundaryPoint(dm, f, &boundary);CHKERRQ(ierr);
2517     ierr = DMPlexGetTreeChildren(dm, f, &numChildren, NULL);CHKERRQ(ierr);
2518     if ((ghost >= 0) || boundary || numChildren) continue;
2519     ierr = DMPlexGetSupportSize(dm, f, &numCells);CHKERRQ(ierr);
2520     if (numCells == 2) {
2521       ierr  = DMPlexGetSupport(dm, f, &fcells);CHKERRQ(ierr);
2522       for (c = 0; c < 2; c++) {
2523         PetscInt cell = fcells[c], off;
2524 
2525         if (cell >= cStart && cell < cEndInterior) {
2526           ierr = PetscSectionGetOffset(neighSec,cell,&off);CHKERRQ(ierr);
2527           off += counter[cell - cStart]++;
2528           neighbors[off][0] = f;
2529           neighbors[off][1] = fcells[1 - c];
2530         }
2531       }
2532     }
2533   }
2534   ierr = PetscFree(counter);CHKERRQ(ierr);
2535   ierr = PetscMalloc3(maxNumFaces*dim, &dx, maxNumFaces*dim, &grad, maxNumFaces, &gref);CHKERRQ(ierr);
2536   for (c = cStart; c < cEndInterior; c++) {
2537     PetscInt               numFaces, f, d, off, ghost = -1;
2538     PetscFVCellGeom        *cg;
2539 
2540     ierr = DMPlexPointLocalRead(dmCell, c, cgeom, &cg);CHKERRQ(ierr);
2541     ierr = PetscSectionGetDof(neighSec, c, &numFaces);CHKERRQ(ierr);
2542     ierr = PetscSectionGetOffset(neighSec, c, &off);CHKERRQ(ierr);
2543     if (ghostLabel) {ierr = DMLabelGetValue(ghostLabel, c, &ghost);CHKERRQ(ierr);}
2544     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);
2545     for (f = 0; f < numFaces; ++f) {
2546       PetscFVCellGeom       *cg1;
2547       PetscFVFaceGeom       *fg;
2548       const PetscInt        *fcells;
2549       PetscInt               ncell, side, nface;
2550 
2551       nface = neighbors[off + f][0];
2552       ncell = neighbors[off + f][1];
2553       ierr  = DMPlexGetSupport(dm,nface,&fcells);CHKERRQ(ierr);
2554       side  = (c != fcells[0]);
2555       ierr  = DMPlexPointLocalRef(dmFace, nface, fgeom, &fg);CHKERRQ(ierr);
2556       ierr  = DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1);CHKERRQ(ierr);
2557       for (d = 0; d < dim; ++d) dx[f*dim+d] = cg1->centroid[d] - cg->centroid[d];
2558       gref[f] = fg->grad[side];  /* Gradient reconstruction term will go here */
2559     }
2560     ierr = PetscFVComputeGradient(fvm, numFaces, dx, grad);CHKERRQ(ierr);
2561     for (f = 0; f < numFaces; ++f) {
2562       for (d = 0; d < dim; ++d) gref[f][d] = grad[f*dim+d];
2563     }
2564   }
2565   ierr = PetscFree3(dx, grad, gref);CHKERRQ(ierr);
2566   ierr = PetscSectionDestroy(&neighSec);CHKERRQ(ierr);
2567   ierr = PetscFree(neighbors);CHKERRQ(ierr);
2568   PetscFunctionReturn(0);
2569 }
2570 
2571 /*@
2572   DMPlexComputeGradientFVM - Compute geometric factors for gradient reconstruction, which are stored in the geometry data, and compute layout for gradient data
2573 
2574   Collective on dm
2575 
2576   Input Parameters:
2577 + dm  - The DM
2578 . fvm - The PetscFV
2579 - cellGeometry - The face geometry from DMPlexComputeCellGeometryFVM()
2580 
2581   Input/Output Parameter:
2582 . faceGeometry - The face geometry from DMPlexComputeFaceGeometryFVM(); on output
2583                  the geometric factors for gradient calculation are inserted
2584 
2585   Output Parameter:
2586 . dmGrad - The DM describing the layout of gradient data
2587 
2588   Level: developer
2589 
2590 .seealso: DMPlexGetFaceGeometryFVM(), DMPlexGetCellGeometryFVM()
2591 @*/
2592 PetscErrorCode DMPlexComputeGradientFVM(DM dm, PetscFV fvm, Vec faceGeometry, Vec cellGeometry, DM *dmGrad)
2593 {
2594   DM             dmFace, dmCell;
2595   PetscScalar   *fgeom, *cgeom;
2596   PetscSection   sectionGrad, parentSection;
2597   PetscInt       dim, pdim, cStart, cEnd, cEndInterior, c;
2598   PetscErrorCode ierr;
2599 
2600   PetscFunctionBegin;
2601   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
2602   ierr = PetscFVGetNumComponents(fvm, &pdim);CHKERRQ(ierr);
2603   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
2604   ierr = DMPlexGetGhostCellStratum(dm, &cEndInterior, NULL);CHKERRQ(ierr);
2605   /* Construct the interpolant corresponding to each face from the least-square solution over the cell neighborhood */
2606   ierr = VecGetDM(faceGeometry, &dmFace);CHKERRQ(ierr);
2607   ierr = VecGetDM(cellGeometry, &dmCell);CHKERRQ(ierr);
2608   ierr = VecGetArray(faceGeometry, &fgeom);CHKERRQ(ierr);
2609   ierr = VecGetArray(cellGeometry, &cgeom);CHKERRQ(ierr);
2610   ierr = DMPlexGetTree(dm,&parentSection,NULL,NULL,NULL,NULL);CHKERRQ(ierr);
2611   if (!parentSection) {
2612     ierr = BuildGradientReconstruction_Internal(dm, fvm, dmFace, fgeom, dmCell, cgeom);CHKERRQ(ierr);
2613   } else {
2614     ierr = BuildGradientReconstruction_Internal_Tree(dm, fvm, dmFace, fgeom, dmCell, cgeom);CHKERRQ(ierr);
2615   }
2616   ierr = VecRestoreArray(faceGeometry, &fgeom);CHKERRQ(ierr);
2617   ierr = VecRestoreArray(cellGeometry, &cgeom);CHKERRQ(ierr);
2618   /* Create storage for gradients */
2619   ierr = DMClone(dm, dmGrad);CHKERRQ(ierr);
2620   ierr = PetscSectionCreate(PetscObjectComm((PetscObject) dm), &sectionGrad);CHKERRQ(ierr);
2621   ierr = PetscSectionSetChart(sectionGrad, cStart, cEnd);CHKERRQ(ierr);
2622   for (c = cStart; c < cEnd; ++c) {ierr = PetscSectionSetDof(sectionGrad, c, pdim*dim);CHKERRQ(ierr);}
2623   ierr = PetscSectionSetUp(sectionGrad);CHKERRQ(ierr);
2624   ierr = DMSetLocalSection(*dmGrad, sectionGrad);CHKERRQ(ierr);
2625   ierr = PetscSectionDestroy(&sectionGrad);CHKERRQ(ierr);
2626   PetscFunctionReturn(0);
2627 }
2628 
2629 /*@
2630   DMPlexGetDataFVM - Retrieve precomputed cell geometry
2631 
2632   Collective on dm
2633 
2634   Input Parameters:
2635 + dm  - The DM
2636 - fv  - The PetscFV
2637 
2638   Output Parameters:
2639 + cellGeometry - The cell geometry
2640 . faceGeometry - The face geometry
2641 - gradDM       - The gradient matrices
2642 
2643   Level: developer
2644 
2645 .seealso: DMPlexComputeGeometryFVM()
2646 @*/
2647 PetscErrorCode DMPlexGetDataFVM(DM dm, PetscFV fv, Vec *cellgeom, Vec *facegeom, DM *gradDM)
2648 {
2649   PetscObject    cellgeomobj, facegeomobj;
2650   PetscErrorCode ierr;
2651 
2652   PetscFunctionBegin;
2653   ierr = PetscObjectQuery((PetscObject) dm, "DMPlex_cellgeom_fvm", &cellgeomobj);CHKERRQ(ierr);
2654   if (!cellgeomobj) {
2655     Vec cellgeomInt, facegeomInt;
2656 
2657     ierr = DMPlexComputeGeometryFVM(dm, &cellgeomInt, &facegeomInt);CHKERRQ(ierr);
2658     ierr = PetscObjectCompose((PetscObject) dm, "DMPlex_cellgeom_fvm",(PetscObject)cellgeomInt);CHKERRQ(ierr);
2659     ierr = PetscObjectCompose((PetscObject) dm, "DMPlex_facegeom_fvm",(PetscObject)facegeomInt);CHKERRQ(ierr);
2660     ierr = VecDestroy(&cellgeomInt);CHKERRQ(ierr);
2661     ierr = VecDestroy(&facegeomInt);CHKERRQ(ierr);
2662     ierr = PetscObjectQuery((PetscObject) dm, "DMPlex_cellgeom_fvm", &cellgeomobj);CHKERRQ(ierr);
2663   }
2664   ierr = PetscObjectQuery((PetscObject) dm, "DMPlex_facegeom_fvm", &facegeomobj);CHKERRQ(ierr);
2665   if (cellgeom) *cellgeom = (Vec) cellgeomobj;
2666   if (facegeom) *facegeom = (Vec) facegeomobj;
2667   if (gradDM) {
2668     PetscObject gradobj;
2669     PetscBool   computeGradients;
2670 
2671     ierr = PetscFVGetComputeGradients(fv,&computeGradients);CHKERRQ(ierr);
2672     if (!computeGradients) {
2673       *gradDM = NULL;
2674       PetscFunctionReturn(0);
2675     }
2676     ierr = PetscObjectQuery((PetscObject) dm, "DMPlex_dmgrad_fvm", &gradobj);CHKERRQ(ierr);
2677     if (!gradobj) {
2678       DM dmGradInt;
2679 
2680       ierr = DMPlexComputeGradientFVM(dm,fv,(Vec) facegeomobj,(Vec) cellgeomobj,&dmGradInt);CHKERRQ(ierr);
2681       ierr = PetscObjectCompose((PetscObject) dm, "DMPlex_dmgrad_fvm", (PetscObject)dmGradInt);CHKERRQ(ierr);
2682       ierr = DMDestroy(&dmGradInt);CHKERRQ(ierr);
2683       ierr = PetscObjectQuery((PetscObject) dm, "DMPlex_dmgrad_fvm", &gradobj);CHKERRQ(ierr);
2684     }
2685     *gradDM = (DM) gradobj;
2686   }
2687   PetscFunctionReturn(0);
2688 }
2689 
2690 static PetscErrorCode DMPlexCoordinatesToReference_NewtonUpdate(PetscInt dimC, PetscInt dimR, PetscScalar *J, PetscScalar *invJ, PetscScalar *work,  PetscReal *resNeg, PetscReal *guess)
2691 {
2692   PetscInt l, m;
2693 
2694   PetscFunctionBeginHot;
2695   if (dimC == dimR && dimR <= 3) {
2696     /* invert Jacobian, multiply */
2697     PetscScalar det, idet;
2698 
2699     switch (dimR) {
2700     case 1:
2701       invJ[0] = 1./ J[0];
2702       break;
2703     case 2:
2704       det = J[0] * J[3] - J[1] * J[2];
2705       idet = 1./det;
2706       invJ[0] =  J[3] * idet;
2707       invJ[1] = -J[1] * idet;
2708       invJ[2] = -J[2] * idet;
2709       invJ[3] =  J[0] * idet;
2710       break;
2711     case 3:
2712       {
2713         invJ[0] = J[4] * J[8] - J[5] * J[7];
2714         invJ[1] = J[2] * J[7] - J[1] * J[8];
2715         invJ[2] = J[1] * J[5] - J[2] * J[4];
2716         det = invJ[0] * J[0] + invJ[1] * J[3] + invJ[2] * J[6];
2717         idet = 1./det;
2718         invJ[0] *= idet;
2719         invJ[1] *= idet;
2720         invJ[2] *= idet;
2721         invJ[3]  = idet * (J[5] * J[6] - J[3] * J[8]);
2722         invJ[4]  = idet * (J[0] * J[8] - J[2] * J[6]);
2723         invJ[5]  = idet * (J[2] * J[3] - J[0] * J[5]);
2724         invJ[6]  = idet * (J[3] * J[7] - J[4] * J[6]);
2725         invJ[7]  = idet * (J[1] * J[6] - J[0] * J[7]);
2726         invJ[8]  = idet * (J[0] * J[4] - J[1] * J[3]);
2727       }
2728       break;
2729     }
2730     for (l = 0; l < dimR; l++) {
2731       for (m = 0; m < dimC; m++) {
2732         guess[l] += PetscRealPart(invJ[l * dimC + m]) * resNeg[m];
2733       }
2734     }
2735   } else {
2736 #if defined(PETSC_USE_COMPLEX)
2737     char transpose = 'C';
2738 #else
2739     char transpose = 'T';
2740 #endif
2741     PetscBLASInt m = dimR;
2742     PetscBLASInt n = dimC;
2743     PetscBLASInt one = 1;
2744     PetscBLASInt worksize = dimR * dimC, info;
2745 
2746     for (l = 0; l < dimC; l++) {invJ[l] = resNeg[l];}
2747 
2748     PetscStackCallBLAS("LAPACKgels",LAPACKgels_(&transpose,&m,&n,&one,J,&m,invJ,&n,work,&worksize, &info));
2749     if (info != 0) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Bad argument to GELS");
2750 
2751     for (l = 0; l < dimR; l++) {guess[l] += PetscRealPart(invJ[l]);}
2752   }
2753   PetscFunctionReturn(0);
2754 }
2755 
2756 static PetscErrorCode DMPlexCoordinatesToReference_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[], Vec coords, PetscInt dimC, PetscInt dimR)
2757 {
2758   PetscInt       coordSize, i, j, k, l, m, maxIts = 7, numV = (1 << dimR);
2759   PetscScalar    *coordsScalar = NULL;
2760   PetscReal      *cellData, *cellCoords, *cellCoeffs, *extJ, *resNeg;
2761   PetscScalar    *J, *invJ, *work;
2762   PetscErrorCode ierr;
2763 
2764   PetscFunctionBegin;
2765   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
2766   ierr = DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar);CHKERRQ(ierr);
2767   if (coordSize < dimC * numV) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Expecting at least %D coordinates, got %D",dimC * (1 << dimR), coordSize);
2768   ierr = DMGetWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData);CHKERRQ(ierr);
2769   ierr = DMGetWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J);CHKERRQ(ierr);
2770   cellCoords = &cellData[0];
2771   cellCoeffs = &cellData[coordSize];
2772   extJ       = &cellData[2 * coordSize];
2773   resNeg     = &cellData[2 * coordSize + dimR];
2774   invJ       = &J[dimR * dimC];
2775   work       = &J[2 * dimR * dimC];
2776   if (dimR == 2) {
2777     const PetscInt zToPlex[4] = {0, 1, 3, 2};
2778 
2779     for (i = 0; i < 4; i++) {
2780       PetscInt plexI = zToPlex[i];
2781 
2782       for (j = 0; j < dimC; j++) {
2783         cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
2784       }
2785     }
2786   } else if (dimR == 3) {
2787     const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
2788 
2789     for (i = 0; i < 8; i++) {
2790       PetscInt plexI = zToPlex[i];
2791 
2792       for (j = 0; j < dimC; j++) {
2793         cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
2794       }
2795     }
2796   } else {
2797     for (i = 0; i < coordSize; i++) {cellCoords[i] = PetscRealPart(coordsScalar[i]);}
2798   }
2799   /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */
2800   for (i = 0; i < dimR; i++) {
2801     PetscReal *swap;
2802 
2803     for (j = 0; j < (numV / 2); j++) {
2804       for (k = 0; k < dimC; k++) {
2805         cellCoeffs[dimC * j + k]                = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]);
2806         cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]);
2807       }
2808     }
2809 
2810     if (i < dimR - 1) {
2811       swap = cellCoeffs;
2812       cellCoeffs = cellCoords;
2813       cellCoords = swap;
2814     }
2815   }
2816   ierr = PetscArrayzero(refCoords,numPoints * dimR);CHKERRQ(ierr);
2817   for (j = 0; j < numPoints; j++) {
2818     for (i = 0; i < maxIts; i++) {
2819       PetscReal *guess = &refCoords[dimR * j];
2820 
2821       /* compute -residual and Jacobian */
2822       for (k = 0; k < dimC; k++) {resNeg[k] = realCoords[dimC * j + k];}
2823       for (k = 0; k < dimC * dimR; k++) {J[k] = 0.;}
2824       for (k = 0; k < numV; k++) {
2825         PetscReal extCoord = 1.;
2826         for (l = 0; l < dimR; l++) {
2827           PetscReal coord = guess[l];
2828           PetscInt  dep   = (k & (1 << l)) >> l;
2829 
2830           extCoord *= dep * coord + !dep;
2831           extJ[l] = dep;
2832 
2833           for (m = 0; m < dimR; m++) {
2834             PetscReal coord = guess[m];
2835             PetscInt  dep   = ((k & (1 << m)) >> m) && (m != l);
2836             PetscReal mult  = dep * coord + !dep;
2837 
2838             extJ[l] *= mult;
2839           }
2840         }
2841         for (l = 0; l < dimC; l++) {
2842           PetscReal coeff = cellCoeffs[dimC * k + l];
2843 
2844           resNeg[l] -= coeff * extCoord;
2845           for (m = 0; m < dimR; m++) {
2846             J[dimR * l + m] += coeff * extJ[m];
2847           }
2848         }
2849       }
2850       if (0 && PetscDefined(USE_DEBUG)) {
2851         PetscReal maxAbs = 0.;
2852 
2853         for (l = 0; l < dimC; l++) {
2854           maxAbs = PetscMax(maxAbs,PetscAbsReal(resNeg[l]));
2855         }
2856         ierr = PetscInfo4(dm,"cell %D, point %D, iter %D: res %g\n",cell,j,i,(double) maxAbs);CHKERRQ(ierr);
2857       }
2858 
2859       ierr = DMPlexCoordinatesToReference_NewtonUpdate(dimC,dimR,J,invJ,work,resNeg,guess);CHKERRQ(ierr);
2860     }
2861   }
2862   ierr = DMRestoreWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J);CHKERRQ(ierr);
2863   ierr = DMRestoreWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData);CHKERRQ(ierr);
2864   ierr = DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar);CHKERRQ(ierr);
2865   PetscFunctionReturn(0);
2866 }
2867 
2868 static PetscErrorCode DMPlexReferenceToCoordinates_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt dimC, PetscInt dimR)
2869 {
2870   PetscInt       coordSize, i, j, k, l, numV = (1 << dimR);
2871   PetscScalar    *coordsScalar = NULL;
2872   PetscReal      *cellData, *cellCoords, *cellCoeffs;
2873   PetscErrorCode ierr;
2874 
2875   PetscFunctionBegin;
2876   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
2877   ierr = DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar);CHKERRQ(ierr);
2878   if (coordSize < dimC * numV) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Expecting at least %D coordinates, got %D",dimC * (1 << dimR), coordSize);
2879   ierr = DMGetWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData);CHKERRQ(ierr);
2880   cellCoords = &cellData[0];
2881   cellCoeffs = &cellData[coordSize];
2882   if (dimR == 2) {
2883     const PetscInt zToPlex[4] = {0, 1, 3, 2};
2884 
2885     for (i = 0; i < 4; i++) {
2886       PetscInt plexI = zToPlex[i];
2887 
2888       for (j = 0; j < dimC; j++) {
2889         cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
2890       }
2891     }
2892   } else if (dimR == 3) {
2893     const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
2894 
2895     for (i = 0; i < 8; i++) {
2896       PetscInt plexI = zToPlex[i];
2897 
2898       for (j = 0; j < dimC; j++) {
2899         cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
2900       }
2901     }
2902   } else {
2903     for (i = 0; i < coordSize; i++) {cellCoords[i] = PetscRealPart(coordsScalar[i]);}
2904   }
2905   /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */
2906   for (i = 0; i < dimR; i++) {
2907     PetscReal *swap;
2908 
2909     for (j = 0; j < (numV / 2); j++) {
2910       for (k = 0; k < dimC; k++) {
2911         cellCoeffs[dimC * j + k]                = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]);
2912         cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]);
2913       }
2914     }
2915 
2916     if (i < dimR - 1) {
2917       swap = cellCoeffs;
2918       cellCoeffs = cellCoords;
2919       cellCoords = swap;
2920     }
2921   }
2922   ierr = PetscArrayzero(realCoords,numPoints * dimC);CHKERRQ(ierr);
2923   for (j = 0; j < numPoints; j++) {
2924     const PetscReal *guess  = &refCoords[dimR * j];
2925     PetscReal       *mapped = &realCoords[dimC * j];
2926 
2927     for (k = 0; k < numV; k++) {
2928       PetscReal extCoord = 1.;
2929       for (l = 0; l < dimR; l++) {
2930         PetscReal coord = guess[l];
2931         PetscInt  dep   = (k & (1 << l)) >> l;
2932 
2933         extCoord *= dep * coord + !dep;
2934       }
2935       for (l = 0; l < dimC; l++) {
2936         PetscReal coeff = cellCoeffs[dimC * k + l];
2937 
2938         mapped[l] += coeff * extCoord;
2939       }
2940     }
2941   }
2942   ierr = DMRestoreWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData);CHKERRQ(ierr);
2943   ierr = DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar);CHKERRQ(ierr);
2944   PetscFunctionReturn(0);
2945 }
2946 
2947 /* TODO: TOBY please fix this for Nc > 1 */
2948 static PetscErrorCode DMPlexCoordinatesToReference_FE(DM dm, PetscFE fe, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[], Vec coords, PetscInt Nc, PetscInt dimR)
2949 {
2950   PetscInt       numComp, pdim, i, j, k, l, m, maxIter = 7, coordSize;
2951   PetscScalar    *nodes = NULL;
2952   PetscReal      *invV, *modes;
2953   PetscReal      *B, *D, *resNeg;
2954   PetscScalar    *J, *invJ, *work;
2955   PetscErrorCode ierr;
2956 
2957   PetscFunctionBegin;
2958   ierr = PetscFEGetDimension(fe, &pdim);CHKERRQ(ierr);
2959   ierr = PetscFEGetNumComponents(fe, &numComp);CHKERRQ(ierr);
2960   if (numComp != Nc) SETERRQ2(PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"coordinate discretization must have as many components (%D) as embedding dimension (!= %D)",numComp,Nc);
2961   ierr = DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &nodes);CHKERRQ(ierr);
2962   /* convert nodes to values in the stable evaluation basis */
2963   ierr = DMGetWorkArray(dm,pdim,MPIU_REAL,&modes);CHKERRQ(ierr);
2964   invV = fe->invV;
2965   for (i = 0; i < pdim; ++i) {
2966     modes[i] = 0.;
2967     for (j = 0; j < pdim; ++j) {
2968       modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]);
2969     }
2970   }
2971   ierr   = DMGetWorkArray(dm,pdim * Nc + pdim * Nc * dimR + Nc,MPIU_REAL,&B);CHKERRQ(ierr);
2972   D      = &B[pdim*Nc];
2973   resNeg = &D[pdim*Nc * dimR];
2974   ierr = DMGetWorkArray(dm,3 * Nc * dimR,MPIU_SCALAR,&J);CHKERRQ(ierr);
2975   invJ = &J[Nc * dimR];
2976   work = &invJ[Nc * dimR];
2977   for (i = 0; i < numPoints * dimR; i++) {refCoords[i] = 0.;}
2978   for (j = 0; j < numPoints; j++) {
2979       for (i = 0; i < maxIter; i++) { /* we could batch this so that we're not making big B and D arrays all the time */
2980       PetscReal *guess = &refCoords[j * dimR];
2981       ierr = PetscSpaceEvaluate(fe->basisSpace, 1, guess, B, D, NULL);CHKERRQ(ierr);
2982       for (k = 0; k < Nc; k++) {resNeg[k] = realCoords[j * Nc + k];}
2983       for (k = 0; k < Nc * dimR; k++) {J[k] = 0.;}
2984       for (k = 0; k < pdim; k++) {
2985         for (l = 0; l < Nc; l++) {
2986           resNeg[l] -= modes[k] * B[k * Nc + l];
2987           for (m = 0; m < dimR; m++) {
2988             J[l * dimR + m] += modes[k] * D[(k * Nc + l) * dimR + m];
2989           }
2990         }
2991       }
2992       if (0 && PetscDefined(USE_DEBUG)) {
2993         PetscReal maxAbs = 0.;
2994 
2995         for (l = 0; l < Nc; l++) {
2996           maxAbs = PetscMax(maxAbs,PetscAbsReal(resNeg[l]));
2997         }
2998         ierr = PetscInfo4(dm,"cell %D, point %D, iter %D: res %g\n",cell,j,i,(double) maxAbs);CHKERRQ(ierr);
2999       }
3000       ierr = DMPlexCoordinatesToReference_NewtonUpdate(Nc,dimR,J,invJ,work,resNeg,guess);CHKERRQ(ierr);
3001     }
3002   }
3003   ierr = DMRestoreWorkArray(dm,3 * Nc * dimR,MPIU_SCALAR,&J);CHKERRQ(ierr);
3004   ierr = DMRestoreWorkArray(dm,pdim * Nc + pdim * Nc * dimR + Nc,MPIU_REAL,&B);CHKERRQ(ierr);
3005   ierr = DMRestoreWorkArray(dm,pdim,MPIU_REAL,&modes);CHKERRQ(ierr);
3006   ierr = DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes);CHKERRQ(ierr);
3007   PetscFunctionReturn(0);
3008 }
3009 
3010 /* TODO: TOBY please fix this for Nc > 1 */
3011 static PetscErrorCode DMPlexReferenceToCoordinates_FE(DM dm, PetscFE fe, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt Nc, PetscInt dimR)
3012 {
3013   PetscInt       numComp, pdim, i, j, k, l, coordSize;
3014   PetscScalar    *nodes = NULL;
3015   PetscReal      *invV, *modes;
3016   PetscReal      *B;
3017   PetscErrorCode ierr;
3018 
3019   PetscFunctionBegin;
3020   ierr = PetscFEGetDimension(fe, &pdim);CHKERRQ(ierr);
3021   ierr = PetscFEGetNumComponents(fe, &numComp);CHKERRQ(ierr);
3022   if (numComp != Nc) SETERRQ2(PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"coordinate discretization must have as many components (%D) as embedding dimension (!= %D)",numComp,Nc);
3023   ierr = DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &nodes);CHKERRQ(ierr);
3024   /* convert nodes to values in the stable evaluation basis */
3025   ierr = DMGetWorkArray(dm,pdim,MPIU_REAL,&modes);CHKERRQ(ierr);
3026   invV = fe->invV;
3027   for (i = 0; i < pdim; ++i) {
3028     modes[i] = 0.;
3029     for (j = 0; j < pdim; ++j) {
3030       modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]);
3031     }
3032   }
3033   ierr = DMGetWorkArray(dm,numPoints * pdim * Nc,MPIU_REAL,&B);CHKERRQ(ierr);
3034   ierr = PetscSpaceEvaluate(fe->basisSpace, numPoints, refCoords, B, NULL, NULL);CHKERRQ(ierr);
3035   for (i = 0; i < numPoints * Nc; i++) {realCoords[i] = 0.;}
3036   for (j = 0; j < numPoints; j++) {
3037     PetscReal *mapped = &realCoords[j * Nc];
3038 
3039     for (k = 0; k < pdim; k++) {
3040       for (l = 0; l < Nc; l++) {
3041         mapped[l] += modes[k] * B[(j * pdim + k) * Nc + l];
3042       }
3043     }
3044   }
3045   ierr = DMRestoreWorkArray(dm,numPoints * pdim * Nc,MPIU_REAL,&B);CHKERRQ(ierr);
3046   ierr = DMRestoreWorkArray(dm,pdim,MPIU_REAL,&modes);CHKERRQ(ierr);
3047   ierr = DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes);CHKERRQ(ierr);
3048   PetscFunctionReturn(0);
3049 }
3050 
3051 /*@
3052   DMPlexCoordinatesToReference - Pull coordinates back from the mesh to the reference element using a single element
3053   map.  This inversion will be accurate inside the reference element, but may be inaccurate for mappings that do not
3054   extend uniquely outside the reference cell (e.g, most non-affine maps)
3055 
3056   Not collective
3057 
3058   Input Parameters:
3059 + dm         - The mesh, with coordinate maps defined either by a PetscDS for the coordinate DM (see DMGetCoordinateDM()) or
3060                implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or
3061                as a multilinear map for tensor-product elements
3062 . cell       - the cell whose map is used.
3063 . numPoints  - the number of points to locate
3064 - realCoords - (numPoints x coordinate dimension) array of coordinates (see DMGetCoordinateDim())
3065 
3066   Output Parameters:
3067 . refCoords  - (numPoints x dimension) array of reference coordinates (see DMGetDimension())
3068 
3069   Level: intermediate
3070 
3071 .seealso: DMPlexReferenceToCoordinates()
3072 @*/
3073 PetscErrorCode DMPlexCoordinatesToReference(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[])
3074 {
3075   PetscInt       dimC, dimR, depth, cStart, cEnd, i;
3076   DM             coordDM = NULL;
3077   Vec            coords;
3078   PetscFE        fe = NULL;
3079   PetscErrorCode ierr;
3080 
3081   PetscFunctionBegin;
3082   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
3083   ierr = DMGetDimension(dm,&dimR);CHKERRQ(ierr);
3084   ierr = DMGetCoordinateDim(dm,&dimC);CHKERRQ(ierr);
3085   if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(0);
3086   ierr = DMPlexGetDepth(dm,&depth);CHKERRQ(ierr);
3087   ierr = DMGetCoordinatesLocal(dm,&coords);CHKERRQ(ierr);
3088   ierr = DMGetCoordinateDM(dm,&coordDM);CHKERRQ(ierr);
3089   if (coordDM) {
3090     PetscInt coordFields;
3091 
3092     ierr = DMGetNumFields(coordDM,&coordFields);CHKERRQ(ierr);
3093     if (coordFields) {
3094       PetscClassId id;
3095       PetscObject  disc;
3096 
3097       ierr = DMGetField(coordDM,0,NULL,&disc);CHKERRQ(ierr);
3098       ierr = PetscObjectGetClassId(disc,&id);CHKERRQ(ierr);
3099       if (id == PETSCFE_CLASSID) {
3100         fe = (PetscFE) disc;
3101       }
3102     }
3103   }
3104   ierr = DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
3105   if (cell < cStart || cell >= cEnd) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"point %D not in cell range [%D,%D)",cell,cStart,cEnd);
3106   if (!fe) { /* implicit discretization: affine or multilinear */
3107     PetscInt  coneSize;
3108     PetscBool isSimplex, isTensor;
3109 
3110     ierr = DMPlexGetConeSize(dm,cell,&coneSize);CHKERRQ(ierr);
3111     isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE;
3112     isTensor  = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE;
3113     if (isSimplex) {
3114       PetscReal detJ, *v0, *J, *invJ;
3115 
3116       ierr = DMGetWorkArray(dm,dimC + 2 * dimC * dimC, MPIU_REAL, &v0);CHKERRQ(ierr);
3117       J    = &v0[dimC];
3118       invJ = &J[dimC * dimC];
3119       ierr = DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, invJ, &detJ);CHKERRQ(ierr);
3120       for (i = 0; i < numPoints; i++) { /* Apply the inverse affine transformation for each point */
3121         const PetscReal x0[3] = {-1.,-1.,-1.};
3122 
3123         CoordinatesRealToRef(dimC, dimR, x0, v0, invJ, &realCoords[dimC * i], &refCoords[dimR * i]);
3124       }
3125       ierr = DMRestoreWorkArray(dm,dimC + 2 * dimC * dimC, MPIU_REAL, &v0);CHKERRQ(ierr);
3126     } else if (isTensor) {
3127       ierr = DMPlexCoordinatesToReference_Tensor(coordDM, cell, numPoints, realCoords, refCoords, coords, dimC, dimR);CHKERRQ(ierr);
3128     } else SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_SUP,"Unrecognized cone size %D",coneSize);
3129   } else {
3130     ierr = DMPlexCoordinatesToReference_FE(coordDM, fe, cell, numPoints, realCoords, refCoords, coords, dimC, dimR);CHKERRQ(ierr);
3131   }
3132   PetscFunctionReturn(0);
3133 }
3134 
3135 /*@
3136   DMPlexReferenceToCoordinates - Map references coordinates to coordinates in the the mesh for a single element map.
3137 
3138   Not collective
3139 
3140   Input Parameters:
3141 + dm         - The mesh, with coordinate maps defined either by a PetscDS for the coordinate DM (see DMGetCoordinateDM()) or
3142                implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or
3143                as a multilinear map for tensor-product elements
3144 . cell       - the cell whose map is used.
3145 . numPoints  - the number of points to locate
3146 - refCoords  - (numPoints x dimension) array of reference coordinates (see DMGetDimension())
3147 
3148   Output Parameters:
3149 . realCoords - (numPoints x coordinate dimension) array of coordinates (see DMGetCoordinateDim())
3150 
3151    Level: intermediate
3152 
3153 .seealso: DMPlexCoordinatesToReference()
3154 @*/
3155 PetscErrorCode DMPlexReferenceToCoordinates(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[])
3156 {
3157   PetscInt       dimC, dimR, depth, cStart, cEnd, i;
3158   DM             coordDM = NULL;
3159   Vec            coords;
3160   PetscFE        fe = NULL;
3161   PetscErrorCode ierr;
3162 
3163   PetscFunctionBegin;
3164   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
3165   ierr = DMGetDimension(dm,&dimR);CHKERRQ(ierr);
3166   ierr = DMGetCoordinateDim(dm,&dimC);CHKERRQ(ierr);
3167   if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(0);
3168   ierr = DMPlexGetDepth(dm,&depth);CHKERRQ(ierr);
3169   ierr = DMGetCoordinatesLocal(dm,&coords);CHKERRQ(ierr);
3170   ierr = DMGetCoordinateDM(dm,&coordDM);CHKERRQ(ierr);
3171   if (coordDM) {
3172     PetscInt coordFields;
3173 
3174     ierr = DMGetNumFields(coordDM,&coordFields);CHKERRQ(ierr);
3175     if (coordFields) {
3176       PetscClassId id;
3177       PetscObject  disc;
3178 
3179       ierr = DMGetField(coordDM,0,NULL,&disc);CHKERRQ(ierr);
3180       ierr = PetscObjectGetClassId(disc,&id);CHKERRQ(ierr);
3181       if (id == PETSCFE_CLASSID) {
3182         fe = (PetscFE) disc;
3183       }
3184     }
3185   }
3186   ierr = DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
3187   if (cell < cStart || cell >= cEnd) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"point %D not in cell range [%D,%D)",cell,cStart,cEnd);
3188   if (!fe) { /* implicit discretization: affine or multilinear */
3189     PetscInt  coneSize;
3190     PetscBool isSimplex, isTensor;
3191 
3192     ierr = DMPlexGetConeSize(dm,cell,&coneSize);CHKERRQ(ierr);
3193     isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE;
3194     isTensor  = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE;
3195     if (isSimplex) {
3196       PetscReal detJ, *v0, *J;
3197 
3198       ierr = DMGetWorkArray(dm,dimC + 2 * dimC * dimC, MPIU_REAL, &v0);CHKERRQ(ierr);
3199       J    = &v0[dimC];
3200       ierr = DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, NULL, &detJ);CHKERRQ(ierr);
3201       for (i = 0; i < numPoints; i++) { /* Apply the affine transformation for each point */
3202         const PetscReal xi0[3] = {-1.,-1.,-1.};
3203 
3204         CoordinatesRefToReal(dimC, dimR, xi0, v0, J, &refCoords[dimR * i], &realCoords[dimC * i]);
3205       }
3206       ierr = DMRestoreWorkArray(dm,dimC + 2 * dimC * dimC, MPIU_REAL, &v0);CHKERRQ(ierr);
3207     } else if (isTensor) {
3208       ierr = DMPlexReferenceToCoordinates_Tensor(coordDM, cell, numPoints, refCoords, realCoords, coords, dimC, dimR);CHKERRQ(ierr);
3209     } else SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_SUP,"Unrecognized cone size %D",coneSize);
3210   } else {
3211     ierr = DMPlexReferenceToCoordinates_FE(coordDM, fe, cell, numPoints, refCoords, realCoords, coords, dimC, dimR);CHKERRQ(ierr);
3212   }
3213   PetscFunctionReturn(0);
3214 }
3215 
3216 /*@C
3217   DMPlexRemapGeometry - This function maps the original DM coordinates to new coordinates.
3218 
3219   Not collective
3220 
3221   Input Parameters:
3222 + dm      - The DM
3223 . time    - The time
3224 - func    - The function transforming current coordinates to new coordaintes
3225 
3226    Calling sequence of func:
3227 $    func(PetscInt dim, PetscInt Nf, PetscInt NfAux,
3228 $         const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
3229 $         const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
3230 $         PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f[]);
3231 
3232 +  dim          - The spatial dimension
3233 .  Nf           - The number of input fields (here 1)
3234 .  NfAux        - The number of input auxiliary fields
3235 .  uOff         - The offset of the coordinates in u[] (here 0)
3236 .  uOff_x       - The offset of the coordinates in u_x[] (here 0)
3237 .  u            - The coordinate values at this point in space
3238 .  u_t          - The coordinate time derivative at this point in space (here NULL)
3239 .  u_x          - The coordinate derivatives at this point in space
3240 .  aOff         - The offset of each auxiliary field in u[]
3241 .  aOff_x       - The offset of each auxiliary field in u_x[]
3242 .  a            - The auxiliary field values at this point in space
3243 .  a_t          - The auxiliary field time derivative at this point in space (or NULL)
3244 .  a_x          - The auxiliary field derivatives at this point in space
3245 .  t            - The current time
3246 .  x            - The coordinates of this point (here not used)
3247 .  numConstants - The number of constants
3248 .  constants    - The value of each constant
3249 -  f            - The new coordinates at this point in space
3250 
3251   Level: intermediate
3252 
3253 .seealso: DMGetCoordinates(), DMGetCoordinatesLocal(), DMGetCoordinateDM(), DMProjectFieldLocal(), DMProjectFieldLabelLocal()
3254 @*/
3255 PetscErrorCode DMPlexRemapGeometry(DM dm, PetscReal time,
3256                                    void (*func)(PetscInt, PetscInt, PetscInt,
3257                                                 const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
3258                                                 const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
3259                                                 PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]))
3260 {
3261   DM             cdm;
3262   DMField        cf;
3263   Vec            lCoords, tmpCoords;
3264   PetscErrorCode ierr;
3265 
3266   PetscFunctionBegin;
3267   ierr = DMGetCoordinateDM(dm, &cdm);CHKERRQ(ierr);
3268   ierr = DMGetCoordinatesLocal(dm, &lCoords);CHKERRQ(ierr);
3269   ierr = DMGetLocalVector(cdm, &tmpCoords);CHKERRQ(ierr);
3270   ierr = VecCopy(lCoords, tmpCoords);CHKERRQ(ierr);
3271   /* We have to do the coordinate field manually right now since the coordinate DM will not have its own */
3272   ierr = DMGetCoordinateField(dm, &cf);CHKERRQ(ierr);
3273   cdm->coordinateField = cf;
3274   ierr = DMProjectFieldLocal(cdm, time, tmpCoords, &func, INSERT_VALUES, lCoords);CHKERRQ(ierr);
3275   cdm->coordinateField = NULL;
3276   ierr = DMRestoreLocalVector(cdm, &tmpCoords);CHKERRQ(ierr);
3277   ierr = DMSetCoordinatesLocal(dm, lCoords);CHKERRQ(ierr);
3278   PetscFunctionReturn(0);
3279 }
3280 
3281 /* Shear applies the transformation, assuming we fix z,
3282   / 1  0  m_0 \
3283   | 0  1  m_1 |
3284   \ 0  0   1  /
3285 */
3286 static void f0_shear(PetscInt dim, PetscInt Nf, PetscInt NfAux,
3287                      const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
3288                      const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
3289                      PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar coords[])
3290 {
3291   const PetscInt Nc = uOff[1]-uOff[0];
3292   const PetscInt ax = (PetscInt) PetscRealPart(constants[0]);
3293   PetscInt       c;
3294 
3295   for (c = 0; c < Nc; ++c) {
3296     coords[c] = u[c] + constants[c+1]*u[ax];
3297   }
3298 }
3299 
3300 /*@C
3301   DMPlexShearGeometry - This shears the domain, meaning adds a multiple of the shear coordinate to all other coordinates.
3302 
3303   Not collective
3304 
3305   Input Parameters:
3306 + dm          - The DM
3307 . direction   - The shear coordinate direction, e.g. 0 is the x-axis
3308 - multipliers - The multiplier m for each direction which is not the shear direction
3309 
3310   Level: intermediate
3311 
3312 .seealso: DMPlexRemapGeometry()
3313 @*/
3314 PetscErrorCode DMPlexShearGeometry(DM dm, DMDirection direction, PetscReal multipliers[])
3315 {
3316   DM             cdm;
3317   PetscDS        cds;
3318   PetscObject    obj;
3319   PetscClassId   id;
3320   PetscScalar   *moduli;
3321   const PetscInt dir = (PetscInt) direction;
3322   PetscInt       dE, d, e;
3323   PetscErrorCode ierr;
3324 
3325   PetscFunctionBegin;
3326   ierr = DMGetCoordinateDM(dm, &cdm);CHKERRQ(ierr);
3327   ierr = DMGetCoordinateDim(dm, &dE);CHKERRQ(ierr);
3328   ierr = PetscMalloc1(dE+1, &moduli);CHKERRQ(ierr);
3329   moduli[0] = dir;
3330   for (d = 0, e = 0; d < dE; ++d) moduli[d+1] = d == dir ? 0.0 : (multipliers ? multipliers[e++] : 1.0);
3331   ierr = DMGetDS(cdm, &cds);CHKERRQ(ierr);
3332   ierr = PetscDSGetDiscretization(cds, 0, &obj);CHKERRQ(ierr);
3333   ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3334   if (id != PETSCFE_CLASSID) {
3335     Vec           lCoords;
3336     PetscSection  cSection;
3337     PetscScalar  *coords;
3338     PetscInt      vStart, vEnd, v;
3339 
3340     ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
3341     ierr = DMGetCoordinateSection(dm, &cSection);CHKERRQ(ierr);
3342     ierr = DMGetCoordinatesLocal(dm, &lCoords);CHKERRQ(ierr);
3343     ierr = VecGetArray(lCoords, &coords);CHKERRQ(ierr);
3344     for (v = vStart; v < vEnd; ++v) {
3345       PetscReal ds;
3346       PetscInt  off, c;
3347 
3348       ierr = PetscSectionGetOffset(cSection, v, &off);CHKERRQ(ierr);
3349       ds   = PetscRealPart(coords[off+dir]);
3350       for (c = 0; c < dE; ++c) coords[off+c] += moduli[c]*ds;
3351     }
3352     ierr = VecRestoreArray(lCoords, &coords);CHKERRQ(ierr);
3353   } else {
3354     ierr = PetscDSSetConstants(cds, dE+1, moduli);CHKERRQ(ierr);
3355     ierr = DMPlexRemapGeometry(dm, 0.0, f0_shear);CHKERRQ(ierr);
3356   }
3357   ierr = PetscFree(moduli);CHKERRQ(ierr);
3358   PetscFunctionReturn(0);
3359 }
3360