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