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