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