xref: /petsc/src/dm/impls/plex/plexgeometry.c (revision 030f984af8d8bb4c203755d35bded3c05b3d83ce)
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 Parameter:
831 . coords - The coordinates of a segment
832 
833   Output Parameters:
834 + coords - The new y-coordinate, and 0 for x
835 - R - The rotation which accomplishes the projection
836 
837   Level: developer
838 
839 .seealso: DMPlexComputeProjection3Dto1D(), DMPlexComputeProjection3Dto2D()
840 @*/
841 PetscErrorCode DMPlexComputeProjection2Dto1D(PetscScalar coords[], PetscReal R[])
842 {
843   const PetscReal x = PetscRealPart(coords[2] - coords[0]);
844   const PetscReal y = PetscRealPart(coords[3] - coords[1]);
845   const PetscReal r = PetscSqrtReal(x*x + y*y), c = x/r, s = y/r;
846 
847   PetscFunctionBegin;
848   R[0] = c; R[1] = -s;
849   R[2] = s; R[3] =  c;
850   coords[0] = 0.0;
851   coords[1] = r;
852   PetscFunctionReturn(0);
853 }
854 
855 /*@C
856   DMPlexComputeProjection3Dto1D - Rewrite coordinates to be the 1D projection of the 3D coordinates
857 
858   Not collective
859 
860   Input Parameter:
861 . coords - The coordinates of a segment
862 
863   Output Parameters:
864 + coords - The new y-coordinate, and 0 for x and z
865 - R - The rotation which accomplishes the projection
866 
867   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
868 
869   Level: developer
870 
871 .seealso: DMPlexComputeProjection2Dto1D(), DMPlexComputeProjection3Dto2D()
872 @*/
873 PetscErrorCode DMPlexComputeProjection3Dto1D(PetscScalar coords[], PetscReal R[])
874 {
875   PetscReal      x    = PetscRealPart(coords[3] - coords[0]);
876   PetscReal      y    = PetscRealPart(coords[4] - coords[1]);
877   PetscReal      z    = PetscRealPart(coords[5] - coords[2]);
878   PetscReal      r    = PetscSqrtReal(x*x + y*y + z*z);
879   PetscReal      rinv = 1. / r;
880   PetscFunctionBegin;
881 
882   x *= rinv; y *= rinv; z *= rinv;
883   if (x > 0.) {
884     PetscReal inv1pX   = 1./ (1. + x);
885 
886     R[0] = x; R[1] = -y;              R[2] = -z;
887     R[3] = y; R[4] = 1. - y*y*inv1pX; R[5] =     -y*z*inv1pX;
888     R[6] = z; R[7] =     -y*z*inv1pX; R[8] = 1. - z*z*inv1pX;
889   }
890   else {
891     PetscReal inv1mX   = 1./ (1. - x);
892 
893     R[0] = x; R[1] = z;               R[2] = y;
894     R[3] = y; R[4] =     -y*z*inv1mX; R[5] = 1. - y*y*inv1mX;
895     R[6] = z; R[7] = 1. - z*z*inv1mX; R[8] =     -y*z*inv1mX;
896   }
897   coords[0] = 0.0;
898   coords[1] = r;
899   PetscFunctionReturn(0);
900 }
901 
902 /*@
903   DMPlexComputeProjection3Dto2D - Rewrite coordinates of 3 or more coplanar 3D points to a common 2D basis for the
904     plane.  The normal is defined by positive orientation of the first 3 points.
905 
906   Not collective
907 
908   Input Parameter:
909 + coordSize - Length of coordinate array (3x number of points); must be at least 9 (3 points)
910 - coords - The interlaced coordinates of each coplanar 3D point
911 
912   Output Parameters:
913 + coords - The first 2*coordSize/3 entries contain interlaced 2D points, with the rest undefined
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 minium 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 minium 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 . faceGeometry - The face geometry from DMPlexComputeFaceGeometryFVM()
2459 - cellGeometry - The face geometry from DMPlexComputeCellGeometryFVM()
2460 
2461   Output Parameters:
2462 + faceGeometry - The geometric factors for gradient calculation are inserted
2463 - dmGrad - The DM describing the layout of gradient data
2464 
2465   Level: developer
2466 
2467 .seealso: DMPlexGetFaceGeometryFVM(), DMPlexGetCellGeometryFVM()
2468 @*/
2469 PetscErrorCode DMPlexComputeGradientFVM(DM dm, PetscFV fvm, Vec faceGeometry, Vec cellGeometry, DM *dmGrad)
2470 {
2471   DM             dmFace, dmCell;
2472   PetscScalar   *fgeom, *cgeom;
2473   PetscSection   sectionGrad, parentSection;
2474   PetscInt       dim, pdim, cStart, cEnd, cEndInterior, c;
2475   PetscErrorCode ierr;
2476 
2477   PetscFunctionBegin;
2478   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
2479   ierr = PetscFVGetNumComponents(fvm, &pdim);CHKERRQ(ierr);
2480   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
2481   ierr = DMPlexGetGhostCellStratum(dm, &cEndInterior, NULL);CHKERRQ(ierr);
2482   /* Construct the interpolant corresponding to each face from the least-square solution over the cell neighborhood */
2483   ierr = VecGetDM(faceGeometry, &dmFace);CHKERRQ(ierr);
2484   ierr = VecGetDM(cellGeometry, &dmCell);CHKERRQ(ierr);
2485   ierr = VecGetArray(faceGeometry, &fgeom);CHKERRQ(ierr);
2486   ierr = VecGetArray(cellGeometry, &cgeom);CHKERRQ(ierr);
2487   ierr = DMPlexGetTree(dm,&parentSection,NULL,NULL,NULL,NULL);CHKERRQ(ierr);
2488   if (!parentSection) {
2489     ierr = BuildGradientReconstruction_Internal(dm, fvm, dmFace, fgeom, dmCell, cgeom);CHKERRQ(ierr);
2490   } else {
2491     ierr = BuildGradientReconstruction_Internal_Tree(dm, fvm, dmFace, fgeom, dmCell, cgeom);CHKERRQ(ierr);
2492   }
2493   ierr = VecRestoreArray(faceGeometry, &fgeom);CHKERRQ(ierr);
2494   ierr = VecRestoreArray(cellGeometry, &cgeom);CHKERRQ(ierr);
2495   /* Create storage for gradients */
2496   ierr = DMClone(dm, dmGrad);CHKERRQ(ierr);
2497   ierr = PetscSectionCreate(PetscObjectComm((PetscObject) dm), &sectionGrad);CHKERRQ(ierr);
2498   ierr = PetscSectionSetChart(sectionGrad, cStart, cEnd);CHKERRQ(ierr);
2499   for (c = cStart; c < cEnd; ++c) {ierr = PetscSectionSetDof(sectionGrad, c, pdim*dim);CHKERRQ(ierr);}
2500   ierr = PetscSectionSetUp(sectionGrad);CHKERRQ(ierr);
2501   ierr = DMSetLocalSection(*dmGrad, sectionGrad);CHKERRQ(ierr);
2502   ierr = PetscSectionDestroy(&sectionGrad);CHKERRQ(ierr);
2503   PetscFunctionReturn(0);
2504 }
2505 
2506 /*@
2507   DMPlexGetDataFVM - Retrieve precomputed cell geometry
2508 
2509   Collective on dm
2510 
2511   Input Arguments:
2512 + dm  - The DM
2513 - fvm - The PetscFV
2514 
2515   Output Parameters:
2516 + cellGeometry - The cell geometry
2517 . faceGeometry - The face geometry
2518 - dmGrad       - The gradient matrices
2519 
2520   Level: developer
2521 
2522 .seealso: DMPlexComputeGeometryFVM()
2523 @*/
2524 PetscErrorCode DMPlexGetDataFVM(DM dm, PetscFV fv, Vec *cellgeom, Vec *facegeom, DM *gradDM)
2525 {
2526   PetscObject    cellgeomobj, facegeomobj;
2527   PetscErrorCode ierr;
2528 
2529   PetscFunctionBegin;
2530   ierr = PetscObjectQuery((PetscObject) dm, "DMPlex_cellgeom_fvm", &cellgeomobj);CHKERRQ(ierr);
2531   if (!cellgeomobj) {
2532     Vec cellgeomInt, facegeomInt;
2533 
2534     ierr = DMPlexComputeGeometryFVM(dm, &cellgeomInt, &facegeomInt);CHKERRQ(ierr);
2535     ierr = PetscObjectCompose((PetscObject) dm, "DMPlex_cellgeom_fvm",(PetscObject)cellgeomInt);CHKERRQ(ierr);
2536     ierr = PetscObjectCompose((PetscObject) dm, "DMPlex_facegeom_fvm",(PetscObject)facegeomInt);CHKERRQ(ierr);
2537     ierr = VecDestroy(&cellgeomInt);CHKERRQ(ierr);
2538     ierr = VecDestroy(&facegeomInt);CHKERRQ(ierr);
2539     ierr = PetscObjectQuery((PetscObject) dm, "DMPlex_cellgeom_fvm", &cellgeomobj);CHKERRQ(ierr);
2540   }
2541   ierr = PetscObjectQuery((PetscObject) dm, "DMPlex_facegeom_fvm", &facegeomobj);CHKERRQ(ierr);
2542   if (cellgeom) *cellgeom = (Vec) cellgeomobj;
2543   if (facegeom) *facegeom = (Vec) facegeomobj;
2544   if (gradDM) {
2545     PetscObject gradobj;
2546     PetscBool   computeGradients;
2547 
2548     ierr = PetscFVGetComputeGradients(fv,&computeGradients);CHKERRQ(ierr);
2549     if (!computeGradients) {
2550       *gradDM = NULL;
2551       PetscFunctionReturn(0);
2552     }
2553     ierr = PetscObjectQuery((PetscObject) dm, "DMPlex_dmgrad_fvm", &gradobj);CHKERRQ(ierr);
2554     if (!gradobj) {
2555       DM dmGradInt;
2556 
2557       ierr = DMPlexComputeGradientFVM(dm,fv,(Vec) facegeomobj,(Vec) cellgeomobj,&dmGradInt);CHKERRQ(ierr);
2558       ierr = PetscObjectCompose((PetscObject) dm, "DMPlex_dmgrad_fvm", (PetscObject)dmGradInt);CHKERRQ(ierr);
2559       ierr = DMDestroy(&dmGradInt);CHKERRQ(ierr);
2560       ierr = PetscObjectQuery((PetscObject) dm, "DMPlex_dmgrad_fvm", &gradobj);CHKERRQ(ierr);
2561     }
2562     *gradDM = (DM) gradobj;
2563   }
2564   PetscFunctionReturn(0);
2565 }
2566 
2567 static PetscErrorCode DMPlexCoordinatesToReference_NewtonUpdate(PetscInt dimC, PetscInt dimR, PetscScalar *J, PetscScalar *invJ, PetscScalar *work,  PetscReal *resNeg, PetscReal *guess)
2568 {
2569   PetscInt l, m;
2570 
2571   PetscFunctionBeginHot;
2572   if (dimC == dimR && dimR <= 3) {
2573     /* invert Jacobian, multiply */
2574     PetscScalar det, idet;
2575 
2576     switch (dimR) {
2577     case 1:
2578       invJ[0] = 1./ J[0];
2579       break;
2580     case 2:
2581       det = J[0] * J[3] - J[1] * J[2];
2582       idet = 1./det;
2583       invJ[0] =  J[3] * idet;
2584       invJ[1] = -J[1] * idet;
2585       invJ[2] = -J[2] * idet;
2586       invJ[3] =  J[0] * idet;
2587       break;
2588     case 3:
2589       {
2590         invJ[0] = J[4] * J[8] - J[5] * J[7];
2591         invJ[1] = J[2] * J[7] - J[1] * J[8];
2592         invJ[2] = J[1] * J[5] - J[2] * J[4];
2593         det = invJ[0] * J[0] + invJ[1] * J[3] + invJ[2] * J[6];
2594         idet = 1./det;
2595         invJ[0] *= idet;
2596         invJ[1] *= idet;
2597         invJ[2] *= idet;
2598         invJ[3]  = idet * (J[5] * J[6] - J[3] * J[8]);
2599         invJ[4]  = idet * (J[0] * J[8] - J[2] * J[6]);
2600         invJ[5]  = idet * (J[2] * J[3] - J[0] * J[5]);
2601         invJ[6]  = idet * (J[3] * J[7] - J[4] * J[6]);
2602         invJ[7]  = idet * (J[1] * J[6] - J[0] * J[7]);
2603         invJ[8]  = idet * (J[0] * J[4] - J[1] * J[3]);
2604       }
2605       break;
2606     }
2607     for (l = 0; l < dimR; l++) {
2608       for (m = 0; m < dimC; m++) {
2609         guess[l] += PetscRealPart(invJ[l * dimC + m]) * resNeg[m];
2610       }
2611     }
2612   } else {
2613 #if defined(PETSC_USE_COMPLEX)
2614     char transpose = 'C';
2615 #else
2616     char transpose = 'T';
2617 #endif
2618     PetscBLASInt m = dimR;
2619     PetscBLASInt n = dimC;
2620     PetscBLASInt one = 1;
2621     PetscBLASInt worksize = dimR * dimC, info;
2622 
2623     for (l = 0; l < dimC; l++) {invJ[l] = resNeg[l];}
2624 
2625     PetscStackCallBLAS("LAPACKgels",LAPACKgels_(&transpose,&m,&n,&one,J,&m,invJ,&n,work,&worksize, &info));
2626     if (info != 0) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Bad argument to GELS");
2627 
2628     for (l = 0; l < dimR; l++) {guess[l] += PetscRealPart(invJ[l]);}
2629   }
2630   PetscFunctionReturn(0);
2631 }
2632 
2633 static PetscErrorCode DMPlexCoordinatesToReference_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[], Vec coords, PetscInt dimC, PetscInt dimR)
2634 {
2635   PetscInt       coordSize, i, j, k, l, m, maxIts = 7, numV = (1 << dimR);
2636   PetscScalar    *coordsScalar = NULL;
2637   PetscReal      *cellData, *cellCoords, *cellCoeffs, *extJ, *resNeg;
2638   PetscScalar    *J, *invJ, *work;
2639   PetscErrorCode ierr;
2640 
2641   PetscFunctionBegin;
2642   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
2643   ierr = DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar);CHKERRQ(ierr);
2644   if (coordSize < dimC * numV) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Expecting at least %D coordinates, got %D",dimC * (1 << dimR), coordSize);
2645   ierr = DMGetWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData);CHKERRQ(ierr);
2646   ierr = DMGetWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J);CHKERRQ(ierr);
2647   cellCoords = &cellData[0];
2648   cellCoeffs = &cellData[coordSize];
2649   extJ       = &cellData[2 * coordSize];
2650   resNeg     = &cellData[2 * coordSize + dimR];
2651   invJ       = &J[dimR * dimC];
2652   work       = &J[2 * dimR * dimC];
2653   if (dimR == 2) {
2654     const PetscInt zToPlex[4] = {0, 1, 3, 2};
2655 
2656     for (i = 0; i < 4; i++) {
2657       PetscInt plexI = zToPlex[i];
2658 
2659       for (j = 0; j < dimC; j++) {
2660         cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
2661       }
2662     }
2663   } else if (dimR == 3) {
2664     const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
2665 
2666     for (i = 0; i < 8; i++) {
2667       PetscInt plexI = zToPlex[i];
2668 
2669       for (j = 0; j < dimC; j++) {
2670         cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
2671       }
2672     }
2673   } else {
2674     for (i = 0; i < coordSize; i++) {cellCoords[i] = PetscRealPart(coordsScalar[i]);}
2675   }
2676   /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */
2677   for (i = 0; i < dimR; i++) {
2678     PetscReal *swap;
2679 
2680     for (j = 0; j < (numV / 2); j++) {
2681       for (k = 0; k < dimC; k++) {
2682         cellCoeffs[dimC * j + k]                = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]);
2683         cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]);
2684       }
2685     }
2686 
2687     if (i < dimR - 1) {
2688       swap = cellCoeffs;
2689       cellCoeffs = cellCoords;
2690       cellCoords = swap;
2691     }
2692   }
2693   ierr = PetscArrayzero(refCoords,numPoints * dimR);CHKERRQ(ierr);
2694   for (j = 0; j < numPoints; j++) {
2695     for (i = 0; i < maxIts; i++) {
2696       PetscReal *guess = &refCoords[dimR * j];
2697 
2698       /* compute -residual and Jacobian */
2699       for (k = 0; k < dimC; k++) {resNeg[k] = realCoords[dimC * j + k];}
2700       for (k = 0; k < dimC * dimR; k++) {J[k] = 0.;}
2701       for (k = 0; k < numV; k++) {
2702         PetscReal extCoord = 1.;
2703         for (l = 0; l < dimR; l++) {
2704           PetscReal coord = guess[l];
2705           PetscInt  dep   = (k & (1 << l)) >> l;
2706 
2707           extCoord *= dep * coord + !dep;
2708           extJ[l] = dep;
2709 
2710           for (m = 0; m < dimR; m++) {
2711             PetscReal coord = guess[m];
2712             PetscInt  dep   = ((k & (1 << m)) >> m) && (m != l);
2713             PetscReal mult  = dep * coord + !dep;
2714 
2715             extJ[l] *= mult;
2716           }
2717         }
2718         for (l = 0; l < dimC; l++) {
2719           PetscReal coeff = cellCoeffs[dimC * k + l];
2720 
2721           resNeg[l] -= coeff * extCoord;
2722           for (m = 0; m < dimR; m++) {
2723             J[dimR * l + m] += coeff * extJ[m];
2724           }
2725         }
2726       }
2727       if (0 && PetscDefined(USE_DEBUG)) {
2728         PetscReal maxAbs = 0.;
2729 
2730         for (l = 0; l < dimC; l++) {
2731           maxAbs = PetscMax(maxAbs,PetscAbsReal(resNeg[l]));
2732         }
2733         ierr = PetscInfo4(dm,"cell %D, point %D, iter %D: res %g\n",cell,j,i,(double) maxAbs);CHKERRQ(ierr);
2734       }
2735 
2736       ierr = DMPlexCoordinatesToReference_NewtonUpdate(dimC,dimR,J,invJ,work,resNeg,guess);CHKERRQ(ierr);
2737     }
2738   }
2739   ierr = DMRestoreWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J);CHKERRQ(ierr);
2740   ierr = DMRestoreWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData);CHKERRQ(ierr);
2741   ierr = DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar);CHKERRQ(ierr);
2742   PetscFunctionReturn(0);
2743 }
2744 
2745 static PetscErrorCode DMPlexReferenceToCoordinates_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt dimC, PetscInt dimR)
2746 {
2747   PetscInt       coordSize, i, j, k, l, numV = (1 << dimR);
2748   PetscScalar    *coordsScalar = NULL;
2749   PetscReal      *cellData, *cellCoords, *cellCoeffs;
2750   PetscErrorCode ierr;
2751 
2752   PetscFunctionBegin;
2753   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
2754   ierr = DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar);CHKERRQ(ierr);
2755   if (coordSize < dimC * numV) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Expecting at least %D coordinates, got %D",dimC * (1 << dimR), coordSize);
2756   ierr = DMGetWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData);CHKERRQ(ierr);
2757   cellCoords = &cellData[0];
2758   cellCoeffs = &cellData[coordSize];
2759   if (dimR == 2) {
2760     const PetscInt zToPlex[4] = {0, 1, 3, 2};
2761 
2762     for (i = 0; i < 4; i++) {
2763       PetscInt plexI = zToPlex[i];
2764 
2765       for (j = 0; j < dimC; j++) {
2766         cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
2767       }
2768     }
2769   } else if (dimR == 3) {
2770     const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
2771 
2772     for (i = 0; i < 8; i++) {
2773       PetscInt plexI = zToPlex[i];
2774 
2775       for (j = 0; j < dimC; j++) {
2776         cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
2777       }
2778     }
2779   } else {
2780     for (i = 0; i < coordSize; i++) {cellCoords[i] = PetscRealPart(coordsScalar[i]);}
2781   }
2782   /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */
2783   for (i = 0; i < dimR; i++) {
2784     PetscReal *swap;
2785 
2786     for (j = 0; j < (numV / 2); j++) {
2787       for (k = 0; k < dimC; k++) {
2788         cellCoeffs[dimC * j + k]                = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]);
2789         cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]);
2790       }
2791     }
2792 
2793     if (i < dimR - 1) {
2794       swap = cellCoeffs;
2795       cellCoeffs = cellCoords;
2796       cellCoords = swap;
2797     }
2798   }
2799   ierr = PetscArrayzero(realCoords,numPoints * dimC);CHKERRQ(ierr);
2800   for (j = 0; j < numPoints; j++) {
2801     const PetscReal *guess  = &refCoords[dimR * j];
2802     PetscReal       *mapped = &realCoords[dimC * j];
2803 
2804     for (k = 0; k < numV; k++) {
2805       PetscReal extCoord = 1.;
2806       for (l = 0; l < dimR; l++) {
2807         PetscReal coord = guess[l];
2808         PetscInt  dep   = (k & (1 << l)) >> l;
2809 
2810         extCoord *= dep * coord + !dep;
2811       }
2812       for (l = 0; l < dimC; l++) {
2813         PetscReal coeff = cellCoeffs[dimC * k + l];
2814 
2815         mapped[l] += coeff * extCoord;
2816       }
2817     }
2818   }
2819   ierr = DMRestoreWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData);CHKERRQ(ierr);
2820   ierr = DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar);CHKERRQ(ierr);
2821   PetscFunctionReturn(0);
2822 }
2823 
2824 /* TODO: TOBY please fix this for Nc > 1 */
2825 static PetscErrorCode DMPlexCoordinatesToReference_FE(DM dm, PetscFE fe, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[], Vec coords, PetscInt Nc, PetscInt dimR)
2826 {
2827   PetscInt       numComp, pdim, i, j, k, l, m, maxIter = 7, coordSize;
2828   PetscScalar    *nodes = NULL;
2829   PetscReal      *invV, *modes;
2830   PetscReal      *B, *D, *resNeg;
2831   PetscScalar    *J, *invJ, *work;
2832   PetscErrorCode ierr;
2833 
2834   PetscFunctionBegin;
2835   ierr = PetscFEGetDimension(fe, &pdim);CHKERRQ(ierr);
2836   ierr = PetscFEGetNumComponents(fe, &numComp);CHKERRQ(ierr);
2837   if (numComp != Nc) SETERRQ2(PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"coordinate discretization must have as many components (%D) as embedding dimension (!= %D)",numComp,Nc);
2838   ierr = DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &nodes);CHKERRQ(ierr);
2839   /* convert nodes to values in the stable evaluation basis */
2840   ierr = DMGetWorkArray(dm,pdim,MPIU_REAL,&modes);CHKERRQ(ierr);
2841   invV = fe->invV;
2842   for (i = 0; i < pdim; ++i) {
2843     modes[i] = 0.;
2844     for (j = 0; j < pdim; ++j) {
2845       modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]);
2846     }
2847   }
2848   ierr   = DMGetWorkArray(dm,pdim * Nc + pdim * Nc * dimR + Nc,MPIU_REAL,&B);CHKERRQ(ierr);
2849   D      = &B[pdim*Nc];
2850   resNeg = &D[pdim*Nc * dimR];
2851   ierr = DMGetWorkArray(dm,3 * Nc * dimR,MPIU_SCALAR,&J);CHKERRQ(ierr);
2852   invJ = &J[Nc * dimR];
2853   work = &invJ[Nc * dimR];
2854   for (i = 0; i < numPoints * dimR; i++) {refCoords[i] = 0.;}
2855   for (j = 0; j < numPoints; j++) {
2856       for (i = 0; i < maxIter; i++) { /* we could batch this so that we're not making big B and D arrays all the time */
2857       PetscReal *guess = &refCoords[j * dimR];
2858       ierr = PetscSpaceEvaluate(fe->basisSpace, 1, guess, B, D, NULL);CHKERRQ(ierr);
2859       for (k = 0; k < Nc; k++) {resNeg[k] = realCoords[j * Nc + k];}
2860       for (k = 0; k < Nc * dimR; k++) {J[k] = 0.;}
2861       for (k = 0; k < pdim; k++) {
2862         for (l = 0; l < Nc; l++) {
2863           resNeg[l] -= modes[k] * B[k * Nc + l];
2864           for (m = 0; m < dimR; m++) {
2865             J[l * dimR + m] += modes[k] * D[(k * Nc + l) * dimR + m];
2866           }
2867         }
2868       }
2869       if (0 && PetscDefined(USE_DEBUG)) {
2870         PetscReal maxAbs = 0.;
2871 
2872         for (l = 0; l < Nc; l++) {
2873           maxAbs = PetscMax(maxAbs,PetscAbsReal(resNeg[l]));
2874         }
2875         ierr = PetscInfo4(dm,"cell %D, point %D, iter %D: res %g\n",cell,j,i,(double) maxAbs);CHKERRQ(ierr);
2876       }
2877       ierr = DMPlexCoordinatesToReference_NewtonUpdate(Nc,dimR,J,invJ,work,resNeg,guess);CHKERRQ(ierr);
2878     }
2879   }
2880   ierr = DMRestoreWorkArray(dm,3 * Nc * dimR,MPIU_SCALAR,&J);CHKERRQ(ierr);
2881   ierr = DMRestoreWorkArray(dm,pdim * Nc + pdim * Nc * dimR + Nc,MPIU_REAL,&B);CHKERRQ(ierr);
2882   ierr = DMRestoreWorkArray(dm,pdim,MPIU_REAL,&modes);CHKERRQ(ierr);
2883   ierr = DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes);CHKERRQ(ierr);
2884   PetscFunctionReturn(0);
2885 }
2886 
2887 /* TODO: TOBY please fix this for Nc > 1 */
2888 static PetscErrorCode DMPlexReferenceToCoordinates_FE(DM dm, PetscFE fe, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt Nc, PetscInt dimR)
2889 {
2890   PetscInt       numComp, pdim, i, j, k, l, coordSize;
2891   PetscScalar    *nodes = NULL;
2892   PetscReal      *invV, *modes;
2893   PetscReal      *B;
2894   PetscErrorCode ierr;
2895 
2896   PetscFunctionBegin;
2897   ierr = PetscFEGetDimension(fe, &pdim);CHKERRQ(ierr);
2898   ierr = PetscFEGetNumComponents(fe, &numComp);CHKERRQ(ierr);
2899   if (numComp != Nc) SETERRQ2(PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"coordinate discretization must have as many components (%D) as embedding dimension (!= %D)",numComp,Nc);
2900   ierr = DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &nodes);CHKERRQ(ierr);
2901   /* convert nodes to values in the stable evaluation basis */
2902   ierr = DMGetWorkArray(dm,pdim,MPIU_REAL,&modes);CHKERRQ(ierr);
2903   invV = fe->invV;
2904   for (i = 0; i < pdim; ++i) {
2905     modes[i] = 0.;
2906     for (j = 0; j < pdim; ++j) {
2907       modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]);
2908     }
2909   }
2910   ierr = DMGetWorkArray(dm,numPoints * pdim * Nc,MPIU_REAL,&B);CHKERRQ(ierr);
2911   ierr = PetscSpaceEvaluate(fe->basisSpace, numPoints, refCoords, B, NULL, NULL);CHKERRQ(ierr);
2912   for (i = 0; i < numPoints * Nc; i++) {realCoords[i] = 0.;}
2913   for (j = 0; j < numPoints; j++) {
2914     PetscReal *mapped = &realCoords[j * Nc];
2915 
2916     for (k = 0; k < pdim; k++) {
2917       for (l = 0; l < Nc; l++) {
2918         mapped[l] += modes[k] * B[(j * pdim + k) * Nc + l];
2919       }
2920     }
2921   }
2922   ierr = DMRestoreWorkArray(dm,numPoints * pdim * Nc,MPIU_REAL,&B);CHKERRQ(ierr);
2923   ierr = DMRestoreWorkArray(dm,pdim,MPIU_REAL,&modes);CHKERRQ(ierr);
2924   ierr = DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes);CHKERRQ(ierr);
2925   PetscFunctionReturn(0);
2926 }
2927 
2928 /*@
2929   DMPlexCoordinatesToReference - Pull coordinates back from the mesh to the reference element using a single element
2930   map.  This inversion will be accurate inside the reference element, but may be inaccurate for mappings that do not
2931   extend uniquely outside the reference cell (e.g, most non-affine maps)
2932 
2933   Not collective
2934 
2935   Input Parameters:
2936 + dm         - The mesh, with coordinate maps defined either by a PetscDS for the coordinate DM (see DMGetCoordinateDM()) or
2937                implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or
2938                as a multilinear map for tensor-product elements
2939 . cell       - the cell whose map is used.
2940 . numPoints  - the number of points to locate
2941 - realCoords - (numPoints x coordinate dimension) array of coordinates (see DMGetCoordinateDim())
2942 
2943   Output Parameters:
2944 . refCoords  - (numPoints x dimension) array of reference coordinates (see DMGetDimension())
2945 
2946   Level: intermediate
2947 
2948 .seealso: DMPlexReferenceToCoordinates()
2949 @*/
2950 PetscErrorCode DMPlexCoordinatesToReference(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[])
2951 {
2952   PetscInt       dimC, dimR, depth, cStart, cEnd, i;
2953   DM             coordDM = NULL;
2954   Vec            coords;
2955   PetscFE        fe = NULL;
2956   PetscErrorCode ierr;
2957 
2958   PetscFunctionBegin;
2959   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
2960   ierr = DMGetDimension(dm,&dimR);CHKERRQ(ierr);
2961   ierr = DMGetCoordinateDim(dm,&dimC);CHKERRQ(ierr);
2962   if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(0);
2963   ierr = DMPlexGetDepth(dm,&depth);CHKERRQ(ierr);
2964   ierr = DMGetCoordinatesLocal(dm,&coords);CHKERRQ(ierr);
2965   ierr = DMGetCoordinateDM(dm,&coordDM);CHKERRQ(ierr);
2966   if (coordDM) {
2967     PetscInt coordFields;
2968 
2969     ierr = DMGetNumFields(coordDM,&coordFields);CHKERRQ(ierr);
2970     if (coordFields) {
2971       PetscClassId id;
2972       PetscObject  disc;
2973 
2974       ierr = DMGetField(coordDM,0,NULL,&disc);CHKERRQ(ierr);
2975       ierr = PetscObjectGetClassId(disc,&id);CHKERRQ(ierr);
2976       if (id == PETSCFE_CLASSID) {
2977         fe = (PetscFE) disc;
2978       }
2979     }
2980   }
2981   ierr = DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
2982   if (cell < cStart || cell >= cEnd) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"point %D not in cell range [%D,%D)",cell,cStart,cEnd);
2983   if (!fe) { /* implicit discretization: affine or multilinear */
2984     PetscInt  coneSize;
2985     PetscBool isSimplex, isTensor;
2986 
2987     ierr = DMPlexGetConeSize(dm,cell,&coneSize);CHKERRQ(ierr);
2988     isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE;
2989     isTensor  = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE;
2990     if (isSimplex) {
2991       PetscReal detJ, *v0, *J, *invJ;
2992 
2993       ierr = DMGetWorkArray(dm,dimC + 2 * dimC * dimC, MPIU_REAL, &v0);CHKERRQ(ierr);
2994       J    = &v0[dimC];
2995       invJ = &J[dimC * dimC];
2996       ierr = DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, invJ, &detJ);CHKERRQ(ierr);
2997       for (i = 0; i < numPoints; i++) { /* Apply the inverse affine transformation for each point */
2998         const PetscReal x0[3] = {-1.,-1.,-1.};
2999 
3000         CoordinatesRealToRef(dimC, dimR, x0, v0, invJ, &realCoords[dimC * i], &refCoords[dimR * i]);
3001       }
3002       ierr = DMRestoreWorkArray(dm,dimC + 2 * dimC * dimC, MPIU_REAL, &v0);CHKERRQ(ierr);
3003     } else if (isTensor) {
3004       ierr = DMPlexCoordinatesToReference_Tensor(coordDM, cell, numPoints, realCoords, refCoords, coords, dimC, dimR);CHKERRQ(ierr);
3005     } else SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_SUP,"Unrecognized cone size %D",coneSize);
3006   } else {
3007     ierr = DMPlexCoordinatesToReference_FE(coordDM, fe, cell, numPoints, realCoords, refCoords, coords, dimC, dimR);CHKERRQ(ierr);
3008   }
3009   PetscFunctionReturn(0);
3010 }
3011 
3012 /*@
3013   DMPlexReferenceToCoordinates - Map references coordinates to coordinates in the the mesh for a single element map.
3014 
3015   Not collective
3016 
3017   Input Parameters:
3018 + dm         - The mesh, with coordinate maps defined either by a PetscDS for the coordinate DM (see DMGetCoordinateDM()) or
3019                implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or
3020                as a multilinear map for tensor-product elements
3021 . cell       - the cell whose map is used.
3022 . numPoints  - the number of points to locate
3023 - refCoords  - (numPoints x dimension) array of reference coordinates (see DMGetDimension())
3024 
3025   Output Parameters:
3026 . realCoords - (numPoints x coordinate dimension) array of coordinates (see DMGetCoordinateDim())
3027 
3028    Level: intermediate
3029 
3030 .seealso: DMPlexCoordinatesToReference()
3031 @*/
3032 PetscErrorCode DMPlexReferenceToCoordinates(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[])
3033 {
3034   PetscInt       dimC, dimR, depth, cStart, cEnd, i;
3035   DM             coordDM = NULL;
3036   Vec            coords;
3037   PetscFE        fe = NULL;
3038   PetscErrorCode ierr;
3039 
3040   PetscFunctionBegin;
3041   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
3042   ierr = DMGetDimension(dm,&dimR);CHKERRQ(ierr);
3043   ierr = DMGetCoordinateDim(dm,&dimC);CHKERRQ(ierr);
3044   if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(0);
3045   ierr = DMPlexGetDepth(dm,&depth);CHKERRQ(ierr);
3046   ierr = DMGetCoordinatesLocal(dm,&coords);CHKERRQ(ierr);
3047   ierr = DMGetCoordinateDM(dm,&coordDM);CHKERRQ(ierr);
3048   if (coordDM) {
3049     PetscInt coordFields;
3050 
3051     ierr = DMGetNumFields(coordDM,&coordFields);CHKERRQ(ierr);
3052     if (coordFields) {
3053       PetscClassId id;
3054       PetscObject  disc;
3055 
3056       ierr = DMGetField(coordDM,0,NULL,&disc);CHKERRQ(ierr);
3057       ierr = PetscObjectGetClassId(disc,&id);CHKERRQ(ierr);
3058       if (id == PETSCFE_CLASSID) {
3059         fe = (PetscFE) disc;
3060       }
3061     }
3062   }
3063   ierr = DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
3064   if (cell < cStart || cell >= cEnd) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"point %D not in cell range [%D,%D)",cell,cStart,cEnd);
3065   if (!fe) { /* implicit discretization: affine or multilinear */
3066     PetscInt  coneSize;
3067     PetscBool isSimplex, isTensor;
3068 
3069     ierr = DMPlexGetConeSize(dm,cell,&coneSize);CHKERRQ(ierr);
3070     isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE;
3071     isTensor  = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE;
3072     if (isSimplex) {
3073       PetscReal detJ, *v0, *J;
3074 
3075       ierr = DMGetWorkArray(dm,dimC + 2 * dimC * dimC, MPIU_REAL, &v0);CHKERRQ(ierr);
3076       J    = &v0[dimC];
3077       ierr = DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, NULL, &detJ);CHKERRQ(ierr);
3078       for (i = 0; i < numPoints; i++) { /* Apply the affine transformation for each point */
3079         const PetscReal xi0[3] = {-1.,-1.,-1.};
3080 
3081         CoordinatesRefToReal(dimC, dimR, xi0, v0, J, &refCoords[dimR * i], &realCoords[dimC * i]);
3082       }
3083       ierr = DMRestoreWorkArray(dm,dimC + 2 * dimC * dimC, MPIU_REAL, &v0);CHKERRQ(ierr);
3084     } else if (isTensor) {
3085       ierr = DMPlexReferenceToCoordinates_Tensor(coordDM, cell, numPoints, refCoords, realCoords, coords, dimC, dimR);CHKERRQ(ierr);
3086     } else SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_SUP,"Unrecognized cone size %D",coneSize);
3087   } else {
3088     ierr = DMPlexReferenceToCoordinates_FE(coordDM, fe, cell, numPoints, refCoords, realCoords, coords, dimC, dimR);CHKERRQ(ierr);
3089   }
3090   PetscFunctionReturn(0);
3091 }
3092 
3093 /*@C
3094   DMPlexRemapGeometry - This function maps the original DM coordinates to new coordinates.
3095 
3096   Not collective
3097 
3098   Input Parameters:
3099 + dm      - The DM
3100 . time    - The time
3101 - func    - The function transforming current coordinates to new coordaintes
3102 
3103    Calling sequence of func:
3104 $    func(PetscInt dim, PetscInt Nf, PetscInt NfAux,
3105 $         const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
3106 $         const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
3107 $         PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f[]);
3108 
3109 +  dim          - The spatial dimension
3110 .  Nf           - The number of input fields (here 1)
3111 .  NfAux        - The number of input auxiliary fields
3112 .  uOff         - The offset of the coordinates in u[] (here 0)
3113 .  uOff_x       - The offset of the coordinates in u_x[] (here 0)
3114 .  u            - The coordinate values at this point in space
3115 .  u_t          - The coordinate time derivative at this point in space (here NULL)
3116 .  u_x          - The coordinate derivatives at this point in space
3117 .  aOff         - The offset of each auxiliary field in u[]
3118 .  aOff_x       - The offset of each auxiliary field in u_x[]
3119 .  a            - The auxiliary field values at this point in space
3120 .  a_t          - The auxiliary field time derivative at this point in space (or NULL)
3121 .  a_x          - The auxiliary field derivatives at this point in space
3122 .  t            - The current time
3123 .  x            - The coordinates of this point (here not used)
3124 .  numConstants - The number of constants
3125 .  constants    - The value of each constant
3126 -  f            - The new coordinates at this point in space
3127 
3128   Level: intermediate
3129 
3130 .seealso: DMGetCoordinates(), DMGetCoordinatesLocal(), DMGetCoordinateDM(), DMProjectFieldLocal(), DMProjectFieldLabelLocal()
3131 @*/
3132 PetscErrorCode DMPlexRemapGeometry(DM dm, PetscReal time,
3133                                    void (*func)(PetscInt, PetscInt, PetscInt,
3134                                                 const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
3135                                                 const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
3136                                                 PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]))
3137 {
3138   DM             cdm;
3139   DMField        cf;
3140   Vec            lCoords, tmpCoords;
3141   PetscErrorCode ierr;
3142 
3143   PetscFunctionBegin;
3144   ierr = DMGetCoordinateDM(dm, &cdm);CHKERRQ(ierr);
3145   ierr = DMGetCoordinatesLocal(dm, &lCoords);CHKERRQ(ierr);
3146   ierr = DMGetLocalVector(cdm, &tmpCoords);CHKERRQ(ierr);
3147   ierr = VecCopy(lCoords, tmpCoords);CHKERRQ(ierr);
3148   /* We have to do the coordinate field manually right now since the coordinate DM will not have its own */
3149   ierr = DMGetCoordinateField(dm, &cf);CHKERRQ(ierr);
3150   cdm->coordinateField = cf;
3151   ierr = DMProjectFieldLocal(cdm, time, tmpCoords, &func, INSERT_VALUES, lCoords);CHKERRQ(ierr);
3152   cdm->coordinateField = NULL;
3153   ierr = DMRestoreLocalVector(cdm, &tmpCoords);CHKERRQ(ierr);
3154   ierr = DMSetCoordinatesLocal(dm, lCoords);CHKERRQ(ierr);
3155   PetscFunctionReturn(0);
3156 }
3157 
3158 /* Shear applies the transformation, assuming we fix z,
3159   / 1  0  m_0 \
3160   | 0  1  m_1 |
3161   \ 0  0   1  /
3162 */
3163 static void f0_shear(PetscInt dim, PetscInt Nf, PetscInt NfAux,
3164                      const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
3165                      const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
3166                      PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar coords[])
3167 {
3168   const PetscInt Nc = uOff[1]-uOff[0];
3169   const PetscInt ax = (PetscInt) PetscRealPart(constants[0]);
3170   PetscInt       c;
3171 
3172   for (c = 0; c < Nc; ++c) {
3173     coords[c] = u[c] + constants[c+1]*u[ax];
3174   }
3175 }
3176 
3177 /*@C
3178   DMPlexShearGeometry - This shears the domain, meaning adds a multiple of the shear coordinate to all other coordinates.
3179 
3180   Not collective
3181 
3182   Input Parameters:
3183 + dm          - The DM
3184 . direction   - The shear coordinate direction, e.g. 0 is the x-axis
3185 - multipliers - The multiplier m for each direction which is not the shear direction
3186 
3187   Level: intermediate
3188 
3189 .seealso: DMPlexRemapGeometry()
3190 @*/
3191 PetscErrorCode DMPlexShearGeometry(DM dm, DMDirection direction, PetscReal multipliers[])
3192 {
3193   DM             cdm;
3194   PetscDS        cds;
3195   PetscObject    obj;
3196   PetscClassId   id;
3197   PetscScalar   *moduli;
3198   const PetscInt dir = (PetscInt) direction;
3199   PetscInt       dE, d, e;
3200   PetscErrorCode ierr;
3201 
3202   PetscFunctionBegin;
3203   ierr = DMGetCoordinateDM(dm, &cdm);CHKERRQ(ierr);
3204   ierr = DMGetCoordinateDim(dm, &dE);CHKERRQ(ierr);
3205   ierr = PetscMalloc1(dE+1, &moduli);CHKERRQ(ierr);
3206   moduli[0] = dir;
3207   for (d = 0, e = 0; d < dE; ++d) moduli[d+1] = d == dir ? 0.0 : (multipliers ? multipliers[e++] : 1.0);
3208   ierr = DMGetDS(cdm, &cds);CHKERRQ(ierr);
3209   ierr = PetscDSGetDiscretization(cds, 0, &obj);CHKERRQ(ierr);
3210   ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3211   if (id != PETSCFE_CLASSID) {
3212     Vec           lCoords;
3213     PetscSection  cSection;
3214     PetscScalar  *coords;
3215     PetscInt      vStart, vEnd, v;
3216 
3217     ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
3218     ierr = DMGetCoordinateSection(dm, &cSection);CHKERRQ(ierr);
3219     ierr = DMGetCoordinatesLocal(dm, &lCoords);CHKERRQ(ierr);
3220     ierr = VecGetArray(lCoords, &coords);CHKERRQ(ierr);
3221     for (v = vStart; v < vEnd; ++v) {
3222       PetscReal ds;
3223       PetscInt  off, c;
3224 
3225       ierr = PetscSectionGetOffset(cSection, v, &off);CHKERRQ(ierr);
3226       ds   = PetscRealPart(coords[off+dir]);
3227       for (c = 0; c < dE; ++c) coords[off+c] += moduli[c]*ds;
3228     }
3229     ierr = VecRestoreArray(lCoords, &coords);CHKERRQ(ierr);
3230   } else {
3231     ierr = PetscDSSetConstants(cds, dE+1, moduli);CHKERRQ(ierr);
3232     ierr = DMPlexRemapGeometry(dm, 0.0, f0_shear);CHKERRQ(ierr);
3233   }
3234   ierr = PetscFree(moduli);CHKERRQ(ierr);
3235   PetscFunctionReturn(0);
3236 }
3237