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