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