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