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