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