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