Actual source code: plexgeometry.c
1: #include <petsc/private/dmpleximpl.h>
2: #include <petsc/private/petscfeimpl.h>
3: #include <petscblaslapack.h>
4: #include <petsctime.h>
6: /*@
7: DMPlexFindVertices - Try to find DAG points based on their coordinates.
9: Not Collective (provided DMGetCoordinatesLocalSetUp() has been called already)
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
16: Output Parameters:
17: . points - The IS of found DAG points or -1
19: Level: intermediate
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.
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.
28: The output IS must be destroyed by user.
30: The tolerance is interpreted as the maximum Euclidean (L2) distance of the sought point from the specified coordinates.
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.
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;
45: PetscFunctionBegin;
46: if (eps < 0) eps = PETSC_SQRT_MACHINE_EPSILON;
47: PetscCall(DMGetCoordinateDim(dm, &cdim));
48: {
49: PetscInt n;
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;
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;
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(PETSC_SUCCESS);
104: }
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);
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;
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(PETSC_SUCCESS);
138: }
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) */
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;
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(PETSC_SUCCESS);
199: }
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;
208: PetscFunctionBegin;
209: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, &v0, &J, &invJ, &detJ));
210: xi = invJ * (x - v0);
212: if ((xi >= -eps) && (xi <= 2. + eps)) *cell = c;
213: else *cell = DMLOCATEPOINT_POINT_NOT_FOUND;
214: PetscFunctionReturn(PETSC_SUCCESS);
215: }
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;
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]);
231: if ((xi >= -eps) && (eta >= -eps) && (xi + eta <= 2.0 + eps)) *cell = c;
232: else *cell = DMLOCATEPOINT_POINT_NOT_FOUND;
233: PetscFunctionReturn(PETSC_SUCCESS);
234: }
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;
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]);
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(PETSC_SUCCESS);
259: }
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;
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(PETSC_SUCCESS);
289: }
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;
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]);
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(PETSC_SUCCESS);
310: }
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;
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;
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];
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(PETSC_SUCCESS);
357: }
359: static PetscErrorCode PetscGridHashInitialize_Internal(PetscGridHash box, PetscInt dim, const PetscScalar point[])
360: {
361: PetscInt d;
363: PetscFunctionBegin;
364: box->dim = dim;
365: for (d = 0; d < dim; ++d) box->lower[d] = box->upper[d] = PetscRealPart(point[d]);
366: PetscFunctionReturn(PETSC_SUCCESS);
367: }
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(PETSC_SUCCESS);
375: }
377: PetscErrorCode PetscGridHashEnlarge(PetscGridHash box, const PetscScalar point[])
378: {
379: PetscInt d;
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(PETSC_SUCCESS);
387: }
389: /*
390: PetscGridHashSetGrid - Divide the grid into boxes
392: Not collective
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
399: Level: developer
401: .seealso: `PetscGridHashCreate()`
402: */
403: PetscErrorCode PetscGridHashSetGrid(PetscGridHash box, const PetscInt n[], const PetscReal h[])
404: {
405: PetscInt d;
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(PETSC_SUCCESS);
419: }
421: /*
422: PetscGridHashGetEnclosingBox - Find the grid boxes containing each input point
424: Not collective
426: Input Parameters:
427: + box - The grid hash object
428: . numPoints - The number of input points
429: - points - The input point coordinates
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
435: Level: developer
437: Note:
438: This only guarantees that a box contains a point, not that a cell does.
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;
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]);
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(PETSC_SUCCESS);
465: }
467: /*
468: PetscGridHashGetEnclosingBoxQuery - Find the grid boxes containing each input point
470: Not collective
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
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
483: Level: developer
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.
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;
499: PetscFunctionBegin;
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]);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
515: }
516: *found = PETSC_TRUE;
517: PetscFunctionReturn(PETSC_SUCCESS);
518: }
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(PETSC_SUCCESS);
530: }
532: PetscErrorCode DMPlexLocatePoint_Internal(DM dm, PetscInt dim, const PetscScalar point[], PetscInt cellStart, PetscInt *cell)
533: {
534: DMPolytopeType ct;
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(PETSC_SUCCESS);
558: }
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;
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(PETSC_SUCCESS);
585: }
587: /*
588: DMPlexComputeGridHash_Internal - Create a grid hash structure covering the Plex
590: Collective on dm
592: Input Parameter:
593: . dm - The Plex
595: Output Parameter:
596: . localBox - The grid hash object
598: Level: developer
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: PetscSF sf;
608: Vec coordinates;
609: PetscSection coordSection;
610: Vec coordsLocal;
611: const PetscScalar *coords;
612: PetscScalar *edgeCoords;
613: PetscInt *dboxes, *boxes;
614: const PetscInt *leaves;
615: PetscInt n[3] = {2, 2, 2};
616: PetscInt dim, N, Nl = 0, maxConeSize, cStart, cEnd, c, eStart, eEnd, i;
617: PetscBool flg;
619: PetscFunctionBegin;
620: PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
621: PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
622: PetscCall(DMGetCoordinateDim(dm, &dim));
623: PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, NULL));
624: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
625: PetscCall(VecGetLocalSize(coordinates, &N));
626: PetscCall(VecGetArrayRead(coordinates, &coords));
627: PetscCall(PetscGridHashCreate(comm, dim, coords, &lbox));
628: for (i = 0; i < N; i += dim) PetscCall(PetscGridHashEnlarge(lbox, &coords[i]));
629: PetscCall(VecRestoreArrayRead(coordinates, &coords));
630: c = dim;
631: PetscCall(PetscOptionsGetIntArray(NULL, ((PetscObject)dm)->prefix, "-dm_plex_hash_box_faces", n, &c, &flg));
632: if (flg) {
633: for (i = c; i < dim; ++i) n[i] = n[c - 1];
634: } else {
635: for (i = 0; i < dim; ++i) n[i] = PetscMax(2, PetscFloorReal(PetscPowReal((PetscReal)(cEnd - cStart), 1.0 / dim) * 0.8));
636: }
637: PetscCall(PetscGridHashSetGrid(lbox, n, NULL));
638: if (debug)
639: 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],
640: (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]));
641: #if 0
642: /* Could define a custom reduction to merge these */
643: PetscCall(MPIU_Allreduce(lbox->lower, gbox->lower, 3, MPIU_REAL, MPI_MIN, comm));
644: PetscCall(MPIU_Allreduce(lbox->upper, gbox->upper, 3, MPIU_REAL, MPI_MAX, comm));
645: #endif
646: /* Is there a reason to snap the local bounding box to a division of the global box? */
647: /* Should we compute all overlaps of local boxes? We could do this with a rendevouz scheme partitioning the global box */
648: /* Create label */
649: PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
650: if (dim < 2) eStart = eEnd = -1;
651: PetscCall(DMLabelCreate(PETSC_COMM_SELF, "cells", &lbox->cellsSparse));
652: PetscCall(DMLabelCreateIndex(lbox->cellsSparse, cStart, cEnd));
653: /* Compute boxes which overlap each cell: https://stackoverflow.com/questions/13790208/triangle-square-intersection-test-in-2d */
654: PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
655: PetscCall(DMGetCoordinateSection(dm, &coordSection));
656: PetscCall(DMGetPointSF(dm, &sf));
657: if (sf) PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
658: Nl = PetscMax(Nl, 0);
659: PetscCall(PetscCalloc3(16 * dim, &dboxes, 16, &boxes, PetscPowInt(maxConeSize, dim) * dim, &edgeCoords));
660: for (c = cStart; c < cEnd; ++c) {
661: const PetscReal *h = lbox->h;
662: PetscScalar *ccoords = NULL;
663: PetscInt csize = 0;
664: PetscInt *closure = NULL;
665: PetscInt Ncl, cl, Ne = 0, idx;
666: PetscScalar point[3];
667: PetscInt dlim[6], d, e, i, j, k;
669: PetscCall(PetscFindInt(c, Nl, leaves, &idx));
670: if (idx >= 0) continue;
671: /* Get all edges in cell */
672: PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &Ncl, &closure));
673: for (cl = 0; cl < Ncl * 2; ++cl) {
674: if ((closure[cl] >= eStart) && (closure[cl] < eEnd)) {
675: PetscScalar *ecoords = &edgeCoords[Ne * dim * 2];
676: PetscInt ecsize = dim * 2;
678: PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, closure[cl], &ecsize, &ecoords));
679: PetscCheck(ecsize == dim * 2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Got %" PetscInt_FMT " coords for edge, instead of %" PetscInt_FMT, ecsize, dim * 2);
680: ++Ne;
681: }
682: }
683: PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &Ncl, &closure));
684: /* Find boxes enclosing each vertex */
685: PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &csize, &ccoords));
686: PetscCall(PetscGridHashGetEnclosingBox(lbox, csize / dim, ccoords, dboxes, boxes));
687: /* Mark cells containing the vertices */
688: for (e = 0; e < csize / dim; ++e) {
689: if (debug)
690: 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));
691: PetscCall(DMLabelSetValue(lbox->cellsSparse, c, boxes[e]));
692: }
693: /* Get grid of boxes containing these */
694: for (d = 0; d < dim; ++d) dlim[d * 2 + 0] = dlim[d * 2 + 1] = dboxes[d];
695: for (d = dim; d < 3; ++d) dlim[d * 2 + 0] = dlim[d * 2 + 1] = 0;
696: for (e = 1; e < dim + 1; ++e) {
697: for (d = 0; d < dim; ++d) {
698: dlim[d * 2 + 0] = PetscMin(dlim[d * 2 + 0], dboxes[e * dim + d]);
699: dlim[d * 2 + 1] = PetscMax(dlim[d * 2 + 1], dboxes[e * dim + d]);
700: }
701: }
702: /* Check for intersection of box with cell */
703: for (k = dlim[2 * 2 + 0], point[2] = lbox->lower[2] + k * h[2]; k <= dlim[2 * 2 + 1]; ++k, point[2] += h[2]) {
704: for (j = dlim[1 * 2 + 0], point[1] = lbox->lower[1] + j * h[1]; j <= dlim[1 * 2 + 1]; ++j, point[1] += h[1]) {
705: for (i = dlim[0 * 2 + 0], point[0] = lbox->lower[0] + i * h[0]; i <= dlim[0 * 2 + 1]; ++i, point[0] += h[0]) {
706: const PetscInt box = (k * lbox->n[1] + j) * lbox->n[0] + i;
707: PetscScalar cpoint[3];
708: PetscInt cell, edge, ii, jj, kk;
710: if (debug)
711: 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])));
712: /* Check whether cell contains any vertex of this subbox TODO vectorize this */
713: for (kk = 0, cpoint[2] = point[2]; kk < (dim > 2 ? 2 : 1); ++kk, cpoint[2] += h[2]) {
714: for (jj = 0, cpoint[1] = point[1]; jj < (dim > 1 ? 2 : 1); ++jj, cpoint[1] += h[1]) {
715: for (ii = 0, cpoint[0] = point[0]; ii < 2; ++ii, cpoint[0] += h[0]) {
716: PetscCall(DMPlexLocatePoint_Internal(dm, dim, cpoint, c, &cell));
717: if (cell >= 0) {
718: if (debug)
719: 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));
720: PetscCall(DMLabelSetValue(lbox->cellsSparse, c, box));
721: jj = kk = 2;
722: break;
723: }
724: }
725: }
726: }
727: /* Check whether cell edge intersects any face of these subboxes TODO vectorize this */
728: for (edge = 0; edge < Ne; ++edge) {
729: PetscReal segA[6] = {0., 0., 0., 0., 0., 0.};
730: PetscReal segB[6] = {0., 0., 0., 0., 0., 0.};
731: PetscReal segC[6] = {0., 0., 0., 0., 0., 0.};
733: PetscCheck(dim <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected dim %" PetscInt_FMT " > 3", dim);
734: for (d = 0; d < dim * 2; ++d) segA[d] = PetscRealPart(edgeCoords[edge * dim * 2 + d]);
735: /* 1D: (x) -- (x+h) 0 -- 1
736: 2D: (x, y) -- (x, y+h) (0, 0) -- (0, 1)
737: (x+h, y) -- (x+h, y+h) (1, 0) -- (1, 1)
738: (x, y) -- (x+h, y) (0, 0) -- (1, 0)
739: (x, y+h) -- (x+h, y+h) (0, 1) -- (1, 1)
740: 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)
741: (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)
742: (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)
743: (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)
744: (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)
745: (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)
746: */
747: /* Loop over faces with normal in direction d */
748: for (d = 0; d < dim; ++d) {
749: PetscBool intersects = PETSC_FALSE;
750: PetscInt e = (d + 1) % dim;
751: PetscInt f = (d + 2) % dim;
753: /* There are two faces in each dimension */
754: for (ii = 0; ii < 2; ++ii) {
755: segB[d] = PetscRealPart(point[d] + ii * h[d]);
756: segB[dim + d] = PetscRealPart(point[d] + ii * h[d]);
757: segC[d] = PetscRealPart(point[d] + ii * h[d]);
758: segC[dim + d] = PetscRealPart(point[d] + ii * h[d]);
759: if (dim > 1) {
760: segB[e] = PetscRealPart(point[e] + 0 * h[e]);
761: segB[dim + e] = PetscRealPart(point[e] + 1 * h[e]);
762: segC[e] = PetscRealPart(point[e] + 0 * h[e]);
763: segC[dim + e] = PetscRealPart(point[e] + 0 * h[e]);
764: }
765: if (dim > 2) {
766: segB[f] = PetscRealPart(point[f] + 0 * h[f]);
767: segB[dim + f] = PetscRealPart(point[f] + 0 * h[f]);
768: segC[f] = PetscRealPart(point[f] + 0 * h[f]);
769: segC[dim + f] = PetscRealPart(point[f] + 1 * h[f]);
770: }
771: if (dim == 2) {
772: PetscCall(DMPlexGetLineIntersection_2D_Internal(segA, segB, NULL, &intersects));
773: } else if (dim == 3) {
774: PetscCall(DMPlexGetLinePlaneIntersection_3D_Internal(segA, segB, segC, NULL, &intersects));
775: }
776: if (intersects) {
777: if (debug)
778: 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]));
779: PetscCall(DMLabelSetValue(lbox->cellsSparse, c, box));
780: edge = Ne;
781: break;
782: }
783: }
784: }
785: }
786: }
787: }
788: }
789: PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, NULL, &ccoords));
790: }
791: PetscCall(PetscFree3(dboxes, boxes, edgeCoords));
792: if (debug) PetscCall(DMLabelView(lbox->cellsSparse, PETSC_VIEWER_STDOUT_SELF));
793: PetscCall(DMLabelConvertToSection(lbox->cellsSparse, &lbox->cellSection, &lbox->cells));
794: PetscCall(DMLabelDestroy(&lbox->cellsSparse));
795: *localBox = lbox;
796: PetscFunctionReturn(PETSC_SUCCESS);
797: }
799: PetscErrorCode DMLocatePoints_Plex(DM dm, Vec v, DMPointLocationType ltype, PetscSF cellSF)
800: {
801: PetscInt debug = ((DM_Plex *)dm->data)->printLocate;
802: DM_Plex *mesh = (DM_Plex *)dm->data;
803: PetscBool hash = mesh->useHashLocation, reuse = PETSC_FALSE;
804: PetscInt bs, numPoints, p, numFound, *found = NULL;
805: PetscInt dim, Nl = 0, cStart, cEnd, numCells, c, d;
806: PetscSF sf;
807: const PetscInt *leaves;
808: const PetscInt *boxCells;
809: PetscSFNode *cells;
810: PetscScalar *a;
811: PetscMPIInt result;
812: PetscLogDouble t0, t1;
813: PetscReal gmin[3], gmax[3];
814: PetscInt terminating_query_type[] = {0, 0, 0};
816: PetscFunctionBegin;
817: PetscCall(PetscLogEventBegin(DMPLEX_LocatePoints, 0, 0, 0, 0));
818: PetscCall(PetscTime(&t0));
819: 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.");
820: PetscCall(DMGetCoordinateDim(dm, &dim));
821: PetscCall(VecGetBlockSize(v, &bs));
822: PetscCallMPI(MPI_Comm_compare(PetscObjectComm((PetscObject)cellSF), PETSC_COMM_SELF, &result));
823: PetscCheck(result == MPI_IDENT || result == MPI_CONGRUENT, PetscObjectComm((PetscObject)cellSF), PETSC_ERR_SUP, "Trying parallel point location: only local point location supported");
824: 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);
825: PetscCall(DMGetCoordinatesLocalSetUp(dm));
826: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
827: PetscCall(DMGetPointSF(dm, &sf));
828: if (sf) PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
829: Nl = PetscMax(Nl, 0);
830: PetscCall(VecGetLocalSize(v, &numPoints));
831: PetscCall(VecGetArray(v, &a));
832: numPoints /= bs;
833: {
834: const PetscSFNode *sf_cells;
836: PetscCall(PetscSFGetGraph(cellSF, NULL, NULL, NULL, &sf_cells));
837: if (sf_cells) {
838: PetscCall(PetscInfo(dm, "[DMLocatePoints_Plex] Re-using existing StarForest node list\n"));
839: cells = (PetscSFNode *)sf_cells;
840: reuse = PETSC_TRUE;
841: } else {
842: PetscCall(PetscInfo(dm, "[DMLocatePoints_Plex] Creating and initializing new StarForest node list\n"));
843: PetscCall(PetscMalloc1(numPoints, &cells));
844: /* initialize cells if created */
845: for (p = 0; p < numPoints; p++) {
846: cells[p].rank = 0;
847: cells[p].index = DMLOCATEPOINT_POINT_NOT_FOUND;
848: }
849: }
850: }
851: PetscCall(DMGetBoundingBox(dm, gmin, gmax));
852: if (hash) {
853: if (!mesh->lbox) {
854: PetscCall(PetscInfo(dm, "Initializing grid hashing\n"));
855: PetscCall(DMPlexComputeGridHash_Internal(dm, &mesh->lbox));
856: }
857: /* Designate the local box for each point */
858: /* Send points to correct process */
859: /* Search cells that lie in each subbox */
860: /* Should we bin points before doing search? */
861: PetscCall(ISGetIndices(mesh->lbox->cells, &boxCells));
862: }
863: for (p = 0, numFound = 0; p < numPoints; ++p) {
864: const PetscScalar *point = &a[p * bs];
865: PetscInt dbin[3] = {-1, -1, -1}, bin, cell = -1, cellOffset;
866: PetscBool point_outside_domain = PETSC_FALSE;
868: /* check bounding box of domain */
869: for (d = 0; d < dim; d++) {
870: if (PetscRealPart(point[d]) < gmin[d]) {
871: point_outside_domain = PETSC_TRUE;
872: break;
873: }
874: if (PetscRealPart(point[d]) > gmax[d]) {
875: point_outside_domain = PETSC_TRUE;
876: break;
877: }
878: }
879: if (point_outside_domain) {
880: cells[p].rank = 0;
881: cells[p].index = DMLOCATEPOINT_POINT_NOT_FOUND;
882: terminating_query_type[0]++;
883: continue;
884: }
886: /* check initial values in cells[].index - abort early if found */
887: if (cells[p].index != DMLOCATEPOINT_POINT_NOT_FOUND) {
888: c = cells[p].index;
889: cells[p].index = DMLOCATEPOINT_POINT_NOT_FOUND;
890: PetscCall(DMPlexLocatePoint_Internal(dm, dim, point, c, &cell));
891: if (cell >= 0) {
892: cells[p].rank = 0;
893: cells[p].index = cell;
894: numFound++;
895: }
896: }
897: if (cells[p].index != DMLOCATEPOINT_POINT_NOT_FOUND) {
898: terminating_query_type[1]++;
899: continue;
900: }
902: if (hash) {
903: PetscBool found_box;
905: 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])));
906: /* allow for case that point is outside box - abort early */
907: PetscCall(PetscGridHashGetEnclosingBoxQuery(mesh->lbox, mesh->lbox->cellSection, 1, point, dbin, &bin, &found_box));
908: if (found_box) {
909: 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]));
910: /* TODO Lay an interface over this so we can switch between Section (dense) and Label (sparse) */
911: PetscCall(PetscSectionGetDof(mesh->lbox->cellSection, bin, &numCells));
912: PetscCall(PetscSectionGetOffset(mesh->lbox->cellSection, bin, &cellOffset));
913: for (c = cellOffset; c < cellOffset + numCells; ++c) {
914: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " Checking for point in cell %" PetscInt_FMT "\n", boxCells[c]));
915: PetscCall(DMPlexLocatePoint_Internal(dm, dim, point, boxCells[c], &cell));
916: if (cell >= 0) {
917: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " FOUND in cell %" PetscInt_FMT "\n", cell));
918: cells[p].rank = 0;
919: cells[p].index = cell;
920: numFound++;
921: terminating_query_type[2]++;
922: break;
923: }
924: }
925: }
926: } else {
927: for (c = cStart; c < cEnd; ++c) {
928: PetscInt idx;
930: PetscCall(PetscFindInt(c, Nl, leaves, &idx));
931: if (idx >= 0) continue;
932: PetscCall(DMPlexLocatePoint_Internal(dm, dim, point, c, &cell));
933: if (cell >= 0) {
934: cells[p].rank = 0;
935: cells[p].index = cell;
936: numFound++;
937: terminating_query_type[2]++;
938: break;
939: }
940: }
941: }
942: }
943: if (hash) PetscCall(ISRestoreIndices(mesh->lbox->cells, &boxCells));
944: if (ltype == DM_POINTLOCATION_NEAREST && hash && numFound < numPoints) {
945: for (p = 0; p < numPoints; p++) {
946: const PetscScalar *point = &a[p * bs];
947: PetscReal cpoint[3], diff[3], best[3] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MAX_REAL}, dist, distMax = PETSC_MAX_REAL;
948: PetscInt dbin[3] = {-1, -1, -1}, bin, cellOffset, d, bestc = -1;
950: if (cells[p].index < 0) {
951: PetscCall(PetscGridHashGetEnclosingBox(mesh->lbox, 1, point, dbin, &bin));
952: PetscCall(PetscSectionGetDof(mesh->lbox->cellSection, bin, &numCells));
953: PetscCall(PetscSectionGetOffset(mesh->lbox->cellSection, bin, &cellOffset));
954: for (c = cellOffset; c < cellOffset + numCells; ++c) {
955: PetscCall(DMPlexClosestPoint_Internal(dm, dim, point, boxCells[c], cpoint));
956: for (d = 0; d < dim; ++d) diff[d] = cpoint[d] - PetscRealPart(point[d]);
957: dist = DMPlex_NormD_Internal(dim, diff);
958: if (dist < distMax) {
959: for (d = 0; d < dim; ++d) best[d] = cpoint[d];
960: bestc = boxCells[c];
961: distMax = dist;
962: }
963: }
964: if (distMax < PETSC_MAX_REAL) {
965: ++numFound;
966: cells[p].rank = 0;
967: cells[p].index = bestc;
968: for (d = 0; d < dim; ++d) a[p * bs + d] = best[d];
969: }
970: }
971: }
972: }
973: /* This code is only be relevant when interfaced to parallel point location */
974: /* Check for highest numbered proc that claims a point (do we care?) */
975: if (ltype == DM_POINTLOCATION_REMOVE && numFound < numPoints) {
976: PetscCall(PetscMalloc1(numFound, &found));
977: for (p = 0, numFound = 0; p < numPoints; p++) {
978: if (cells[p].rank >= 0 && cells[p].index >= 0) {
979: if (numFound < p) cells[numFound] = cells[p];
980: found[numFound++] = p;
981: }
982: }
983: }
984: PetscCall(VecRestoreArray(v, &a));
985: if (!reuse) PetscCall(PetscSFSetGraph(cellSF, cEnd - cStart, numFound, found, PETSC_OWN_POINTER, cells, PETSC_OWN_POINTER));
986: PetscCall(PetscTime(&t1));
987: if (hash) {
988: 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]));
989: } else {
990: 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]));
991: }
992: 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))));
993: PetscCall(PetscLogEventEnd(DMPLEX_LocatePoints, 0, 0, 0, 0));
994: PetscFunctionReturn(PETSC_SUCCESS);
995: }
997: /*@C
998: DMPlexComputeProjection2Dto1D - Rewrite coordinates to be the 1D projection of the 2D coordinates
1000: Not collective
1002: Input/Output Parameter:
1003: . coords - The coordinates of a segment, on output the new y-coordinate, and 0 for x
1005: Output Parameter:
1006: . R - The rotation which accomplishes the projection
1008: Level: developer
1010: .seealso: `DMPlexComputeProjection3Dto1D()`, `DMPlexComputeProjection3Dto2D()`
1011: @*/
1012: PetscErrorCode DMPlexComputeProjection2Dto1D(PetscScalar coords[], PetscReal R[])
1013: {
1014: const PetscReal x = PetscRealPart(coords[2] - coords[0]);
1015: const PetscReal y = PetscRealPart(coords[3] - coords[1]);
1016: const PetscReal r = PetscSqrtReal(x * x + y * y), c = x / r, s = y / r;
1018: PetscFunctionBegin;
1019: R[0] = c;
1020: R[1] = -s;
1021: R[2] = s;
1022: R[3] = c;
1023: coords[0] = 0.0;
1024: coords[1] = r;
1025: PetscFunctionReturn(PETSC_SUCCESS);
1026: }
1028: /*@C
1029: DMPlexComputeProjection3Dto1D - Rewrite coordinates to be the 1D projection of the 3D coordinates
1031: Not collective
1033: Input/Output Parameter:
1034: . coords - The coordinates of a segment; on output, the new y-coordinate, and 0 for x and z
1036: Output Parameter:
1037: . R - The rotation which accomplishes the projection
1039: 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
1041: Level: developer
1043: .seealso: `DMPlexComputeProjection2Dto1D()`, `DMPlexComputeProjection3Dto2D()`
1044: @*/
1045: PetscErrorCode DMPlexComputeProjection3Dto1D(PetscScalar coords[], PetscReal R[])
1046: {
1047: PetscReal x = PetscRealPart(coords[3] - coords[0]);
1048: PetscReal y = PetscRealPart(coords[4] - coords[1]);
1049: PetscReal z = PetscRealPart(coords[5] - coords[2]);
1050: PetscReal r = PetscSqrtReal(x * x + y * y + z * z);
1051: PetscReal rinv = 1. / r;
1052: PetscFunctionBegin;
1054: x *= rinv;
1055: y *= rinv;
1056: z *= rinv;
1057: if (x > 0.) {
1058: PetscReal inv1pX = 1. / (1. + x);
1060: R[0] = x;
1061: R[1] = -y;
1062: R[2] = -z;
1063: R[3] = y;
1064: R[4] = 1. - y * y * inv1pX;
1065: R[5] = -y * z * inv1pX;
1066: R[6] = z;
1067: R[7] = -y * z * inv1pX;
1068: R[8] = 1. - z * z * inv1pX;
1069: } else {
1070: PetscReal inv1mX = 1. / (1. - x);
1072: R[0] = x;
1073: R[1] = z;
1074: R[2] = y;
1075: R[3] = y;
1076: R[4] = -y * z * inv1mX;
1077: R[5] = 1. - y * y * inv1mX;
1078: R[6] = z;
1079: R[7] = 1. - z * z * inv1mX;
1080: R[8] = -y * z * inv1mX;
1081: }
1082: coords[0] = 0.0;
1083: coords[1] = r;
1084: PetscFunctionReturn(PETSC_SUCCESS);
1085: }
1087: /*@
1088: DMPlexComputeProjection3Dto2D - Rewrite coordinates of 3 or more coplanar 3D points to a common 2D basis for the
1089: plane. The normal is defined by positive orientation of the first 3 points.
1091: Not collective
1093: Input Parameter:
1094: . coordSize - Length of coordinate array (3x number of points); must be at least 9 (3 points)
1096: Input/Output Parameter:
1097: . coords - The interlaced coordinates of each coplanar 3D point; on output the first
1098: 2*coordSize/3 entries contain interlaced 2D points, with the rest undefined
1100: Output Parameter:
1101: . 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.
1103: Level: developer
1105: .seealso: `DMPlexComputeProjection2Dto1D()`, `DMPlexComputeProjection3Dto1D()`
1106: @*/
1107: PetscErrorCode DMPlexComputeProjection3Dto2D(PetscInt coordSize, PetscScalar coords[], PetscReal R[])
1108: {
1109: PetscReal x1[3], x2[3], n[3], c[3], norm;
1110: const PetscInt dim = 3;
1111: PetscInt d, p;
1113: PetscFunctionBegin;
1114: /* 0) Calculate normal vector */
1115: for (d = 0; d < dim; ++d) {
1116: x1[d] = PetscRealPart(coords[1 * dim + d] - coords[0 * dim + d]);
1117: x2[d] = PetscRealPart(coords[2 * dim + d] - coords[0 * dim + d]);
1118: }
1119: // n = x1 \otimes x2
1120: n[0] = x1[1] * x2[2] - x1[2] * x2[1];
1121: n[1] = x1[2] * x2[0] - x1[0] * x2[2];
1122: n[2] = x1[0] * x2[1] - x1[1] * x2[0];
1123: norm = PetscSqrtReal(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
1124: for (d = 0; d < dim; d++) n[d] /= norm;
1125: norm = PetscSqrtReal(x1[0] * x1[0] + x1[1] * x1[1] + x1[2] * x1[2]);
1126: for (d = 0; d < dim; d++) x1[d] /= norm;
1127: // x2 = n \otimes x1
1128: x2[0] = n[1] * x1[2] - n[2] * x1[1];
1129: x2[1] = n[2] * x1[0] - n[0] * x1[2];
1130: x2[2] = n[0] * x1[1] - n[1] * x1[0];
1131: for (d = 0; d < dim; d++) {
1132: R[d * dim + 0] = x1[d];
1133: R[d * dim + 1] = x2[d];
1134: R[d * dim + 2] = n[d];
1135: c[d] = PetscRealPart(coords[0 * dim + d]);
1136: }
1137: for (p = 0; p < coordSize / dim; p++) {
1138: PetscReal y[3];
1139: for (d = 0; d < dim; d++) y[d] = PetscRealPart(coords[p * dim + d]) - c[d];
1140: 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];
1141: }
1142: PetscFunctionReturn(PETSC_SUCCESS);
1143: }
1145: PETSC_UNUSED static inline void Volume_Triangle_Internal(PetscReal *vol, PetscReal coords[])
1146: {
1147: /* Signed volume is 1/2 the determinant
1149: | 1 1 1 |
1150: | x0 x1 x2 |
1151: | y0 y1 y2 |
1153: but if x0,y0 is the origin, we have
1155: | x1 x2 |
1156: | y1 y2 |
1157: */
1158: const PetscReal x1 = coords[2] - coords[0], y1 = coords[3] - coords[1];
1159: const PetscReal x2 = coords[4] - coords[0], y2 = coords[5] - coords[1];
1160: PetscReal M[4], detM;
1161: M[0] = x1;
1162: M[1] = x2;
1163: M[2] = y1;
1164: M[3] = y2;
1165: DMPlex_Det2D_Internal(&detM, M);
1166: *vol = 0.5 * detM;
1167: (void)PetscLogFlops(5.0);
1168: }
1170: PETSC_UNUSED static inline void Volume_Tetrahedron_Internal(PetscReal *vol, PetscReal coords[])
1171: {
1172: /* Signed volume is 1/6th of the determinant
1174: | 1 1 1 1 |
1175: | x0 x1 x2 x3 |
1176: | y0 y1 y2 y3 |
1177: | z0 z1 z2 z3 |
1179: but if x0,y0,z0 is the origin, we have
1181: | x1 x2 x3 |
1182: | y1 y2 y3 |
1183: | z1 z2 z3 |
1184: */
1185: const PetscReal x1 = coords[3] - coords[0], y1 = coords[4] - coords[1], z1 = coords[5] - coords[2];
1186: const PetscReal x2 = coords[6] - coords[0], y2 = coords[7] - coords[1], z2 = coords[8] - coords[2];
1187: const PetscReal x3 = coords[9] - coords[0], y3 = coords[10] - coords[1], z3 = coords[11] - coords[2];
1188: const PetscReal onesixth = ((PetscReal)1. / (PetscReal)6.);
1189: PetscReal M[9], detM;
1190: M[0] = x1;
1191: M[1] = x2;
1192: M[2] = x3;
1193: M[3] = y1;
1194: M[4] = y2;
1195: M[5] = y3;
1196: M[6] = z1;
1197: M[7] = z2;
1198: M[8] = z3;
1199: DMPlex_Det3D_Internal(&detM, M);
1200: *vol = -onesixth * detM;
1201: (void)PetscLogFlops(10.0);
1202: }
1204: static inline void Volume_Tetrahedron_Origin_Internal(PetscReal *vol, PetscReal coords[])
1205: {
1206: const PetscReal onesixth = ((PetscReal)1. / (PetscReal)6.);
1207: DMPlex_Det3D_Internal(vol, coords);
1208: *vol *= -onesixth;
1209: }
1211: static PetscErrorCode DMPlexComputePointGeometry_Internal(DM dm, PetscInt e, PetscReal v0[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1212: {
1213: PetscSection coordSection;
1214: Vec coordinates;
1215: const PetscScalar *coords;
1216: PetscInt dim, d, off;
1218: PetscFunctionBegin;
1219: PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1220: PetscCall(DMGetCoordinateSection(dm, &coordSection));
1221: PetscCall(PetscSectionGetDof(coordSection, e, &dim));
1222: if (!dim) PetscFunctionReturn(PETSC_SUCCESS);
1223: PetscCall(PetscSectionGetOffset(coordSection, e, &off));
1224: PetscCall(VecGetArrayRead(coordinates, &coords));
1225: if (v0) {
1226: for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[off + d]);
1227: }
1228: PetscCall(VecRestoreArrayRead(coordinates, &coords));
1229: *detJ = 1.;
1230: if (J) {
1231: for (d = 0; d < dim * dim; d++) J[d] = 0.;
1232: for (d = 0; d < dim; d++) J[d * dim + d] = 1.;
1233: if (invJ) {
1234: for (d = 0; d < dim * dim; d++) invJ[d] = 0.;
1235: for (d = 0; d < dim; d++) invJ[d * dim + d] = 1.;
1236: }
1237: }
1238: PetscFunctionReturn(PETSC_SUCCESS);
1239: }
1241: /*@C
1242: DMPlexGetCellCoordinates - Get coordinates for a cell, taking into account periodicity
1244: Not collective
1246: Input Parameters:
1247: + dm - The DM
1248: - cell - The cell number
1250: Output Parameters:
1251: + isDG - Using cellwise coordinates
1252: . Nc - The number of coordinates
1253: . array - The coordinate array
1254: - coords - The cell coordinates
1256: Level: developer
1258: .seealso: DMPlexRestoreCellCoordinates(), DMGetCoordinatesLocal(), DMGetCellCoordinatesLocal()
1259: @*/
1260: PetscErrorCode DMPlexGetCellCoordinates(DM dm, PetscInt cell, PetscBool *isDG, PetscInt *Nc, const PetscScalar *array[], PetscScalar *coords[])
1261: {
1262: DM cdm;
1263: Vec coordinates;
1264: PetscSection cs;
1265: const PetscScalar *ccoords;
1266: PetscInt pStart, pEnd;
1268: PetscFunctionBeginHot;
1269: *isDG = PETSC_FALSE;
1270: *Nc = 0;
1271: *array = NULL;
1272: *coords = NULL;
1273: /* Check for cellwise coordinates */
1274: PetscCall(DMGetCellCoordinateSection(dm, &cs));
1275: if (!cs) goto cg;
1276: /* Check that the cell exists in the cellwise section */
1277: PetscCall(PetscSectionGetChart(cs, &pStart, &pEnd));
1278: if (cell < pStart || cell >= pEnd) goto cg;
1279: /* Check for cellwise coordinates for this cell */
1280: PetscCall(PetscSectionGetDof(cs, cell, Nc));
1281: if (!*Nc) goto cg;
1282: /* Check for cellwise coordinates */
1283: PetscCall(DMGetCellCoordinatesLocalNoncollective(dm, &coordinates));
1284: if (!coordinates) goto cg;
1285: /* Get cellwise coordinates */
1286: PetscCall(DMGetCellCoordinateDM(dm, &cdm));
1287: PetscCall(VecGetArrayRead(coordinates, array));
1288: PetscCall(DMPlexPointLocalRead(cdm, cell, *array, &ccoords));
1289: PetscCall(DMGetWorkArray(cdm, *Nc, MPIU_SCALAR, coords));
1290: PetscCall(PetscArraycpy(*coords, ccoords, *Nc));
1291: PetscCall(VecRestoreArrayRead(coordinates, array));
1292: *isDG = PETSC_TRUE;
1293: PetscFunctionReturn(PETSC_SUCCESS);
1294: cg:
1295: /* Use continuous coordinates */
1296: PetscCall(DMGetCoordinateDM(dm, &cdm));
1297: PetscCall(DMGetCoordinateSection(dm, &cs));
1298: PetscCall(DMGetCoordinatesLocalNoncollective(dm, &coordinates));
1299: PetscCall(DMPlexVecGetClosure(cdm, cs, coordinates, cell, Nc, coords));
1300: PetscFunctionReturn(PETSC_SUCCESS);
1301: }
1303: /*@C
1304: DMPlexRestoreCellCoordinates - Get coordinates for a cell, taking into account periodicity
1306: Not collective
1308: Input Parameters:
1309: + dm - The DM
1310: - cell - The cell number
1312: Output Parameters:
1313: + isDG - Using cellwise coordinates
1314: . Nc - The number of coordinates
1315: . array - The coordinate array
1316: - coords - The cell coordinates
1318: Level: developer
1320: .seealso: DMPlexGetCellCoordinates(), DMGetCoordinatesLocal(), DMGetCellCoordinatesLocal()
1321: @*/
1322: PetscErrorCode DMPlexRestoreCellCoordinates(DM dm, PetscInt cell, PetscBool *isDG, PetscInt *Nc, const PetscScalar *array[], PetscScalar *coords[])
1323: {
1324: DM cdm;
1325: PetscSection cs;
1326: Vec coordinates;
1328: PetscFunctionBeginHot;
1329: if (*isDG) {
1330: PetscCall(DMGetCellCoordinateDM(dm, &cdm));
1331: PetscCall(DMRestoreWorkArray(cdm, *Nc, MPIU_SCALAR, coords));
1332: } else {
1333: PetscCall(DMGetCoordinateDM(dm, &cdm));
1334: PetscCall(DMGetCoordinateSection(dm, &cs));
1335: PetscCall(DMGetCoordinatesLocalNoncollective(dm, &coordinates));
1336: PetscCall(DMPlexVecRestoreClosure(cdm, cs, coordinates, cell, Nc, (PetscScalar **)coords));
1337: }
1338: PetscFunctionReturn(PETSC_SUCCESS);
1339: }
1341: static PetscErrorCode DMPlexComputeLineGeometry_Internal(DM dm, PetscInt e, PetscReal v0[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1342: {
1343: const PetscScalar *array;
1344: PetscScalar *coords = NULL;
1345: PetscInt numCoords, d;
1346: PetscBool isDG;
1348: PetscFunctionBegin;
1349: PetscCall(DMPlexGetCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1350: PetscCheck(!invJ || J, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "In order to compute invJ, J must not be NULL");
1351: *detJ = 0.0;
1352: if (numCoords == 6) {
1353: const PetscInt dim = 3;
1354: PetscReal R[9], J0;
1356: if (v0) {
1357: for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);
1358: }
1359: PetscCall(DMPlexComputeProjection3Dto1D(coords, R));
1360: if (J) {
1361: J0 = 0.5 * PetscRealPart(coords[1]);
1362: J[0] = R[0] * J0;
1363: J[1] = R[1];
1364: J[2] = R[2];
1365: J[3] = R[3] * J0;
1366: J[4] = R[4];
1367: J[5] = R[5];
1368: J[6] = R[6] * J0;
1369: J[7] = R[7];
1370: J[8] = R[8];
1371: DMPlex_Det3D_Internal(detJ, J);
1372: if (invJ) DMPlex_Invert2D_Internal(invJ, J, *detJ);
1373: }
1374: } else if (numCoords == 4) {
1375: const PetscInt dim = 2;
1376: PetscReal R[4], J0;
1378: if (v0) {
1379: for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);
1380: }
1381: PetscCall(DMPlexComputeProjection2Dto1D(coords, R));
1382: if (J) {
1383: J0 = 0.5 * PetscRealPart(coords[1]);
1384: J[0] = R[0] * J0;
1385: J[1] = R[1];
1386: J[2] = R[2] * J0;
1387: J[3] = R[3];
1388: DMPlex_Det2D_Internal(detJ, J);
1389: if (invJ) DMPlex_Invert2D_Internal(invJ, J, *detJ);
1390: }
1391: } else if (numCoords == 2) {
1392: const PetscInt dim = 1;
1394: if (v0) {
1395: for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);
1396: }
1397: if (J) {
1398: J[0] = 0.5 * (PetscRealPart(coords[1]) - PetscRealPart(coords[0]));
1399: *detJ = J[0];
1400: PetscCall(PetscLogFlops(2.0));
1401: if (invJ) {
1402: invJ[0] = 1.0 / J[0];
1403: PetscCall(PetscLogFlops(1.0));
1404: }
1405: }
1406: } 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);
1407: PetscCall(DMPlexRestoreCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1408: PetscFunctionReturn(PETSC_SUCCESS);
1409: }
1411: static PetscErrorCode DMPlexComputeTriangleGeometry_Internal(DM dm, PetscInt e, PetscReal v0[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1412: {
1413: const PetscScalar *array;
1414: PetscScalar *coords = NULL;
1415: PetscInt numCoords, d;
1416: PetscBool isDG;
1418: PetscFunctionBegin;
1419: PetscCall(DMPlexGetCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1420: PetscCheck(!invJ || J, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "In order to compute invJ, J must not be NULL");
1421: *detJ = 0.0;
1422: if (numCoords == 9) {
1423: const PetscInt dim = 3;
1424: PetscReal R[9], J0[9] = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
1426: if (v0) {
1427: for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);
1428: }
1429: PetscCall(DMPlexComputeProjection3Dto2D(numCoords, coords, R));
1430: if (J) {
1431: const PetscInt pdim = 2;
1433: for (d = 0; d < pdim; d++) {
1434: for (PetscInt f = 0; f < pdim; f++) J0[d * dim + f] = 0.5 * (PetscRealPart(coords[(f + 1) * pdim + d]) - PetscRealPart(coords[0 * pdim + d]));
1435: }
1436: PetscCall(PetscLogFlops(8.0));
1437: DMPlex_Det3D_Internal(detJ, J0);
1438: for (d = 0; d < dim; d++) {
1439: for (PetscInt f = 0; f < dim; f++) {
1440: J[d * dim + f] = 0.0;
1441: for (PetscInt g = 0; g < dim; g++) J[d * dim + f] += R[d * dim + g] * J0[g * dim + f];
1442: }
1443: }
1444: PetscCall(PetscLogFlops(18.0));
1445: }
1446: if (invJ) DMPlex_Invert3D_Internal(invJ, J, *detJ);
1447: } else if (numCoords == 6) {
1448: const PetscInt dim = 2;
1450: if (v0) {
1451: for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);
1452: }
1453: if (J) {
1454: for (d = 0; d < dim; d++) {
1455: for (PetscInt f = 0; f < dim; f++) J[d * dim + f] = 0.5 * (PetscRealPart(coords[(f + 1) * dim + d]) - PetscRealPart(coords[0 * dim + d]));
1456: }
1457: PetscCall(PetscLogFlops(8.0));
1458: DMPlex_Det2D_Internal(detJ, J);
1459: }
1460: if (invJ) DMPlex_Invert2D_Internal(invJ, J, *detJ);
1461: } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of coordinates for this triangle is %" PetscInt_FMT " != 6 or 9", numCoords);
1462: PetscCall(DMPlexRestoreCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1463: PetscFunctionReturn(PETSC_SUCCESS);
1464: }
1466: static PetscErrorCode DMPlexComputeRectangleGeometry_Internal(DM dm, PetscInt e, PetscBool isTensor, PetscInt Nq, const PetscReal points[], PetscReal v[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1467: {
1468: const PetscScalar *array;
1469: PetscScalar *coords = NULL;
1470: PetscInt numCoords, d;
1471: PetscBool isDG;
1473: PetscFunctionBegin;
1474: PetscCall(DMPlexGetCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1475: PetscCheck(!invJ || J, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "In order to compute invJ, J must not be NULL");
1476: if (!Nq) {
1477: PetscInt vorder[4] = {0, 1, 2, 3};
1479: if (isTensor) {
1480: vorder[2] = 3;
1481: vorder[3] = 2;
1482: }
1483: *detJ = 0.0;
1484: if (numCoords == 12) {
1485: const PetscInt dim = 3;
1486: PetscReal R[9], J0[9] = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
1488: if (v) {
1489: for (d = 0; d < dim; d++) v[d] = PetscRealPart(coords[d]);
1490: }
1491: PetscCall(DMPlexComputeProjection3Dto2D(numCoords, coords, R));
1492: if (J) {
1493: const PetscInt pdim = 2;
1495: for (d = 0; d < pdim; d++) {
1496: J0[d * dim + 0] = 0.5 * (PetscRealPart(coords[vorder[1] * pdim + d]) - PetscRealPart(coords[vorder[0] * pdim + d]));
1497: J0[d * dim + 1] = 0.5 * (PetscRealPart(coords[vorder[2] * pdim + d]) - PetscRealPart(coords[vorder[1] * pdim + d]));
1498: }
1499: PetscCall(PetscLogFlops(8.0));
1500: DMPlex_Det3D_Internal(detJ, J0);
1501: for (d = 0; d < dim; d++) {
1502: for (PetscInt f = 0; f < dim; f++) {
1503: J[d * dim + f] = 0.0;
1504: for (PetscInt g = 0; g < dim; g++) J[d * dim + f] += R[d * dim + g] * J0[g * dim + f];
1505: }
1506: }
1507: PetscCall(PetscLogFlops(18.0));
1508: }
1509: if (invJ) DMPlex_Invert3D_Internal(invJ, J, *detJ);
1510: } else if (numCoords == 8) {
1511: const PetscInt dim = 2;
1513: if (v) {
1514: for (d = 0; d < dim; d++) v[d] = PetscRealPart(coords[d]);
1515: }
1516: if (J) {
1517: for (d = 0; d < dim; d++) {
1518: J[d * dim + 0] = 0.5 * (PetscRealPart(coords[vorder[1] * dim + d]) - PetscRealPart(coords[vorder[0] * dim + d]));
1519: J[d * dim + 1] = 0.5 * (PetscRealPart(coords[vorder[3] * dim + d]) - PetscRealPart(coords[vorder[0] * dim + d]));
1520: }
1521: PetscCall(PetscLogFlops(8.0));
1522: DMPlex_Det2D_Internal(detJ, J);
1523: }
1524: if (invJ) DMPlex_Invert2D_Internal(invJ, J, *detJ);
1525: } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of coordinates for this quadrilateral is %" PetscInt_FMT " != 8 or 12", numCoords);
1526: } else {
1527: const PetscInt Nv = 4;
1528: const PetscInt dimR = 2;
1529: PetscInt zToPlex[4] = {0, 1, 3, 2};
1530: PetscReal zOrder[12];
1531: PetscReal zCoeff[12];
1532: PetscInt i, j, k, l, dim;
1534: if (isTensor) {
1535: zToPlex[2] = 2;
1536: zToPlex[3] = 3;
1537: }
1538: if (numCoords == 12) {
1539: dim = 3;
1540: } else if (numCoords == 8) {
1541: dim = 2;
1542: } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of coordinates for this quadrilateral is %" PetscInt_FMT " != 8 or 12", numCoords);
1543: for (i = 0; i < Nv; i++) {
1544: PetscInt zi = zToPlex[i];
1546: for (j = 0; j < dim; j++) zOrder[dim * i + j] = PetscRealPart(coords[dim * zi + j]);
1547: }
1548: for (j = 0; j < dim; j++) {
1549: /* Nodal basis for evaluation at the vertices: (1 \mp xi) (1 \mp eta):
1550: \phi^0 = (1 - xi - eta + xi eta) --> 1 = 1/4 ( \phi^0 + \phi^1 + \phi^2 + \phi^3)
1551: \phi^1 = (1 + xi - eta - xi eta) --> xi = 1/4 (-\phi^0 + \phi^1 - \phi^2 + \phi^3)
1552: \phi^2 = (1 - xi + eta - xi eta) --> eta = 1/4 (-\phi^0 - \phi^1 + \phi^2 + \phi^3)
1553: \phi^3 = (1 + xi + eta + xi eta) --> xi eta = 1/4 ( \phi^0 - \phi^1 - \phi^2 + \phi^3)
1554: */
1555: zCoeff[dim * 0 + j] = 0.25 * (zOrder[dim * 0 + j] + zOrder[dim * 1 + j] + zOrder[dim * 2 + j] + zOrder[dim * 3 + j]);
1556: zCoeff[dim * 1 + j] = 0.25 * (-zOrder[dim * 0 + j] + zOrder[dim * 1 + j] - zOrder[dim * 2 + j] + zOrder[dim * 3 + j]);
1557: zCoeff[dim * 2 + j] = 0.25 * (-zOrder[dim * 0 + j] - zOrder[dim * 1 + j] + zOrder[dim * 2 + j] + zOrder[dim * 3 + j]);
1558: zCoeff[dim * 3 + j] = 0.25 * (zOrder[dim * 0 + j] - zOrder[dim * 1 + j] - zOrder[dim * 2 + j] + zOrder[dim * 3 + j]);
1559: }
1560: for (i = 0; i < Nq; i++) {
1561: PetscReal xi = points[dimR * i], eta = points[dimR * i + 1];
1563: if (v) {
1564: PetscReal extPoint[4];
1566: extPoint[0] = 1.;
1567: extPoint[1] = xi;
1568: extPoint[2] = eta;
1569: extPoint[3] = xi * eta;
1570: for (j = 0; j < dim; j++) {
1571: PetscReal val = 0.;
1573: for (k = 0; k < Nv; k++) val += extPoint[k] * zCoeff[dim * k + j];
1574: v[i * dim + j] = val;
1575: }
1576: }
1577: if (J) {
1578: PetscReal extJ[8];
1580: extJ[0] = 0.;
1581: extJ[1] = 0.;
1582: extJ[2] = 1.;
1583: extJ[3] = 0.;
1584: extJ[4] = 0.;
1585: extJ[5] = 1.;
1586: extJ[6] = eta;
1587: extJ[7] = xi;
1588: for (j = 0; j < dim; j++) {
1589: for (k = 0; k < dimR; k++) {
1590: PetscReal val = 0.;
1592: for (l = 0; l < Nv; l++) val += zCoeff[dim * l + j] * extJ[dimR * l + k];
1593: J[i * dim * dim + dim * j + k] = val;
1594: }
1595: }
1596: if (dim == 3) { /* put the cross product in the third component of the Jacobian */
1597: PetscReal x, y, z;
1598: PetscReal *iJ = &J[i * dim * dim];
1599: PetscReal norm;
1601: x = iJ[1 * dim + 0] * iJ[2 * dim + 1] - iJ[1 * dim + 1] * iJ[2 * dim + 0];
1602: y = iJ[0 * dim + 1] * iJ[2 * dim + 0] - iJ[0 * dim + 0] * iJ[2 * dim + 1];
1603: z = iJ[0 * dim + 0] * iJ[1 * dim + 1] - iJ[0 * dim + 1] * iJ[1 * dim + 0];
1604: norm = PetscSqrtReal(x * x + y * y + z * z);
1605: iJ[2] = x / norm;
1606: iJ[5] = y / norm;
1607: iJ[8] = z / norm;
1608: DMPlex_Det3D_Internal(&detJ[i], &J[i * dim * dim]);
1609: if (invJ) DMPlex_Invert3D_Internal(&invJ[i * dim * dim], &J[i * dim * dim], detJ[i]);
1610: } else {
1611: DMPlex_Det2D_Internal(&detJ[i], &J[i * dim * dim]);
1612: if (invJ) DMPlex_Invert2D_Internal(&invJ[i * dim * dim], &J[i * dim * dim], detJ[i]);
1613: }
1614: }
1615: }
1616: }
1617: PetscCall(DMPlexRestoreCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1618: PetscFunctionReturn(PETSC_SUCCESS);
1619: }
1621: static PetscErrorCode DMPlexComputeTetrahedronGeometry_Internal(DM dm, PetscInt e, PetscReal v0[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1622: {
1623: const PetscScalar *array;
1624: PetscScalar *coords = NULL;
1625: const PetscInt dim = 3;
1626: PetscInt numCoords, d;
1627: PetscBool isDG;
1629: PetscFunctionBegin;
1630: PetscCall(DMPlexGetCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1631: PetscCheck(!invJ || J, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "In order to compute invJ, J must not be NULL");
1632: *detJ = 0.0;
1633: if (v0) {
1634: for (d = 0; d < dim; d++) v0[d] = PetscRealPart(coords[d]);
1635: }
1636: if (J) {
1637: for (d = 0; d < dim; d++) {
1638: /* I orient with outward face normals */
1639: J[d * dim + 0] = 0.5 * (PetscRealPart(coords[2 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
1640: J[d * dim + 1] = 0.5 * (PetscRealPart(coords[1 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
1641: J[d * dim + 2] = 0.5 * (PetscRealPart(coords[3 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
1642: }
1643: PetscCall(PetscLogFlops(18.0));
1644: DMPlex_Det3D_Internal(detJ, J);
1645: }
1646: if (invJ) DMPlex_Invert3D_Internal(invJ, J, *detJ);
1647: PetscCall(DMPlexRestoreCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1648: PetscFunctionReturn(PETSC_SUCCESS);
1649: }
1651: static PetscErrorCode DMPlexComputeHexahedronGeometry_Internal(DM dm, PetscInt e, PetscInt Nq, const PetscReal points[], PetscReal v[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1652: {
1653: const PetscScalar *array;
1654: PetscScalar *coords = NULL;
1655: const PetscInt dim = 3;
1656: PetscInt numCoords, d;
1657: PetscBool isDG;
1659: PetscFunctionBegin;
1660: PetscCall(DMPlexGetCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1661: PetscCheck(!invJ || J, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "In order to compute invJ, J must not be NULL");
1662: if (!Nq) {
1663: *detJ = 0.0;
1664: if (v) {
1665: for (d = 0; d < dim; d++) v[d] = PetscRealPart(coords[d]);
1666: }
1667: if (J) {
1668: for (d = 0; d < dim; d++) {
1669: J[d * dim + 0] = 0.5 * (PetscRealPart(coords[3 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
1670: J[d * dim + 1] = 0.5 * (PetscRealPart(coords[1 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
1671: J[d * dim + 2] = 0.5 * (PetscRealPart(coords[4 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
1672: }
1673: PetscCall(PetscLogFlops(18.0));
1674: DMPlex_Det3D_Internal(detJ, J);
1675: }
1676: if (invJ) DMPlex_Invert3D_Internal(invJ, J, *detJ);
1677: } else {
1678: const PetscInt Nv = 8;
1679: const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
1680: const PetscInt dim = 3;
1681: const PetscInt dimR = 3;
1682: PetscReal zOrder[24];
1683: PetscReal zCoeff[24];
1684: PetscInt i, j, k, l;
1686: for (i = 0; i < Nv; i++) {
1687: PetscInt zi = zToPlex[i];
1689: for (j = 0; j < dim; j++) zOrder[dim * i + j] = PetscRealPart(coords[dim * zi + j]);
1690: }
1691: for (j = 0; j < dim; j++) {
1692: 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]);
1693: 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]);
1694: 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]);
1695: 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]);
1696: 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]);
1697: 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]);
1698: 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]);
1699: 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]);
1700: }
1701: for (i = 0; i < Nq; i++) {
1702: PetscReal xi = points[dimR * i], eta = points[dimR * i + 1], theta = points[dimR * i + 2];
1704: if (v) {
1705: PetscReal extPoint[8];
1707: extPoint[0] = 1.;
1708: extPoint[1] = xi;
1709: extPoint[2] = eta;
1710: extPoint[3] = xi * eta;
1711: extPoint[4] = theta;
1712: extPoint[5] = theta * xi;
1713: extPoint[6] = theta * eta;
1714: extPoint[7] = theta * eta * xi;
1715: for (j = 0; j < dim; j++) {
1716: PetscReal val = 0.;
1718: for (k = 0; k < Nv; k++) val += extPoint[k] * zCoeff[dim * k + j];
1719: v[i * dim + j] = val;
1720: }
1721: }
1722: if (J) {
1723: PetscReal extJ[24];
1725: extJ[0] = 0.;
1726: extJ[1] = 0.;
1727: extJ[2] = 0.;
1728: extJ[3] = 1.;
1729: extJ[4] = 0.;
1730: extJ[5] = 0.;
1731: extJ[6] = 0.;
1732: extJ[7] = 1.;
1733: extJ[8] = 0.;
1734: extJ[9] = eta;
1735: extJ[10] = xi;
1736: extJ[11] = 0.;
1737: extJ[12] = 0.;
1738: extJ[13] = 0.;
1739: extJ[14] = 1.;
1740: extJ[15] = theta;
1741: extJ[16] = 0.;
1742: extJ[17] = xi;
1743: extJ[18] = 0.;
1744: extJ[19] = theta;
1745: extJ[20] = eta;
1746: extJ[21] = theta * eta;
1747: extJ[22] = theta * xi;
1748: extJ[23] = eta * xi;
1750: for (j = 0; j < dim; j++) {
1751: for (k = 0; k < dimR; k++) {
1752: PetscReal val = 0.;
1754: for (l = 0; l < Nv; l++) val += zCoeff[dim * l + j] * extJ[dimR * l + k];
1755: J[i * dim * dim + dim * j + k] = val;
1756: }
1757: }
1758: DMPlex_Det3D_Internal(&detJ[i], &J[i * dim * dim]);
1759: if (invJ) DMPlex_Invert3D_Internal(&invJ[i * dim * dim], &J[i * dim * dim], detJ[i]);
1760: }
1761: }
1762: }
1763: PetscCall(DMPlexRestoreCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1764: PetscFunctionReturn(PETSC_SUCCESS);
1765: }
1767: static PetscErrorCode DMPlexComputeTriangularPrismGeometry_Internal(DM dm, PetscInt e, PetscInt Nq, const PetscReal points[], PetscReal v[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
1768: {
1769: const PetscScalar *array;
1770: PetscScalar *coords = NULL;
1771: const PetscInt dim = 3;
1772: PetscInt numCoords, d;
1773: PetscBool isDG;
1775: PetscFunctionBegin;
1776: PetscCall(DMPlexGetCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1777: PetscCheck(!invJ || J, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "In order to compute invJ, J must not be NULL");
1778: if (!Nq) {
1779: /* Assume that the map to the reference is affine */
1780: *detJ = 0.0;
1781: if (v) {
1782: for (d = 0; d < dim; d++) v[d] = PetscRealPart(coords[d]);
1783: }
1784: if (J) {
1785: for (d = 0; d < dim; d++) {
1786: J[d * dim + 0] = 0.5 * (PetscRealPart(coords[2 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
1787: J[d * dim + 1] = 0.5 * (PetscRealPart(coords[1 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
1788: J[d * dim + 2] = 0.5 * (PetscRealPart(coords[4 * dim + d]) - PetscRealPart(coords[0 * dim + d]));
1789: }
1790: PetscCall(PetscLogFlops(18.0));
1791: DMPlex_Det3D_Internal(detJ, J);
1792: }
1793: if (invJ) DMPlex_Invert3D_Internal(invJ, J, *detJ);
1794: } else {
1795: const PetscInt dim = 3;
1796: const PetscInt dimR = 3;
1797: const PetscInt Nv = 6;
1798: PetscReal verts[18];
1799: PetscReal coeff[18];
1800: PetscInt i, j, k, l;
1802: for (i = 0; i < Nv; ++i)
1803: for (j = 0; j < dim; ++j) verts[dim * i + j] = PetscRealPart(coords[dim * i + j]);
1804: for (j = 0; j < dim; ++j) {
1805: /* Check for triangle,
1806: 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)
1807: 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)
1808: phi^2 = 1/2 (1 + eta) chi^2 = delta(-1, 1)
1810: phi^0 + phi^1 + phi^2 = 1 coef_1 = 1/2 ( chi^1 + chi^2)
1811: -phi^0 + phi^1 - phi^2 = xi coef_xi = 1/2 (-chi^0 + chi^1)
1812: -phi^0 - phi^1 + phi^2 = eta coef_eta = 1/2 (-chi^0 + chi^2)
1814: < chi_0 chi_1 chi_2> A / 1 1 1 \ / phi_0 \ <chi> I <phi>^T so we need the inverse transpose
1815: | -1 1 -1 | | phi_1 | =
1816: \ -1 -1 1 / \ phi_2 /
1818: 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
1819: */
1820: /* Nodal basis for evaluation at the vertices: {-xi - eta, 1 + xi, 1 + eta} (1 \mp zeta):
1821: \phi^0 = 1/4 ( -xi - eta + xi zeta + eta zeta) --> / 1 1 1 1 1 1 \ 1
1822: \phi^1 = 1/4 (1 + eta - zeta - eta zeta) --> | -1 1 -1 -1 -1 1 | eta
1823: \phi^2 = 1/4 (1 + xi - zeta - xi zeta) --> | -1 -1 1 -1 1 -1 | xi
1824: \phi^3 = 1/4 ( -xi - eta - xi zeta - eta zeta) --> | -1 -1 -1 1 1 1 | zeta
1825: \phi^4 = 1/4 (1 + xi + zeta + xi zeta) --> | 1 1 -1 -1 1 -1 | xi zeta
1826: \phi^5 = 1/4 (1 + eta + zeta + eta zeta) --> \ 1 -1 1 -1 -1 1 / eta zeta
1827: 1/4 / 0 1 1 0 1 1 \
1828: | -1 1 0 -1 0 1 |
1829: | -1 0 1 -1 1 0 |
1830: | 0 -1 -1 0 1 1 |
1831: | 1 0 -1 -1 1 0 |
1832: \ 1 -1 0 -1 0 1 /
1833: */
1834: coeff[dim * 0 + j] = (1. / 4.) * (verts[dim * 1 + j] + verts[dim * 2 + j] + verts[dim * 4 + j] + verts[dim * 5 + j]);
1835: coeff[dim * 1 + j] = (1. / 4.) * (-verts[dim * 0 + j] + verts[dim * 1 + j] - verts[dim * 3 + j] + verts[dim * 5 + j]);
1836: coeff[dim * 2 + j] = (1. / 4.) * (-verts[dim * 0 + j] + verts[dim * 2 + j] - verts[dim * 3 + j] + verts[dim * 4 + j]);
1837: coeff[dim * 3 + j] = (1. / 4.) * (-verts[dim * 1 + j] - verts[dim * 2 + j] + verts[dim * 4 + j] + verts[dim * 5 + j]);
1838: coeff[dim * 4 + j] = (1. / 4.) * (verts[dim * 0 + j] - verts[dim * 2 + j] - verts[dim * 3 + j] + verts[dim * 4 + j]);
1839: coeff[dim * 5 + j] = (1. / 4.) * (verts[dim * 0 + j] - verts[dim * 1 + j] - verts[dim * 3 + j] + verts[dim * 5 + j]);
1840: /* For reference prism:
1841: {0, 0, 0}
1842: {0, 1, 0}
1843: {1, 0, 0}
1844: {0, 0, 1}
1845: {0, 0, 0}
1846: {0, 0, 0}
1847: */
1848: }
1849: for (i = 0; i < Nq; ++i) {
1850: const PetscReal xi = points[dimR * i], eta = points[dimR * i + 1], zeta = points[dimR * i + 2];
1852: if (v) {
1853: PetscReal extPoint[6];
1854: PetscInt c;
1856: extPoint[0] = 1.;
1857: extPoint[1] = eta;
1858: extPoint[2] = xi;
1859: extPoint[3] = zeta;
1860: extPoint[4] = xi * zeta;
1861: extPoint[5] = eta * zeta;
1862: for (c = 0; c < dim; ++c) {
1863: PetscReal val = 0.;
1865: for (k = 0; k < Nv; ++k) val += extPoint[k] * coeff[k * dim + c];
1866: v[i * dim + c] = val;
1867: }
1868: }
1869: if (J) {
1870: PetscReal extJ[18];
1872: extJ[0] = 0.;
1873: extJ[1] = 0.;
1874: extJ[2] = 0.;
1875: extJ[3] = 0.;
1876: extJ[4] = 1.;
1877: extJ[5] = 0.;
1878: extJ[6] = 1.;
1879: extJ[7] = 0.;
1880: extJ[8] = 0.;
1881: extJ[9] = 0.;
1882: extJ[10] = 0.;
1883: extJ[11] = 1.;
1884: extJ[12] = zeta;
1885: extJ[13] = 0.;
1886: extJ[14] = xi;
1887: extJ[15] = 0.;
1888: extJ[16] = zeta;
1889: extJ[17] = eta;
1891: for (j = 0; j < dim; j++) {
1892: for (k = 0; k < dimR; k++) {
1893: PetscReal val = 0.;
1895: for (l = 0; l < Nv; l++) val += coeff[dim * l + j] * extJ[dimR * l + k];
1896: J[i * dim * dim + dim * j + k] = val;
1897: }
1898: }
1899: DMPlex_Det3D_Internal(&detJ[i], &J[i * dim * dim]);
1900: if (invJ) DMPlex_Invert3D_Internal(&invJ[i * dim * dim], &J[i * dim * dim], detJ[i]);
1901: }
1902: }
1903: }
1904: PetscCall(DMPlexRestoreCellCoordinates(dm, e, &isDG, &numCoords, &array, &coords));
1905: PetscFunctionReturn(PETSC_SUCCESS);
1906: }
1908: static PetscErrorCode DMPlexComputeCellGeometryFEM_Implicit(DM dm, PetscInt cell, PetscQuadrature quad, PetscReal *v, PetscReal *J, PetscReal *invJ, PetscReal *detJ)
1909: {
1910: DMPolytopeType ct;
1911: PetscInt depth, dim, coordDim, coneSize, i;
1912: PetscInt Nq = 0;
1913: const PetscReal *points = NULL;
1914: DMLabel depthLabel;
1915: PetscReal xi0[3] = {-1., -1., -1.}, v0[3], J0[9], detJ0;
1916: PetscBool isAffine = PETSC_TRUE;
1918: PetscFunctionBegin;
1919: PetscCall(DMPlexGetDepth(dm, &depth));
1920: PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
1921: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
1922: PetscCall(DMLabelGetValue(depthLabel, cell, &dim));
1923: if (depth == 1 && dim == 1) PetscCall(DMGetDimension(dm, &dim));
1924: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1925: PetscCheck(coordDim <= 3, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unsupported coordinate dimension %" PetscInt_FMT " > 3", coordDim);
1926: if (quad) PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, &points, NULL));
1927: PetscCall(DMPlexGetCellType(dm, cell, &ct));
1928: switch (ct) {
1929: case DM_POLYTOPE_POINT:
1930: PetscCall(DMPlexComputePointGeometry_Internal(dm, cell, v, J, invJ, detJ));
1931: isAffine = PETSC_FALSE;
1932: break;
1933: case DM_POLYTOPE_SEGMENT:
1934: case DM_POLYTOPE_POINT_PRISM_TENSOR:
1935: if (Nq) PetscCall(DMPlexComputeLineGeometry_Internal(dm, cell, v0, J0, NULL, &detJ0));
1936: else PetscCall(DMPlexComputeLineGeometry_Internal(dm, cell, v, J, invJ, detJ));
1937: break;
1938: case DM_POLYTOPE_TRIANGLE:
1939: if (Nq) PetscCall(DMPlexComputeTriangleGeometry_Internal(dm, cell, v0, J0, NULL, &detJ0));
1940: else PetscCall(DMPlexComputeTriangleGeometry_Internal(dm, cell, v, J, invJ, detJ));
1941: break;
1942: case DM_POLYTOPE_QUADRILATERAL:
1943: PetscCall(DMPlexComputeRectangleGeometry_Internal(dm, cell, PETSC_FALSE, Nq, points, v, J, invJ, detJ));
1944: isAffine = PETSC_FALSE;
1945: break;
1946: case DM_POLYTOPE_SEG_PRISM_TENSOR:
1947: PetscCall(DMPlexComputeRectangleGeometry_Internal(dm, cell, PETSC_TRUE, Nq, points, v, J, invJ, detJ));
1948: isAffine = PETSC_FALSE;
1949: break;
1950: case DM_POLYTOPE_TETRAHEDRON:
1951: if (Nq) PetscCall(DMPlexComputeTetrahedronGeometry_Internal(dm, cell, v0, J0, NULL, &detJ0));
1952: else PetscCall(DMPlexComputeTetrahedronGeometry_Internal(dm, cell, v, J, invJ, detJ));
1953: break;
1954: case DM_POLYTOPE_HEXAHEDRON:
1955: PetscCall(DMPlexComputeHexahedronGeometry_Internal(dm, cell, Nq, points, v, J, invJ, detJ));
1956: isAffine = PETSC_FALSE;
1957: break;
1958: case DM_POLYTOPE_TRI_PRISM:
1959: PetscCall(DMPlexComputeTriangularPrismGeometry_Internal(dm, cell, Nq, points, v, J, invJ, detJ));
1960: isAffine = PETSC_FALSE;
1961: break;
1962: default:
1963: 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))]);
1964: }
1965: if (isAffine && Nq) {
1966: if (v) {
1967: for (i = 0; i < Nq; i++) CoordinatesRefToReal(coordDim, dim, xi0, v0, J0, &points[dim * i], &v[coordDim * i]);
1968: }
1969: if (detJ) {
1970: for (i = 0; i < Nq; i++) detJ[i] = detJ0;
1971: }
1972: if (J) {
1973: PetscInt k;
1975: for (i = 0, k = 0; i < Nq; i++) {
1976: PetscInt j;
1978: for (j = 0; j < coordDim * coordDim; j++, k++) J[k] = J0[j];
1979: }
1980: }
1981: if (invJ) {
1982: PetscInt k;
1983: switch (coordDim) {
1984: case 0:
1985: break;
1986: case 1:
1987: invJ[0] = 1. / J0[0];
1988: break;
1989: case 2:
1990: DMPlex_Invert2D_Internal(invJ, J0, detJ0);
1991: break;
1992: case 3:
1993: DMPlex_Invert3D_Internal(invJ, J0, detJ0);
1994: break;
1995: }
1996: for (i = 1, k = coordDim * coordDim; i < Nq; i++) {
1997: PetscInt j;
1999: for (j = 0; j < coordDim * coordDim; j++, k++) invJ[k] = invJ[j];
2000: }
2001: }
2002: }
2003: PetscFunctionReturn(PETSC_SUCCESS);
2004: }
2006: /*@C
2007: DMPlexComputeCellGeometryAffineFEM - Assuming an affine map, compute the Jacobian, inverse Jacobian, and Jacobian determinant for a given cell
2009: Collective on dm
2011: Input Parameters:
2012: + dm - the DM
2013: - cell - the cell
2015: Output Parameters:
2016: + v0 - the translation part of this affine transform, meaning the translation to the origin (not the first vertex of the reference cell)
2017: . J - the Jacobian of the transform from the reference element
2018: . invJ - the inverse of the Jacobian
2019: - detJ - the Jacobian determinant
2021: Level: advanced
2023: Fortran Notes:
2024: Since it returns arrays, this routine is only available in Fortran 90, and you must
2025: include petsc.h90 in your code.
2027: .seealso: `DMPlexComputeCellGeometryFEM()`, `DMGetCoordinateSection()`, `DMGetCoordinates()`
2028: @*/
2029: PetscErrorCode DMPlexComputeCellGeometryAffineFEM(DM dm, PetscInt cell, PetscReal *v0, PetscReal *J, PetscReal *invJ, PetscReal *detJ)
2030: {
2031: PetscFunctionBegin;
2032: PetscCall(DMPlexComputeCellGeometryFEM_Implicit(dm, cell, NULL, v0, J, invJ, detJ));
2033: PetscFunctionReturn(PETSC_SUCCESS);
2034: }
2036: static PetscErrorCode DMPlexComputeCellGeometryFEM_FE(DM dm, PetscFE fe, PetscInt point, PetscQuadrature quad, PetscReal v[], PetscReal J[], PetscReal invJ[], PetscReal *detJ)
2037: {
2038: const PetscScalar *array;
2039: PetscScalar *coords = NULL;
2040: PetscInt numCoords;
2041: PetscBool isDG;
2042: PetscQuadrature feQuad;
2043: const PetscReal *quadPoints;
2044: PetscTabulation T;
2045: PetscInt dim, cdim, pdim, qdim, Nq, q;
2047: PetscFunctionBegin;
2048: PetscCall(DMGetDimension(dm, &dim));
2049: PetscCall(DMGetCoordinateDim(dm, &cdim));
2050: PetscCall(DMPlexGetCellCoordinates(dm, point, &isDG, &numCoords, &array, &coords));
2051: if (!quad) { /* use the first point of the first functional of the dual space */
2052: PetscDualSpace dsp;
2054: PetscCall(PetscFEGetDualSpace(fe, &dsp));
2055: PetscCall(PetscDualSpaceGetFunctional(dsp, 0, &quad));
2056: PetscCall(PetscQuadratureGetData(quad, &qdim, NULL, &Nq, &quadPoints, NULL));
2057: Nq = 1;
2058: } else {
2059: PetscCall(PetscQuadratureGetData(quad, &qdim, NULL, &Nq, &quadPoints, NULL));
2060: }
2061: PetscCall(PetscFEGetDimension(fe, &pdim));
2062: PetscCall(PetscFEGetQuadrature(fe, &feQuad));
2063: if (feQuad == quad) {
2064: PetscCall(PetscFEGetCellTabulation(fe, J ? 1 : 0, &T));
2065: 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);
2066: } else {
2067: PetscCall(PetscFECreateTabulation(fe, 1, Nq, quadPoints, J ? 1 : 0, &T));
2068: }
2069: PetscCheck(qdim == dim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Point dimension %" PetscInt_FMT " != quadrature dimension %" PetscInt_FMT, dim, qdim);
2070: {
2071: const PetscReal *basis = T->T[0];
2072: const PetscReal *basisDer = T->T[1];
2073: PetscReal detJt;
2075: #if defined(PETSC_USE_DEBUG)
2076: PetscCheck(Nq == T->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Np %" PetscInt_FMT " != %" PetscInt_FMT, Nq, T->Np);
2077: PetscCheck(pdim == T->Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Nb %" PetscInt_FMT " != %" PetscInt_FMT, pdim, T->Nb);
2078: PetscCheck(dim == T->Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Nc %" PetscInt_FMT " != %" PetscInt_FMT, dim, T->Nc);
2079: PetscCheck(cdim == T->cdim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "cdim %" PetscInt_FMT " != %" PetscInt_FMT, cdim, T->cdim);
2080: #endif
2081: if (v) {
2082: PetscCall(PetscArrayzero(v, Nq * cdim));
2083: for (q = 0; q < Nq; ++q) {
2084: PetscInt i, k;
2086: for (k = 0; k < pdim; ++k) {
2087: const PetscInt vertex = k / cdim;
2088: for (i = 0; i < cdim; ++i) v[q * cdim + i] += basis[(q * pdim + k) * cdim + i] * PetscRealPart(coords[vertex * cdim + i]);
2089: }
2090: PetscCall(PetscLogFlops(2.0 * pdim * cdim));
2091: }
2092: }
2093: if (J) {
2094: PetscCall(PetscArrayzero(J, Nq * cdim * cdim));
2095: for (q = 0; q < Nq; ++q) {
2096: PetscInt i, j, k, c, r;
2098: /* J = dx_i/d\xi_j = sum[k=0,n-1] dN_k/d\xi_j * x_i(k) */
2099: for (k = 0; k < pdim; ++k) {
2100: const PetscInt vertex = k / cdim;
2101: for (j = 0; j < dim; ++j) {
2102: 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]);
2103: }
2104: }
2105: PetscCall(PetscLogFlops(2.0 * pdim * dim * cdim));
2106: if (cdim > dim) {
2107: for (c = dim; c < cdim; ++c)
2108: for (r = 0; r < cdim; ++r) J[r * cdim + c] = r == c ? 1.0 : 0.0;
2109: }
2110: if (!detJ && !invJ) continue;
2111: detJt = 0.;
2112: switch (cdim) {
2113: case 3:
2114: DMPlex_Det3D_Internal(&detJt, &J[q * cdim * dim]);
2115: if (invJ) DMPlex_Invert3D_Internal(&invJ[q * cdim * dim], &J[q * cdim * dim], detJt);
2116: break;
2117: case 2:
2118: DMPlex_Det2D_Internal(&detJt, &J[q * cdim * dim]);
2119: if (invJ) DMPlex_Invert2D_Internal(&invJ[q * cdim * dim], &J[q * cdim * dim], detJt);
2120: break;
2121: case 1:
2122: detJt = J[q * cdim * dim];
2123: if (invJ) invJ[q * cdim * dim] = 1.0 / detJt;
2124: }
2125: if (detJ) detJ[q] = detJt;
2126: }
2127: } else PetscCheck(!detJ && !invJ, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Need J to compute invJ or detJ");
2128: }
2129: if (feQuad != quad) PetscCall(PetscTabulationDestroy(&T));
2130: PetscCall(DMPlexRestoreCellCoordinates(dm, point, &isDG, &numCoords, &array, &coords));
2131: PetscFunctionReturn(PETSC_SUCCESS);
2132: }
2134: /*@C
2135: DMPlexComputeCellGeometryFEM - Compute the Jacobian, inverse Jacobian, and Jacobian determinant at each quadrature point in the given cell
2137: Collective on dm
2139: Input Parameters:
2140: + dm - the DM
2141: . cell - the cell
2142: - quad - the quadrature containing the points in the reference element where the geometry will be evaluated. If quad == NULL, geometry will be
2143: evaluated at the first vertex of the reference element
2145: Output Parameters:
2146: + v - the image of the transformed quadrature points, otherwise the image of the first vertex in the closure of the reference element
2147: . J - the Jacobian of the transform from the reference element at each quadrature point
2148: . invJ - the inverse of the Jacobian at each quadrature point
2149: - detJ - the Jacobian determinant at each quadrature point
2151: Level: advanced
2153: Fortran Notes:
2154: Since it returns arrays, this routine is only available in Fortran 90, and you must
2155: include petsc.h90 in your code.
2157: .seealso: `DMGetCoordinateSection()`, `DMGetCoordinates()`
2158: @*/
2159: PetscErrorCode DMPlexComputeCellGeometryFEM(DM dm, PetscInt cell, PetscQuadrature quad, PetscReal *v, PetscReal *J, PetscReal *invJ, PetscReal *detJ)
2160: {
2161: DM cdm;
2162: PetscFE fe = NULL;
2164: PetscFunctionBegin;
2166: PetscCall(DMGetCoordinateDM(dm, &cdm));
2167: if (cdm) {
2168: PetscClassId id;
2169: PetscInt numFields;
2170: PetscDS prob;
2171: PetscObject disc;
2173: PetscCall(DMGetNumFields(cdm, &numFields));
2174: if (numFields) {
2175: PetscCall(DMGetDS(cdm, &prob));
2176: PetscCall(PetscDSGetDiscretization(prob, 0, &disc));
2177: PetscCall(PetscObjectGetClassId(disc, &id));
2178: if (id == PETSCFE_CLASSID) fe = (PetscFE)disc;
2179: }
2180: }
2181: if (!fe) PetscCall(DMPlexComputeCellGeometryFEM_Implicit(dm, cell, quad, v, J, invJ, detJ));
2182: else PetscCall(DMPlexComputeCellGeometryFEM_FE(dm, fe, cell, quad, v, J, invJ, detJ));
2183: PetscFunctionReturn(PETSC_SUCCESS);
2184: }
2186: static PetscErrorCode DMPlexComputeGeometryFVM_0D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2187: {
2188: PetscSection coordSection;
2189: Vec coordinates;
2190: const PetscScalar *coords = NULL;
2191: PetscInt d, dof, off;
2193: PetscFunctionBegin;
2194: PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
2195: PetscCall(DMGetCoordinateSection(dm, &coordSection));
2196: PetscCall(VecGetArrayRead(coordinates, &coords));
2198: /* for a point the centroid is just the coord */
2199: if (centroid) {
2200: PetscCall(PetscSectionGetDof(coordSection, cell, &dof));
2201: PetscCall(PetscSectionGetOffset(coordSection, cell, &off));
2202: for (d = 0; d < dof; d++) centroid[d] = PetscRealPart(coords[off + d]);
2203: }
2204: if (normal) {
2205: const PetscInt *support, *cones;
2206: PetscInt supportSize;
2207: PetscReal norm, sign;
2209: /* compute the norm based upon the support centroids */
2210: PetscCall(DMPlexGetSupportSize(dm, cell, &supportSize));
2211: PetscCall(DMPlexGetSupport(dm, cell, &support));
2212: PetscCall(DMPlexComputeCellGeometryFVM(dm, support[0], NULL, normal, NULL));
2214: /* Take the normal from the centroid of the support to the vertex*/
2215: PetscCall(PetscSectionGetDof(coordSection, cell, &dof));
2216: PetscCall(PetscSectionGetOffset(coordSection, cell, &off));
2217: for (d = 0; d < dof; d++) normal[d] -= PetscRealPart(coords[off + d]);
2219: /* Determine the sign of the normal based upon its location in the support */
2220: PetscCall(DMPlexGetCone(dm, support[0], &cones));
2221: sign = cones[0] == cell ? 1.0 : -1.0;
2223: norm = DMPlex_NormD_Internal(dim, normal);
2224: for (d = 0; d < dim; ++d) normal[d] /= (norm * sign);
2225: }
2226: if (vol) *vol = 1.0;
2227: PetscCall(VecRestoreArrayRead(coordinates, &coords));
2228: PetscFunctionReturn(PETSC_SUCCESS);
2229: }
2231: static PetscErrorCode DMPlexComputeGeometryFVM_1D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2232: {
2233: const PetscScalar *array;
2234: PetscScalar *coords = NULL;
2235: PetscInt cdim, coordSize, d;
2236: PetscBool isDG;
2238: PetscFunctionBegin;
2239: PetscCall(DMGetCoordinateDim(dm, &cdim));
2240: PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2241: PetscCheck(coordSize == cdim * 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge has %" PetscInt_FMT " coordinates != %" PetscInt_FMT, coordSize, cdim * 2);
2242: if (centroid) {
2243: for (d = 0; d < cdim; ++d) centroid[d] = 0.5 * PetscRealPart(coords[d] + coords[cdim + d]);
2244: }
2245: if (normal) {
2246: PetscReal norm;
2248: switch (cdim) {
2249: case 3:
2250: normal[2] = 0.; /* fall through */
2251: case 2:
2252: normal[0] = -PetscRealPart(coords[1] - coords[cdim + 1]);
2253: normal[1] = PetscRealPart(coords[0] - coords[cdim + 0]);
2254: break;
2255: case 1:
2256: normal[0] = 1.0;
2257: break;
2258: default:
2259: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Dimension %" PetscInt_FMT " not supported", cdim);
2260: }
2261: norm = DMPlex_NormD_Internal(cdim, normal);
2262: for (d = 0; d < cdim; ++d) normal[d] /= norm;
2263: }
2264: if (vol) {
2265: *vol = 0.0;
2266: for (d = 0; d < cdim; ++d) *vol += PetscSqr(PetscRealPart(coords[d] - coords[cdim + d]));
2267: *vol = PetscSqrtReal(*vol);
2268: }
2269: PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2270: PetscFunctionReturn(PETSC_SUCCESS);
2271: }
2273: /* Centroid_i = (\sum_n A_n Cn_i) / A */
2274: static PetscErrorCode DMPlexComputeGeometryFVM_2D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2275: {
2276: DMPolytopeType ct;
2277: const PetscScalar *array;
2278: PetscScalar *coords = NULL;
2279: PetscInt coordSize;
2280: PetscBool isDG;
2281: PetscInt fv[4] = {0, 1, 2, 3};
2282: PetscInt cdim, numCorners, p, d;
2284: PetscFunctionBegin;
2285: /* Must check for hybrid cells because prisms have a different orientation scheme */
2286: PetscCall(DMPlexGetCellType(dm, cell, &ct));
2287: switch (ct) {
2288: case DM_POLYTOPE_SEG_PRISM_TENSOR:
2289: fv[2] = 3;
2290: fv[3] = 2;
2291: break;
2292: default:
2293: break;
2294: }
2295: PetscCall(DMGetCoordinateDim(dm, &cdim));
2296: PetscCall(DMPlexGetConeSize(dm, cell, &numCorners));
2297: PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2298: {
2299: PetscReal c[3] = {0., 0., 0.}, n[3] = {0., 0., 0.}, origin[3] = {0., 0., 0.}, norm;
2301: for (d = 0; d < cdim; d++) origin[d] = PetscRealPart(coords[d]);
2302: for (p = 0; p < numCorners - 2; ++p) {
2303: PetscReal e0[3] = {0., 0., 0.}, e1[3] = {0., 0., 0.};
2304: for (d = 0; d < cdim; d++) {
2305: e0[d] = PetscRealPart(coords[cdim * fv[p + 1] + d]) - origin[d];
2306: e1[d] = PetscRealPart(coords[cdim * fv[p + 2] + d]) - origin[d];
2307: }
2308: const PetscReal dx = e0[1] * e1[2] - e0[2] * e1[1];
2309: const PetscReal dy = e0[2] * e1[0] - e0[0] * e1[2];
2310: const PetscReal dz = e0[0] * e1[1] - e0[1] * e1[0];
2311: const PetscReal a = PetscSqrtReal(dx * dx + dy * dy + dz * dz);
2313: n[0] += dx;
2314: n[1] += dy;
2315: n[2] += dz;
2316: 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.;
2317: }
2318: norm = PetscSqrtReal(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
2319: n[0] /= norm;
2320: n[1] /= norm;
2321: n[2] /= norm;
2322: c[0] /= norm;
2323: c[1] /= norm;
2324: c[2] /= norm;
2325: if (vol) *vol = 0.5 * norm;
2326: if (centroid)
2327: for (d = 0; d < cdim; ++d) centroid[d] = c[d];
2328: if (normal)
2329: for (d = 0; d < cdim; ++d) normal[d] = n[d];
2330: }
2331: PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2332: PetscFunctionReturn(PETSC_SUCCESS);
2333: }
2335: /* Centroid_i = (\sum_n V_n Cn_i) / V */
2336: static PetscErrorCode DMPlexComputeGeometryFVM_3D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2337: {
2338: DMPolytopeType ct;
2339: const PetscScalar *array;
2340: PetscScalar *coords = NULL;
2341: PetscInt coordSize;
2342: PetscBool isDG;
2343: PetscReal vsum = 0.0, vtmp, coordsTmp[3 * 3], origin[3];
2344: const PetscInt order[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
2345: const PetscInt *cone, *faceSizes, *faces;
2346: const DMPolytopeType *faceTypes;
2347: PetscBool isHybrid = PETSC_FALSE;
2348: PetscInt numFaces, f, fOff = 0, p, d;
2350: PetscFunctionBegin;
2351: PetscCheck(dim <= 3, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No support for dim %" PetscInt_FMT " > 3", dim);
2352: /* Must check for hybrid cells because prisms have a different orientation scheme */
2353: PetscCall(DMPlexGetCellType(dm, cell, &ct));
2354: switch (ct) {
2355: case DM_POLYTOPE_POINT_PRISM_TENSOR:
2356: case DM_POLYTOPE_SEG_PRISM_TENSOR:
2357: case DM_POLYTOPE_TRI_PRISM_TENSOR:
2358: case DM_POLYTOPE_QUAD_PRISM_TENSOR:
2359: isHybrid = PETSC_TRUE;
2360: default:
2361: break;
2362: }
2364: if (centroid)
2365: for (d = 0; d < dim; ++d) centroid[d] = 0.0;
2366: PetscCall(DMPlexGetCone(dm, cell, &cone));
2368: // Using the closure of faces for coordinates does not work in periodic geometries, so we index into the cell coordinates
2369: PetscCall(DMPlexGetRawFaces_Internal(dm, ct, order, &numFaces, &faceTypes, &faceSizes, &faces));
2370: PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2371: for (f = 0; f < numFaces; ++f) {
2372: PetscBool flip = isHybrid && f == 0 ? PETSC_TRUE : PETSC_FALSE; /* The first hybrid face is reversed */
2374: // If using zero as the origin vertex for each tetrahedron, an element far from the origin will have positive and
2375: // negative volumes that nearly cancel, thus incurring rounding error. Here we define origin[] as the first vertex
2376: // so that all tetrahedra have positive volume.
2377: if (f == 0)
2378: for (d = 0; d < dim; d++) origin[d] = PetscRealPart(coords[d]);
2379: switch (faceTypes[f]) {
2380: case DM_POLYTOPE_TRIANGLE:
2381: for (d = 0; d < dim; ++d) {
2382: coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + 0] * dim + d]) - origin[d];
2383: coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + 1] * dim + d]) - origin[d];
2384: coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + 2] * dim + d]) - origin[d];
2385: }
2386: Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2387: if (flip) vtmp = -vtmp;
2388: vsum += vtmp;
2389: if (centroid) { /* Centroid of OABC = (a+b+c)/4 */
2390: for (d = 0; d < dim; ++d) {
2391: for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp;
2392: }
2393: }
2394: break;
2395: case DM_POLYTOPE_QUADRILATERAL:
2396: case DM_POLYTOPE_SEG_PRISM_TENSOR: {
2397: PetscInt fv[4] = {0, 1, 2, 3};
2399: /* Side faces for hybrid cells are are stored as tensor products */
2400: if (isHybrid && f > 1) {
2401: fv[2] = 3;
2402: fv[3] = 2;
2403: }
2404: /* DO FOR PYRAMID */
2405: /* First tet */
2406: for (d = 0; d < dim; ++d) {
2407: coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + fv[0]] * dim + d]) - origin[d];
2408: coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + fv[1]] * dim + d]) - origin[d];
2409: coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + fv[3]] * dim + d]) - origin[d];
2410: }
2411: Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2412: if (flip) vtmp = -vtmp;
2413: vsum += vtmp;
2414: if (centroid) {
2415: for (d = 0; d < dim; ++d) {
2416: for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp;
2417: }
2418: }
2419: /* Second tet */
2420: for (d = 0; d < dim; ++d) {
2421: coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + fv[1]] * dim + d]) - origin[d];
2422: coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + fv[2]] * dim + d]) - origin[d];
2423: coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + fv[3]] * dim + d]) - origin[d];
2424: }
2425: Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp);
2426: if (flip) vtmp = -vtmp;
2427: vsum += vtmp;
2428: if (centroid) {
2429: for (d = 0; d < dim; ++d) {
2430: for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp;
2431: }
2432: }
2433: break;
2434: }
2435: default:
2436: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle face %" PetscInt_FMT " of type %s", cone[f], DMPolytopeTypes[ct]);
2437: }
2438: fOff += faceSizes[f];
2439: }
2440: PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, order, &numFaces, &faceTypes, &faceSizes, &faces));
2441: PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords));
2442: if (vol) *vol = PetscAbsReal(vsum);
2443: if (normal)
2444: for (d = 0; d < dim; ++d) normal[d] = 0.0;
2445: if (centroid)
2446: for (d = 0; d < dim; ++d) centroid[d] = centroid[d] / (vsum * 4) + origin[d];
2447: PetscFunctionReturn(PETSC_SUCCESS);
2448: }
2450: /*@C
2451: DMPlexComputeCellGeometryFVM - Compute the volume for a given cell
2453: Collective on dm
2455: Input Parameters:
2456: + dm - the DM
2457: - cell - the cell
2459: Output Parameters:
2460: + volume - the cell volume
2461: . centroid - the cell centroid
2462: - normal - the cell normal, if appropriate
2464: Level: advanced
2466: Fortran Notes:
2467: Since it returns arrays, this routine is only available in Fortran 90, and you must
2468: include petsc.h90 in your code.
2470: .seealso: `DMGetCoordinateSection()`, `DMGetCoordinates()`
2471: @*/
2472: PetscErrorCode DMPlexComputeCellGeometryFVM(DM dm, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[])
2473: {
2474: PetscInt depth, dim;
2476: PetscFunctionBegin;
2477: PetscCall(DMPlexGetDepth(dm, &depth));
2478: PetscCall(DMGetDimension(dm, &dim));
2479: PetscCheck(depth == dim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh must be interpolated");
2480: PetscCall(DMPlexGetPointDepth(dm, cell, &depth));
2481: switch (depth) {
2482: case 0:
2483: PetscCall(DMPlexComputeGeometryFVM_0D_Internal(dm, dim, cell, vol, centroid, normal));
2484: break;
2485: case 1:
2486: PetscCall(DMPlexComputeGeometryFVM_1D_Internal(dm, dim, cell, vol, centroid, normal));
2487: break;
2488: case 2:
2489: PetscCall(DMPlexComputeGeometryFVM_2D_Internal(dm, dim, cell, vol, centroid, normal));
2490: break;
2491: case 3:
2492: PetscCall(DMPlexComputeGeometryFVM_3D_Internal(dm, dim, cell, vol, centroid, normal));
2493: break;
2494: default:
2495: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unsupported dimension %" PetscInt_FMT " (depth %" PetscInt_FMT ") for element geometry computation", dim, depth);
2496: }
2497: PetscFunctionReturn(PETSC_SUCCESS);
2498: }
2500: /*@
2501: DMPlexComputeGeometryFVM - Computes the cell and face geometry for a finite volume method
2503: Input Parameter:
2504: . dm - The DM
2506: Output Parameters:
2507: + cellgeom - A Vec of PetscFVCellGeom data
2508: - facegeom - A Vec of PetscFVFaceGeom data
2510: Level: developer
2512: .seealso: `PetscFVFaceGeom`, `PetscFVCellGeom`
2513: @*/
2514: PetscErrorCode DMPlexComputeGeometryFVM(DM dm, Vec *cellgeom, Vec *facegeom)
2515: {
2516: DM dmFace, dmCell;
2517: DMLabel ghostLabel;
2518: PetscSection sectionFace, sectionCell;
2519: PetscSection coordSection;
2520: Vec coordinates;
2521: PetscScalar *fgeom, *cgeom;
2522: PetscReal minradius, gminradius;
2523: PetscInt dim, cStart, cEnd, cEndInterior, c, fStart, fEnd, f;
2525: PetscFunctionBegin;
2526: PetscCall(DMGetDimension(dm, &dim));
2527: PetscCall(DMGetCoordinateSection(dm, &coordSection));
2528: PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
2529: /* Make cell centroids and volumes */
2530: PetscCall(DMClone(dm, &dmCell));
2531: PetscCall(DMSetCoordinateSection(dmCell, PETSC_DETERMINE, coordSection));
2532: PetscCall(DMSetCoordinatesLocal(dmCell, coordinates));
2533: PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), §ionCell));
2534: PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2535: PetscCall(DMPlexGetGhostCellStratum(dm, &cEndInterior, NULL));
2536: PetscCall(PetscSectionSetChart(sectionCell, cStart, cEnd));
2537: for (c = cStart; c < cEnd; ++c) PetscCall(PetscSectionSetDof(sectionCell, c, (PetscInt)PetscCeilReal(((PetscReal)sizeof(PetscFVCellGeom)) / sizeof(PetscScalar))));
2538: PetscCall(PetscSectionSetUp(sectionCell));
2539: PetscCall(DMSetLocalSection(dmCell, sectionCell));
2540: PetscCall(PetscSectionDestroy(§ionCell));
2541: PetscCall(DMCreateLocalVector(dmCell, cellgeom));
2542: if (cEndInterior < 0) cEndInterior = cEnd;
2543: PetscCall(VecGetArray(*cellgeom, &cgeom));
2544: for (c = cStart; c < cEndInterior; ++c) {
2545: PetscFVCellGeom *cg;
2547: PetscCall(DMPlexPointLocalRef(dmCell, c, cgeom, &cg));
2548: PetscCall(PetscArrayzero(cg, 1));
2549: PetscCall(DMPlexComputeCellGeometryFVM(dmCell, c, &cg->volume, cg->centroid, NULL));
2550: }
2551: /* Compute face normals and minimum cell radius */
2552: PetscCall(DMClone(dm, &dmFace));
2553: PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), §ionFace));
2554: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
2555: PetscCall(PetscSectionSetChart(sectionFace, fStart, fEnd));
2556: for (f = fStart; f < fEnd; ++f) PetscCall(PetscSectionSetDof(sectionFace, f, (PetscInt)PetscCeilReal(((PetscReal)sizeof(PetscFVFaceGeom)) / sizeof(PetscScalar))));
2557: PetscCall(PetscSectionSetUp(sectionFace));
2558: PetscCall(DMSetLocalSection(dmFace, sectionFace));
2559: PetscCall(PetscSectionDestroy(§ionFace));
2560: PetscCall(DMCreateLocalVector(dmFace, facegeom));
2561: PetscCall(VecGetArray(*facegeom, &fgeom));
2562: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
2563: minradius = PETSC_MAX_REAL;
2564: for (f = fStart; f < fEnd; ++f) {
2565: PetscFVFaceGeom *fg;
2566: PetscReal area;
2567: const PetscInt *cells;
2568: PetscInt ncells, ghost = -1, d, numChildren;
2570: if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost));
2571: PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL));
2572: PetscCall(DMPlexGetSupport(dm, f, &cells));
2573: PetscCall(DMPlexGetSupportSize(dm, f, &ncells));
2574: /* It is possible to get a face with no support when using partition overlap */
2575: if (!ncells || ghost >= 0 || numChildren) continue;
2576: PetscCall(DMPlexPointLocalRef(dmFace, f, fgeom, &fg));
2577: PetscCall(DMPlexComputeCellGeometryFVM(dm, f, &area, fg->centroid, fg->normal));
2578: for (d = 0; d < dim; ++d) fg->normal[d] *= area;
2579: /* Flip face orientation if necessary to match ordering in support, and Update minimum radius */
2580: {
2581: PetscFVCellGeom *cL, *cR;
2582: PetscReal *lcentroid, *rcentroid;
2583: PetscReal l[3], r[3], v[3];
2585: PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cgeom, &cL));
2586: lcentroid = cells[0] >= cEndInterior ? fg->centroid : cL->centroid;
2587: if (ncells > 1) {
2588: PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cgeom, &cR));
2589: rcentroid = cells[1] >= cEndInterior ? fg->centroid : cR->centroid;
2590: } else {
2591: rcentroid = fg->centroid;
2592: }
2593: PetscCall(DMLocalizeCoordinateReal_Internal(dm, dim, fg->centroid, lcentroid, l));
2594: PetscCall(DMLocalizeCoordinateReal_Internal(dm, dim, fg->centroid, rcentroid, r));
2595: DMPlex_WaxpyD_Internal(dim, -1, l, r, v);
2596: if (DMPlex_DotRealD_Internal(dim, fg->normal, v) < 0) {
2597: for (d = 0; d < dim; ++d) fg->normal[d] = -fg->normal[d];
2598: }
2599: if (DMPlex_DotRealD_Internal(dim, fg->normal, v) <= 0) {
2600: 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]);
2601: 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]);
2602: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Direction for face %" PetscInt_FMT " could not be fixed", f);
2603: }
2604: if (cells[0] < cEndInterior) {
2605: DMPlex_WaxpyD_Internal(dim, -1, fg->centroid, cL->centroid, v);
2606: minradius = PetscMin(minradius, DMPlex_NormD_Internal(dim, v));
2607: }
2608: if (ncells > 1 && cells[1] < cEndInterior) {
2609: DMPlex_WaxpyD_Internal(dim, -1, fg->centroid, cR->centroid, v);
2610: minradius = PetscMin(minradius, DMPlex_NormD_Internal(dim, v));
2611: }
2612: }
2613: }
2614: PetscCall(MPIU_Allreduce(&minradius, &gminradius, 1, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
2615: PetscCall(DMPlexSetMinRadius(dm, gminradius));
2616: /* Compute centroids of ghost cells */
2617: for (c = cEndInterior; c < cEnd; ++c) {
2618: PetscFVFaceGeom *fg;
2619: const PetscInt *cone, *support;
2620: PetscInt coneSize, supportSize, s;
2622: PetscCall(DMPlexGetConeSize(dmCell, c, &coneSize));
2623: PetscCheck(coneSize == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Ghost cell %" PetscInt_FMT " has cone size %" PetscInt_FMT " != 1", c, coneSize);
2624: PetscCall(DMPlexGetCone(dmCell, c, &cone));
2625: PetscCall(DMPlexGetSupportSize(dmCell, cone[0], &supportSize));
2626: PetscCheck(supportSize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[0], supportSize);
2627: PetscCall(DMPlexGetSupport(dmCell, cone[0], &support));
2628: PetscCall(DMPlexPointLocalRef(dmFace, cone[0], fgeom, &fg));
2629: for (s = 0; s < 2; ++s) {
2630: /* Reflect ghost centroid across plane of face */
2631: if (support[s] == c) {
2632: PetscFVCellGeom *ci;
2633: PetscFVCellGeom *cg;
2634: PetscReal c2f[3], a;
2636: PetscCall(DMPlexPointLocalRead(dmCell, support[(s + 1) % 2], cgeom, &ci));
2637: DMPlex_WaxpyD_Internal(dim, -1, ci->centroid, fg->centroid, c2f); /* cell to face centroid */
2638: a = DMPlex_DotRealD_Internal(dim, c2f, fg->normal) / DMPlex_DotRealD_Internal(dim, fg->normal, fg->normal);
2639: PetscCall(DMPlexPointLocalRef(dmCell, support[s], cgeom, &cg));
2640: DMPlex_WaxpyD_Internal(dim, 2 * a, fg->normal, ci->centroid, cg->centroid);
2641: cg->volume = ci->volume;
2642: }
2643: }
2644: }
2645: PetscCall(VecRestoreArray(*facegeom, &fgeom));
2646: PetscCall(VecRestoreArray(*cellgeom, &cgeom));
2647: PetscCall(DMDestroy(&dmCell));
2648: PetscCall(DMDestroy(&dmFace));
2649: PetscFunctionReturn(PETSC_SUCCESS);
2650: }
2652: /*@C
2653: DMPlexGetMinRadius - Returns the minimum distance from any cell centroid to a face
2655: Not collective
2657: Input Parameter:
2658: . dm - the DM
2660: Output Parameter:
2661: . minradius - the minimum cell radius
2663: Level: developer
2665: .seealso: `DMGetCoordinates()`
2666: @*/
2667: PetscErrorCode DMPlexGetMinRadius(DM dm, PetscReal *minradius)
2668: {
2669: PetscFunctionBegin;
2672: *minradius = ((DM_Plex *)dm->data)->minradius;
2673: PetscFunctionReturn(PETSC_SUCCESS);
2674: }
2676: /*@C
2677: DMPlexSetMinRadius - Sets the minimum distance from the cell centroid to a face
2679: Logically collective
2681: Input Parameters:
2682: + dm - the DM
2683: - minradius - the minimum cell radius
2685: Level: developer
2687: .seealso: `DMSetCoordinates()`
2688: @*/
2689: PetscErrorCode DMPlexSetMinRadius(DM dm, PetscReal minradius)
2690: {
2691: PetscFunctionBegin;
2693: ((DM_Plex *)dm->data)->minradius = minradius;
2694: PetscFunctionReturn(PETSC_SUCCESS);
2695: }
2697: static PetscErrorCode BuildGradientReconstruction_Internal(DM dm, PetscFV fvm, DM dmFace, PetscScalar *fgeom, DM dmCell, PetscScalar *cgeom)
2698: {
2699: DMLabel ghostLabel;
2700: PetscScalar *dx, *grad, **gref;
2701: PetscInt dim, cStart, cEnd, c, cEndInterior, maxNumFaces;
2703: PetscFunctionBegin;
2704: PetscCall(DMGetDimension(dm, &dim));
2705: PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2706: PetscCall(DMPlexGetGhostCellStratum(dm, &cEndInterior, NULL));
2707: cEndInterior = cEndInterior < 0 ? cEnd : cEndInterior;
2708: PetscCall(DMPlexGetMaxSizes(dm, &maxNumFaces, NULL));
2709: PetscCall(PetscFVLeastSquaresSetMaxFaces(fvm, maxNumFaces));
2710: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
2711: PetscCall(PetscMalloc3(maxNumFaces * dim, &dx, maxNumFaces * dim, &grad, maxNumFaces, &gref));
2712: for (c = cStart; c < cEndInterior; c++) {
2713: const PetscInt *faces;
2714: PetscInt numFaces, usedFaces, f, d;
2715: PetscFVCellGeom *cg;
2716: PetscBool boundary;
2717: PetscInt ghost;
2719: // do not attempt to compute a gradient reconstruction stencil in a ghost cell. It will never be used
2720: PetscCall(DMLabelGetValue(ghostLabel, c, &ghost));
2721: if (ghost >= 0) continue;
2723: PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg));
2724: PetscCall(DMPlexGetConeSize(dm, c, &numFaces));
2725: PetscCall(DMPlexGetCone(dm, c, &faces));
2726: 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);
2727: for (f = 0, usedFaces = 0; f < numFaces; ++f) {
2728: PetscFVCellGeom *cg1;
2729: PetscFVFaceGeom *fg;
2730: const PetscInt *fcells;
2731: PetscInt ncell, side;
2733: PetscCall(DMLabelGetValue(ghostLabel, faces[f], &ghost));
2734: PetscCall(DMIsBoundaryPoint(dm, faces[f], &boundary));
2735: if ((ghost >= 0) || boundary) continue;
2736: PetscCall(DMPlexGetSupport(dm, faces[f], &fcells));
2737: side = (c != fcells[0]); /* c is on left=0 or right=1 of face */
2738: ncell = fcells[!side]; /* the neighbor */
2739: PetscCall(DMPlexPointLocalRef(dmFace, faces[f], fgeom, &fg));
2740: PetscCall(DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1));
2741: for (d = 0; d < dim; ++d) dx[usedFaces * dim + d] = cg1->centroid[d] - cg->centroid[d];
2742: gref[usedFaces++] = fg->grad[side]; /* Gradient reconstruction term will go here */
2743: }
2744: PetscCheck(usedFaces, PETSC_COMM_SELF, PETSC_ERR_USER, "Mesh contains isolated cell (no neighbors). Is it intentional?");
2745: PetscCall(PetscFVComputeGradient(fvm, usedFaces, dx, grad));
2746: for (f = 0, usedFaces = 0; f < numFaces; ++f) {
2747: PetscCall(DMLabelGetValue(ghostLabel, faces[f], &ghost));
2748: PetscCall(DMIsBoundaryPoint(dm, faces[f], &boundary));
2749: if ((ghost >= 0) || boundary) continue;
2750: for (d = 0; d < dim; ++d) gref[usedFaces][d] = grad[usedFaces * dim + d];
2751: ++usedFaces;
2752: }
2753: }
2754: PetscCall(PetscFree3(dx, grad, gref));
2755: PetscFunctionReturn(PETSC_SUCCESS);
2756: }
2758: static PetscErrorCode BuildGradientReconstruction_Internal_Tree(DM dm, PetscFV fvm, DM dmFace, PetscScalar *fgeom, DM dmCell, PetscScalar *cgeom)
2759: {
2760: DMLabel ghostLabel;
2761: PetscScalar *dx, *grad, **gref;
2762: PetscInt dim, cStart, cEnd, c, cEndInterior, fStart, fEnd, f, nStart, nEnd, maxNumFaces = 0;
2763: PetscSection neighSec;
2764: PetscInt(*neighbors)[2];
2765: PetscInt *counter;
2767: PetscFunctionBegin;
2768: PetscCall(DMGetDimension(dm, &dim));
2769: PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2770: PetscCall(DMPlexGetGhostCellStratum(dm, &cEndInterior, NULL));
2771: if (cEndInterior < 0) cEndInterior = cEnd;
2772: PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &neighSec));
2773: PetscCall(PetscSectionSetChart(neighSec, cStart, cEndInterior));
2774: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
2775: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
2776: for (f = fStart; f < fEnd; f++) {
2777: const PetscInt *fcells;
2778: PetscBool boundary;
2779: PetscInt ghost = -1;
2780: PetscInt numChildren, numCells, c;
2782: if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost));
2783: PetscCall(DMIsBoundaryPoint(dm, f, &boundary));
2784: PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL));
2785: if ((ghost >= 0) || boundary || numChildren) continue;
2786: PetscCall(DMPlexGetSupportSize(dm, f, &numCells));
2787: if (numCells == 2) {
2788: PetscCall(DMPlexGetSupport(dm, f, &fcells));
2789: for (c = 0; c < 2; c++) {
2790: PetscInt cell = fcells[c];
2792: if (cell >= cStart && cell < cEndInterior) PetscCall(PetscSectionAddDof(neighSec, cell, 1));
2793: }
2794: }
2795: }
2796: PetscCall(PetscSectionSetUp(neighSec));
2797: PetscCall(PetscSectionGetMaxDof(neighSec, &maxNumFaces));
2798: PetscCall(PetscFVLeastSquaresSetMaxFaces(fvm, maxNumFaces));
2799: nStart = 0;
2800: PetscCall(PetscSectionGetStorageSize(neighSec, &nEnd));
2801: PetscCall(PetscMalloc1((nEnd - nStart), &neighbors));
2802: PetscCall(PetscCalloc1((cEndInterior - cStart), &counter));
2803: for (f = fStart; f < fEnd; f++) {
2804: const PetscInt *fcells;
2805: PetscBool boundary;
2806: PetscInt ghost = -1;
2807: PetscInt numChildren, numCells, c;
2809: if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost));
2810: PetscCall(DMIsBoundaryPoint(dm, f, &boundary));
2811: PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL));
2812: if ((ghost >= 0) || boundary || numChildren) continue;
2813: PetscCall(DMPlexGetSupportSize(dm, f, &numCells));
2814: if (numCells == 2) {
2815: PetscCall(DMPlexGetSupport(dm, f, &fcells));
2816: for (c = 0; c < 2; c++) {
2817: PetscInt cell = fcells[c], off;
2819: if (cell >= cStart && cell < cEndInterior) {
2820: PetscCall(PetscSectionGetOffset(neighSec, cell, &off));
2821: off += counter[cell - cStart]++;
2822: neighbors[off][0] = f;
2823: neighbors[off][1] = fcells[1 - c];
2824: }
2825: }
2826: }
2827: }
2828: PetscCall(PetscFree(counter));
2829: PetscCall(PetscMalloc3(maxNumFaces * dim, &dx, maxNumFaces * dim, &grad, maxNumFaces, &gref));
2830: for (c = cStart; c < cEndInterior; c++) {
2831: PetscInt numFaces, f, d, off, ghost = -1;
2832: PetscFVCellGeom *cg;
2834: PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg));
2835: PetscCall(PetscSectionGetDof(neighSec, c, &numFaces));
2836: PetscCall(PetscSectionGetOffset(neighSec, c, &off));
2838: // do not attempt to compute a gradient reconstruction stencil in a ghost cell. It will never be used
2839: if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, c, &ghost));
2840: if (ghost >= 0) continue;
2842: 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);
2843: for (f = 0; f < numFaces; ++f) {
2844: PetscFVCellGeom *cg1;
2845: PetscFVFaceGeom *fg;
2846: const PetscInt *fcells;
2847: PetscInt ncell, side, nface;
2849: nface = neighbors[off + f][0];
2850: ncell = neighbors[off + f][1];
2851: PetscCall(DMPlexGetSupport(dm, nface, &fcells));
2852: side = (c != fcells[0]);
2853: PetscCall(DMPlexPointLocalRef(dmFace, nface, fgeom, &fg));
2854: PetscCall(DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1));
2855: for (d = 0; d < dim; ++d) dx[f * dim + d] = cg1->centroid[d] - cg->centroid[d];
2856: gref[f] = fg->grad[side]; /* Gradient reconstruction term will go here */
2857: }
2858: PetscCall(PetscFVComputeGradient(fvm, numFaces, dx, grad));
2859: for (f = 0; f < numFaces; ++f) {
2860: for (d = 0; d < dim; ++d) gref[f][d] = grad[f * dim + d];
2861: }
2862: }
2863: PetscCall(PetscFree3(dx, grad, gref));
2864: PetscCall(PetscSectionDestroy(&neighSec));
2865: PetscCall(PetscFree(neighbors));
2866: PetscFunctionReturn(PETSC_SUCCESS);
2867: }
2869: /*@
2870: DMPlexComputeGradientFVM - Compute geometric factors for gradient reconstruction, which are stored in the geometry data, and compute layout for gradient data
2872: Collective on dm
2874: Input Parameters:
2875: + dm - The DM
2876: . fvm - The PetscFV
2877: - cellGeometry - The face geometry from DMPlexComputeCellGeometryFVM()
2879: Input/Output Parameter:
2880: . faceGeometry - The face geometry from DMPlexComputeFaceGeometryFVM(); on output
2881: the geometric factors for gradient calculation are inserted
2883: Output Parameter:
2884: . dmGrad - The DM describing the layout of gradient data
2886: Level: developer
2888: .seealso: `DMPlexGetFaceGeometryFVM()`, `DMPlexGetCellGeometryFVM()`
2889: @*/
2890: PetscErrorCode DMPlexComputeGradientFVM(DM dm, PetscFV fvm, Vec faceGeometry, Vec cellGeometry, DM *dmGrad)
2891: {
2892: DM dmFace, dmCell;
2893: PetscScalar *fgeom, *cgeom;
2894: PetscSection sectionGrad, parentSection;
2895: PetscInt dim, pdim, cStart, cEnd, cEndInterior, c;
2897: PetscFunctionBegin;
2898: PetscCall(DMGetDimension(dm, &dim));
2899: PetscCall(PetscFVGetNumComponents(fvm, &pdim));
2900: PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2901: PetscCall(DMPlexGetGhostCellStratum(dm, &cEndInterior, NULL));
2902: /* Construct the interpolant corresponding to each face from the least-square solution over the cell neighborhood */
2903: PetscCall(VecGetDM(faceGeometry, &dmFace));
2904: PetscCall(VecGetDM(cellGeometry, &dmCell));
2905: PetscCall(VecGetArray(faceGeometry, &fgeom));
2906: PetscCall(VecGetArray(cellGeometry, &cgeom));
2907: PetscCall(DMPlexGetTree(dm, &parentSection, NULL, NULL, NULL, NULL));
2908: if (!parentSection) {
2909: PetscCall(BuildGradientReconstruction_Internal(dm, fvm, dmFace, fgeom, dmCell, cgeom));
2910: } else {
2911: PetscCall(BuildGradientReconstruction_Internal_Tree(dm, fvm, dmFace, fgeom, dmCell, cgeom));
2912: }
2913: PetscCall(VecRestoreArray(faceGeometry, &fgeom));
2914: PetscCall(VecRestoreArray(cellGeometry, &cgeom));
2915: /* Create storage for gradients */
2916: PetscCall(DMClone(dm, dmGrad));
2917: PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), §ionGrad));
2918: PetscCall(PetscSectionSetChart(sectionGrad, cStart, cEnd));
2919: for (c = cStart; c < cEnd; ++c) PetscCall(PetscSectionSetDof(sectionGrad, c, pdim * dim));
2920: PetscCall(PetscSectionSetUp(sectionGrad));
2921: PetscCall(DMSetLocalSection(*dmGrad, sectionGrad));
2922: PetscCall(PetscSectionDestroy(§ionGrad));
2923: PetscFunctionReturn(PETSC_SUCCESS);
2924: }
2926: /*@
2927: DMPlexGetDataFVM - Retrieve precomputed cell geometry
2929: Collective on dm
2931: Input Parameters:
2932: + dm - The DM
2933: - fv - The PetscFV
2935: Output Parameters:
2936: + cellGeometry - The cell geometry
2937: . faceGeometry - The face geometry
2938: - gradDM - The gradient matrices
2940: Level: developer
2942: .seealso: `DMPlexComputeGeometryFVM()`
2943: @*/
2944: PetscErrorCode DMPlexGetDataFVM(DM dm, PetscFV fv, Vec *cellgeom, Vec *facegeom, DM *gradDM)
2945: {
2946: PetscObject cellgeomobj, facegeomobj;
2948: PetscFunctionBegin;
2949: PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_cellgeom_fvm", &cellgeomobj));
2950: if (!cellgeomobj) {
2951: Vec cellgeomInt, facegeomInt;
2953: PetscCall(DMPlexComputeGeometryFVM(dm, &cellgeomInt, &facegeomInt));
2954: PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_cellgeom_fvm", (PetscObject)cellgeomInt));
2955: PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_facegeom_fvm", (PetscObject)facegeomInt));
2956: PetscCall(VecDestroy(&cellgeomInt));
2957: PetscCall(VecDestroy(&facegeomInt));
2958: PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_cellgeom_fvm", &cellgeomobj));
2959: }
2960: PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_facegeom_fvm", &facegeomobj));
2961: if (cellgeom) *cellgeom = (Vec)cellgeomobj;
2962: if (facegeom) *facegeom = (Vec)facegeomobj;
2963: if (gradDM) {
2964: PetscObject gradobj;
2965: PetscBool computeGradients;
2967: PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
2968: if (!computeGradients) {
2969: *gradDM = NULL;
2970: PetscFunctionReturn(PETSC_SUCCESS);
2971: }
2972: PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_dmgrad_fvm", &gradobj));
2973: if (!gradobj) {
2974: DM dmGradInt;
2976: PetscCall(DMPlexComputeGradientFVM(dm, fv, (Vec)facegeomobj, (Vec)cellgeomobj, &dmGradInt));
2977: PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_dmgrad_fvm", (PetscObject)dmGradInt));
2978: PetscCall(DMDestroy(&dmGradInt));
2979: PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_dmgrad_fvm", &gradobj));
2980: }
2981: *gradDM = (DM)gradobj;
2982: }
2983: PetscFunctionReturn(PETSC_SUCCESS);
2984: }
2986: static PetscErrorCode DMPlexCoordinatesToReference_NewtonUpdate(PetscInt dimC, PetscInt dimR, PetscScalar *J, PetscScalar *invJ, PetscScalar *work, PetscReal *resNeg, PetscReal *guess)
2987: {
2988: PetscInt l, m;
2990: PetscFunctionBeginHot;
2991: if (dimC == dimR && dimR <= 3) {
2992: /* invert Jacobian, multiply */
2993: PetscScalar det, idet;
2995: switch (dimR) {
2996: case 1:
2997: invJ[0] = 1. / J[0];
2998: break;
2999: case 2:
3000: det = J[0] * J[3] - J[1] * J[2];
3001: idet = 1. / det;
3002: invJ[0] = J[3] * idet;
3003: invJ[1] = -J[1] * idet;
3004: invJ[2] = -J[2] * idet;
3005: invJ[3] = J[0] * idet;
3006: break;
3007: case 3: {
3008: invJ[0] = J[4] * J[8] - J[5] * J[7];
3009: invJ[1] = J[2] * J[7] - J[1] * J[8];
3010: invJ[2] = J[1] * J[5] - J[2] * J[4];
3011: det = invJ[0] * J[0] + invJ[1] * J[3] + invJ[2] * J[6];
3012: idet = 1. / det;
3013: invJ[0] *= idet;
3014: invJ[1] *= idet;
3015: invJ[2] *= idet;
3016: invJ[3] = idet * (J[5] * J[6] - J[3] * J[8]);
3017: invJ[4] = idet * (J[0] * J[8] - J[2] * J[6]);
3018: invJ[5] = idet * (J[2] * J[3] - J[0] * J[5]);
3019: invJ[6] = idet * (J[3] * J[7] - J[4] * J[6]);
3020: invJ[7] = idet * (J[1] * J[6] - J[0] * J[7]);
3021: invJ[8] = idet * (J[0] * J[4] - J[1] * J[3]);
3022: } break;
3023: }
3024: for (l = 0; l < dimR; l++) {
3025: for (m = 0; m < dimC; m++) guess[l] += PetscRealPart(invJ[l * dimC + m]) * resNeg[m];
3026: }
3027: } else {
3028: #if defined(PETSC_USE_COMPLEX)
3029: char transpose = 'C';
3030: #else
3031: char transpose = 'T';
3032: #endif
3033: PetscBLASInt m = dimR;
3034: PetscBLASInt n = dimC;
3035: PetscBLASInt one = 1;
3036: PetscBLASInt worksize = dimR * dimC, info;
3038: for (l = 0; l < dimC; l++) invJ[l] = resNeg[l];
3040: PetscCallBLAS("LAPACKgels", LAPACKgels_(&transpose, &m, &n, &one, J, &m, invJ, &n, work, &worksize, &info));
3041: PetscCheck(info == 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "Bad argument to GELS");
3043: for (l = 0; l < dimR; l++) guess[l] += PetscRealPart(invJ[l]);
3044: }
3045: PetscFunctionReturn(PETSC_SUCCESS);
3046: }
3048: static PetscErrorCode DMPlexCoordinatesToReference_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[], Vec coords, PetscInt dimC, PetscInt dimR)
3049: {
3050: PetscInt coordSize, i, j, k, l, m, maxIts = 7, numV = (1 << dimR);
3051: PetscScalar *coordsScalar = NULL;
3052: PetscReal *cellData, *cellCoords, *cellCoeffs, *extJ, *resNeg;
3053: PetscScalar *J, *invJ, *work;
3055: PetscFunctionBegin;
3057: PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3058: PetscCheck(coordSize >= dimC * numV, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expecting at least %" PetscInt_FMT " coordinates, got %" PetscInt_FMT, dimC * (1 << dimR), coordSize);
3059: PetscCall(DMGetWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData));
3060: PetscCall(DMGetWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J));
3061: cellCoords = &cellData[0];
3062: cellCoeffs = &cellData[coordSize];
3063: extJ = &cellData[2 * coordSize];
3064: resNeg = &cellData[2 * coordSize + dimR];
3065: invJ = &J[dimR * dimC];
3066: work = &J[2 * dimR * dimC];
3067: if (dimR == 2) {
3068: const PetscInt zToPlex[4] = {0, 1, 3, 2};
3070: for (i = 0; i < 4; i++) {
3071: PetscInt plexI = zToPlex[i];
3073: for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3074: }
3075: } else if (dimR == 3) {
3076: const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
3078: for (i = 0; i < 8; i++) {
3079: PetscInt plexI = zToPlex[i];
3081: for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3082: }
3083: } else {
3084: for (i = 0; i < coordSize; i++) cellCoords[i] = PetscRealPart(coordsScalar[i]);
3085: }
3086: /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */
3087: for (i = 0; i < dimR; i++) {
3088: PetscReal *swap;
3090: for (j = 0; j < (numV / 2); j++) {
3091: for (k = 0; k < dimC; k++) {
3092: cellCoeffs[dimC * j + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]);
3093: cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]);
3094: }
3095: }
3097: if (i < dimR - 1) {
3098: swap = cellCoeffs;
3099: cellCoeffs = cellCoords;
3100: cellCoords = swap;
3101: }
3102: }
3103: PetscCall(PetscArrayzero(refCoords, numPoints * dimR));
3104: for (j = 0; j < numPoints; j++) {
3105: for (i = 0; i < maxIts; i++) {
3106: PetscReal *guess = &refCoords[dimR * j];
3108: /* compute -residual and Jacobian */
3109: for (k = 0; k < dimC; k++) resNeg[k] = realCoords[dimC * j + k];
3110: for (k = 0; k < dimC * dimR; k++) J[k] = 0.;
3111: for (k = 0; k < numV; k++) {
3112: PetscReal extCoord = 1.;
3113: for (l = 0; l < dimR; l++) {
3114: PetscReal coord = guess[l];
3115: PetscInt dep = (k & (1 << l)) >> l;
3117: extCoord *= dep * coord + !dep;
3118: extJ[l] = dep;
3120: for (m = 0; m < dimR; m++) {
3121: PetscReal coord = guess[m];
3122: PetscInt dep = ((k & (1 << m)) >> m) && (m != l);
3123: PetscReal mult = dep * coord + !dep;
3125: extJ[l] *= mult;
3126: }
3127: }
3128: for (l = 0; l < dimC; l++) {
3129: PetscReal coeff = cellCoeffs[dimC * k + l];
3131: resNeg[l] -= coeff * extCoord;
3132: for (m = 0; m < dimR; m++) J[dimR * l + m] += coeff * extJ[m];
3133: }
3134: }
3135: if (0 && PetscDefined(USE_DEBUG)) {
3136: PetscReal maxAbs = 0.;
3138: for (l = 0; l < dimC; l++) maxAbs = PetscMax(maxAbs, PetscAbsReal(resNeg[l]));
3139: PetscCall(PetscInfo(dm, "cell %" PetscInt_FMT ", point %" PetscInt_FMT ", iter %" PetscInt_FMT ": res %g\n", cell, j, i, (double)maxAbs));
3140: }
3142: PetscCall(DMPlexCoordinatesToReference_NewtonUpdate(dimC, dimR, J, invJ, work, resNeg, guess));
3143: }
3144: }
3145: PetscCall(DMRestoreWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J));
3146: PetscCall(DMRestoreWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData));
3147: PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3148: PetscFunctionReturn(PETSC_SUCCESS);
3149: }
3151: static PetscErrorCode DMPlexReferenceToCoordinates_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt dimC, PetscInt dimR)
3152: {
3153: PetscInt coordSize, i, j, k, l, numV = (1 << dimR);
3154: PetscScalar *coordsScalar = NULL;
3155: PetscReal *cellData, *cellCoords, *cellCoeffs;
3157: PetscFunctionBegin;
3159: PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3160: PetscCheck(coordSize >= dimC * numV, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expecting at least %" PetscInt_FMT " coordinates, got %" PetscInt_FMT, dimC * (1 << dimR), coordSize);
3161: PetscCall(DMGetWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData));
3162: cellCoords = &cellData[0];
3163: cellCoeffs = &cellData[coordSize];
3164: if (dimR == 2) {
3165: const PetscInt zToPlex[4] = {0, 1, 3, 2};
3167: for (i = 0; i < 4; i++) {
3168: PetscInt plexI = zToPlex[i];
3170: for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3171: }
3172: } else if (dimR == 3) {
3173: const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6};
3175: for (i = 0; i < 8; i++) {
3176: PetscInt plexI = zToPlex[i];
3178: for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]);
3179: }
3180: } else {
3181: for (i = 0; i < coordSize; i++) cellCoords[i] = PetscRealPart(coordsScalar[i]);
3182: }
3183: /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */
3184: for (i = 0; i < dimR; i++) {
3185: PetscReal *swap;
3187: for (j = 0; j < (numV / 2); j++) {
3188: for (k = 0; k < dimC; k++) {
3189: cellCoeffs[dimC * j + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]);
3190: cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]);
3191: }
3192: }
3194: if (i < dimR - 1) {
3195: swap = cellCoeffs;
3196: cellCoeffs = cellCoords;
3197: cellCoords = swap;
3198: }
3199: }
3200: PetscCall(PetscArrayzero(realCoords, numPoints * dimC));
3201: for (j = 0; j < numPoints; j++) {
3202: const PetscReal *guess = &refCoords[dimR * j];
3203: PetscReal *mapped = &realCoords[dimC * j];
3205: for (k = 0; k < numV; k++) {
3206: PetscReal extCoord = 1.;
3207: for (l = 0; l < dimR; l++) {
3208: PetscReal coord = guess[l];
3209: PetscInt dep = (k & (1 << l)) >> l;
3211: extCoord *= dep * coord + !dep;
3212: }
3213: for (l = 0; l < dimC; l++) {
3214: PetscReal coeff = cellCoeffs[dimC * k + l];
3216: mapped[l] += coeff * extCoord;
3217: }
3218: }
3219: }
3220: PetscCall(DMRestoreWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData));
3221: PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar));
3222: PetscFunctionReturn(PETSC_SUCCESS);
3223: }
3225: /* TODO: TOBY please fix this for Nc > 1 */
3226: static PetscErrorCode DMPlexCoordinatesToReference_FE(DM dm, PetscFE fe, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[], Vec coords, PetscInt Nc, PetscInt dimR)
3227: {
3228: PetscInt numComp, pdim, i, j, k, l, m, maxIter = 7, coordSize;
3229: PetscScalar *nodes = NULL;
3230: PetscReal *invV, *modes;
3231: PetscReal *B, *D, *resNeg;
3232: PetscScalar *J, *invJ, *work;
3234: PetscFunctionBegin;
3235: PetscCall(PetscFEGetDimension(fe, &pdim));
3236: PetscCall(PetscFEGetNumComponents(fe, &numComp));
3237: 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);
3238: PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &nodes));
3239: /* convert nodes to values in the stable evaluation basis */
3240: PetscCall(DMGetWorkArray(dm, pdim, MPIU_REAL, &modes));
3241: invV = fe->invV;
3242: for (i = 0; i < pdim; ++i) {
3243: modes[i] = 0.;
3244: for (j = 0; j < pdim; ++j) modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]);
3245: }
3246: PetscCall(DMGetWorkArray(dm, pdim * Nc + pdim * Nc * dimR + Nc, MPIU_REAL, &B));
3247: D = &B[pdim * Nc];
3248: resNeg = &D[pdim * Nc * dimR];
3249: PetscCall(DMGetWorkArray(dm, 3 * Nc * dimR, MPIU_SCALAR, &J));
3250: invJ = &J[Nc * dimR];
3251: work = &invJ[Nc * dimR];
3252: for (i = 0; i < numPoints * dimR; i++) refCoords[i] = 0.;
3253: for (j = 0; j < numPoints; j++) {
3254: for (i = 0; i < maxIter; i++) { /* we could batch this so that we're not making big B and D arrays all the time */
3255: PetscReal *guess = &refCoords[j * dimR];
3256: PetscCall(PetscSpaceEvaluate(fe->basisSpace, 1, guess, B, D, NULL));
3257: for (k = 0; k < Nc; k++) resNeg[k] = realCoords[j * Nc + k];
3258: for (k = 0; k < Nc * dimR; k++) J[k] = 0.;
3259: for (k = 0; k < pdim; k++) {
3260: for (l = 0; l < Nc; l++) {
3261: resNeg[l] -= modes[k] * B[k * Nc + l];
3262: for (m = 0; m < dimR; m++) J[l * dimR + m] += modes[k] * D[(k * Nc + l) * dimR + m];
3263: }
3264: }
3265: if (0 && PetscDefined(USE_DEBUG)) {
3266: PetscReal maxAbs = 0.;
3268: for (l = 0; l < Nc; l++) maxAbs = PetscMax(maxAbs, PetscAbsReal(resNeg[l]));
3269: PetscCall(PetscInfo(dm, "cell %" PetscInt_FMT ", point %" PetscInt_FMT ", iter %" PetscInt_FMT ": res %g\n", cell, j, i, (double)maxAbs));
3270: }
3271: PetscCall(DMPlexCoordinatesToReference_NewtonUpdate(Nc, dimR, J, invJ, work, resNeg, guess));
3272: }
3273: }
3274: PetscCall(DMRestoreWorkArray(dm, 3 * Nc * dimR, MPIU_SCALAR, &J));
3275: PetscCall(DMRestoreWorkArray(dm, pdim * Nc + pdim * Nc * dimR + Nc, MPIU_REAL, &B));
3276: PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_REAL, &modes));
3277: PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes));
3278: PetscFunctionReturn(PETSC_SUCCESS);
3279: }
3281: /* TODO: TOBY please fix this for Nc > 1 */
3282: static PetscErrorCode DMPlexReferenceToCoordinates_FE(DM dm, PetscFE fe, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt Nc, PetscInt dimR)
3283: {
3284: PetscInt numComp, pdim, i, j, k, l, coordSize;
3285: PetscScalar *nodes = NULL;
3286: PetscReal *invV, *modes;
3287: PetscReal *B;
3289: PetscFunctionBegin;
3290: PetscCall(PetscFEGetDimension(fe, &pdim));
3291: PetscCall(PetscFEGetNumComponents(fe, &numComp));
3292: 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);
3293: PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &nodes));
3294: /* convert nodes to values in the stable evaluation basis */
3295: PetscCall(DMGetWorkArray(dm, pdim, MPIU_REAL, &modes));
3296: invV = fe->invV;
3297: for (i = 0; i < pdim; ++i) {
3298: modes[i] = 0.;
3299: for (j = 0; j < pdim; ++j) modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]);
3300: }
3301: PetscCall(DMGetWorkArray(dm, numPoints * pdim * Nc, MPIU_REAL, &B));
3302: PetscCall(PetscSpaceEvaluate(fe->basisSpace, numPoints, refCoords, B, NULL, NULL));
3303: for (i = 0; i < numPoints * Nc; i++) realCoords[i] = 0.;
3304: for (j = 0; j < numPoints; j++) {
3305: PetscReal *mapped = &realCoords[j * Nc];
3307: for (k = 0; k < pdim; k++) {
3308: for (l = 0; l < Nc; l++) mapped[l] += modes[k] * B[(j * pdim + k) * Nc + l];
3309: }
3310: }
3311: PetscCall(DMRestoreWorkArray(dm, numPoints * pdim * Nc, MPIU_REAL, &B));
3312: PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_REAL, &modes));
3313: PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes));
3314: PetscFunctionReturn(PETSC_SUCCESS);
3315: }
3317: /*@
3318: DMPlexCoordinatesToReference - Pull coordinates back from the mesh to the reference element using a single element
3319: map. This inversion will be accurate inside the reference element, but may be inaccurate for mappings that do not
3320: extend uniquely outside the reference cell (e.g, most non-affine maps)
3322: Not collective
3324: Input Parameters:
3325: + dm - The mesh, with coordinate maps defined either by a PetscDS for the coordinate DM (see DMGetCoordinateDM()) or
3326: implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or
3327: as a multilinear map for tensor-product elements
3328: . cell - the cell whose map is used.
3329: . numPoints - the number of points to locate
3330: - realCoords - (numPoints x coordinate dimension) array of coordinates (see DMGetCoordinateDim())
3332: Output Parameters:
3333: . refCoords - (numPoints x dimension) array of reference coordinates (see DMGetDimension())
3335: Level: intermediate
3337: .seealso: `DMPlexReferenceToCoordinates()`
3338: @*/
3339: PetscErrorCode DMPlexCoordinatesToReference(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[])
3340: {
3341: PetscInt dimC, dimR, depth, cStart, cEnd, i;
3342: DM coordDM = NULL;
3343: Vec coords;
3344: PetscFE fe = NULL;
3346: PetscFunctionBegin;
3348: PetscCall(DMGetDimension(dm, &dimR));
3349: PetscCall(DMGetCoordinateDim(dm, &dimC));
3350: if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(PETSC_SUCCESS);
3351: PetscCall(DMPlexGetDepth(dm, &depth));
3352: PetscCall(DMGetCoordinatesLocal(dm, &coords));
3353: PetscCall(DMGetCoordinateDM(dm, &coordDM));
3354: if (coordDM) {
3355: PetscInt coordFields;
3357: PetscCall(DMGetNumFields(coordDM, &coordFields));
3358: if (coordFields) {
3359: PetscClassId id;
3360: PetscObject disc;
3362: PetscCall(DMGetField(coordDM, 0, NULL, &disc));
3363: PetscCall(PetscObjectGetClassId(disc, &id));
3364: if (id == PETSCFE_CLASSID) fe = (PetscFE)disc;
3365: }
3366: }
3367: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
3368: 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);
3369: if (!fe) { /* implicit discretization: affine or multilinear */
3370: PetscInt coneSize;
3371: PetscBool isSimplex, isTensor;
3373: PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
3374: isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE;
3375: isTensor = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE;
3376: if (isSimplex) {
3377: PetscReal detJ, *v0, *J, *invJ;
3379: PetscCall(DMGetWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3380: J = &v0[dimC];
3381: invJ = &J[dimC * dimC];
3382: PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, invJ, &detJ));
3383: for (i = 0; i < numPoints; i++) { /* Apply the inverse affine transformation for each point */
3384: const PetscReal x0[3] = {-1., -1., -1.};
3386: CoordinatesRealToRef(dimC, dimR, x0, v0, invJ, &realCoords[dimC * i], &refCoords[dimR * i]);
3387: }
3388: PetscCall(DMRestoreWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3389: } else if (isTensor) {
3390: PetscCall(DMPlexCoordinatesToReference_Tensor(coordDM, cell, numPoints, realCoords, refCoords, coords, dimC, dimR));
3391: } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unrecognized cone size %" PetscInt_FMT, coneSize);
3392: } else {
3393: PetscCall(DMPlexCoordinatesToReference_FE(coordDM, fe, cell, numPoints, realCoords, refCoords, coords, dimC, dimR));
3394: }
3395: PetscFunctionReturn(PETSC_SUCCESS);
3396: }
3398: /*@
3399: DMPlexReferenceToCoordinates - Map references coordinates to coordinates in the the mesh for a single element map.
3401: Not collective
3403: Input Parameters:
3404: + dm - The mesh, with coordinate maps defined either by a PetscDS for the coordinate DM (see DMGetCoordinateDM()) or
3405: implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or
3406: as a multilinear map for tensor-product elements
3407: . cell - the cell whose map is used.
3408: . numPoints - the number of points to locate
3409: - refCoords - (numPoints x dimension) array of reference coordinates (see DMGetDimension())
3411: Output Parameters:
3412: . realCoords - (numPoints x coordinate dimension) array of coordinates (see DMGetCoordinateDim())
3414: Level: intermediate
3416: .seealso: `DMPlexCoordinatesToReference()`
3417: @*/
3418: PetscErrorCode DMPlexReferenceToCoordinates(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[])
3419: {
3420: PetscInt dimC, dimR, depth, cStart, cEnd, i;
3421: DM coordDM = NULL;
3422: Vec coords;
3423: PetscFE fe = NULL;
3425: PetscFunctionBegin;
3427: PetscCall(DMGetDimension(dm, &dimR));
3428: PetscCall(DMGetCoordinateDim(dm, &dimC));
3429: if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(PETSC_SUCCESS);
3430: PetscCall(DMPlexGetDepth(dm, &depth));
3431: PetscCall(DMGetCoordinatesLocal(dm, &coords));
3432: PetscCall(DMGetCoordinateDM(dm, &coordDM));
3433: if (coordDM) {
3434: PetscInt coordFields;
3436: PetscCall(DMGetNumFields(coordDM, &coordFields));
3437: if (coordFields) {
3438: PetscClassId id;
3439: PetscObject disc;
3441: PetscCall(DMGetField(coordDM, 0, NULL, &disc));
3442: PetscCall(PetscObjectGetClassId(disc, &id));
3443: if (id == PETSCFE_CLASSID) fe = (PetscFE)disc;
3444: }
3445: }
3446: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
3447: 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);
3448: if (!fe) { /* implicit discretization: affine or multilinear */
3449: PetscInt coneSize;
3450: PetscBool isSimplex, isTensor;
3452: PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
3453: isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE;
3454: isTensor = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE;
3455: if (isSimplex) {
3456: PetscReal detJ, *v0, *J;
3458: PetscCall(DMGetWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3459: J = &v0[dimC];
3460: PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, NULL, &detJ));
3461: for (i = 0; i < numPoints; i++) { /* Apply the affine transformation for each point */
3462: const PetscReal xi0[3] = {-1., -1., -1.};
3464: CoordinatesRefToReal(dimC, dimR, xi0, v0, J, &refCoords[dimR * i], &realCoords[dimC * i]);
3465: }
3466: PetscCall(DMRestoreWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0));
3467: } else if (isTensor) {
3468: PetscCall(DMPlexReferenceToCoordinates_Tensor(coordDM, cell, numPoints, refCoords, realCoords, coords, dimC, dimR));
3469: } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unrecognized cone size %" PetscInt_FMT, coneSize);
3470: } else {
3471: PetscCall(DMPlexReferenceToCoordinates_FE(coordDM, fe, cell, numPoints, refCoords, realCoords, coords, dimC, dimR));
3472: }
3473: PetscFunctionReturn(PETSC_SUCCESS);
3474: }
3476: /*@C
3477: DMPlexRemapGeometry - This function maps the original DM coordinates to new coordinates.
3479: Not collective
3481: Input Parameters:
3482: + dm - The DM
3483: . time - The time
3484: - func - The function transforming current coordinates to new coordaintes
3486: Calling sequence of func:
3487: $ func(PetscInt dim, PetscInt Nf, PetscInt NfAux,
3488: $ const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
3489: $ const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
3490: $ PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f[]);
3492: + dim - The spatial dimension
3493: . Nf - The number of input fields (here 1)
3494: . NfAux - The number of input auxiliary fields
3495: . uOff - The offset of the coordinates in u[] (here 0)
3496: . uOff_x - The offset of the coordinates in u_x[] (here 0)
3497: . u - The coordinate values at this point in space
3498: . u_t - The coordinate time derivative at this point in space (here NULL)
3499: . u_x - The coordinate derivatives at this point in space
3500: . aOff - The offset of each auxiliary field in u[]
3501: . aOff_x - The offset of each auxiliary field in u_x[]
3502: . a - The auxiliary field values at this point in space
3503: . a_t - The auxiliary field time derivative at this point in space (or NULL)
3504: . a_x - The auxiliary field derivatives at this point in space
3505: . t - The current time
3506: . x - The coordinates of this point (here not used)
3507: . numConstants - The number of constants
3508: . constants - The value of each constant
3509: - f - The new coordinates at this point in space
3511: Level: intermediate
3513: .seealso: `DMGetCoordinates()`, `DMGetCoordinatesLocal()`, `DMGetCoordinateDM()`, `DMProjectFieldLocal()`, `DMProjectFieldLabelLocal()`
3514: @*/
3515: 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[]))
3516: {
3517: DM cdm;
3518: DMField cf;
3519: Vec lCoords, tmpCoords;
3521: PetscFunctionBegin;
3522: PetscCall(DMGetCoordinateDM(dm, &cdm));
3523: PetscCall(DMGetCoordinatesLocal(dm, &lCoords));
3524: PetscCall(DMGetLocalVector(cdm, &tmpCoords));
3525: PetscCall(VecCopy(lCoords, tmpCoords));
3526: /* We have to do the coordinate field manually right now since the coordinate DM will not have its own */
3527: PetscCall(DMGetCoordinateField(dm, &cf));
3528: cdm->coordinates[0].field = cf;
3529: PetscCall(DMProjectFieldLocal(cdm, time, tmpCoords, &func, INSERT_VALUES, lCoords));
3530: cdm->coordinates[0].field = NULL;
3531: PetscCall(DMRestoreLocalVector(cdm, &tmpCoords));
3532: PetscCall(DMSetCoordinatesLocal(dm, lCoords));
3533: PetscFunctionReturn(PETSC_SUCCESS);
3534: }
3536: /* Shear applies the transformation, assuming we fix z,
3537: / 1 0 m_0 \
3538: | 0 1 m_1 |
3539: \ 0 0 1 /
3540: */
3541: 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[])
3542: {
3543: const PetscInt Nc = uOff[1] - uOff[0];
3544: const PetscInt ax = (PetscInt)PetscRealPart(constants[0]);
3545: PetscInt c;
3547: for (c = 0; c < Nc; ++c) coords[c] = u[c] + constants[c + 1] * u[ax];
3548: }
3550: /*@C
3551: DMPlexShearGeometry - This shears the domain, meaning adds a multiple of the shear coordinate to all other coordinates.
3553: Not collective
3555: Input Parameters:
3556: + dm - The DM
3557: . direction - The shear coordinate direction, e.g. 0 is the x-axis
3558: - multipliers - The multiplier m for each direction which is not the shear direction
3560: Level: intermediate
3562: .seealso: `DMPlexRemapGeometry()`
3563: @*/
3564: PetscErrorCode DMPlexShearGeometry(DM dm, DMDirection direction, PetscReal multipliers[])
3565: {
3566: DM cdm;
3567: PetscDS cds;
3568: PetscObject obj;
3569: PetscClassId id;
3570: PetscScalar *moduli;
3571: const PetscInt dir = (PetscInt)direction;
3572: PetscInt dE, d, e;
3574: PetscFunctionBegin;
3575: PetscCall(DMGetCoordinateDM(dm, &cdm));
3576: PetscCall(DMGetCoordinateDim(dm, &dE));
3577: PetscCall(PetscMalloc1(dE + 1, &moduli));
3578: moduli[0] = dir;
3579: for (d = 0, e = 0; d < dE; ++d) moduli[d + 1] = d == dir ? 0.0 : (multipliers ? multipliers[e++] : 1.0);
3580: PetscCall(DMGetDS(cdm, &cds));
3581: PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
3582: PetscCall(PetscObjectGetClassId(obj, &id));
3583: if (id != PETSCFE_CLASSID) {
3584: Vec lCoords;
3585: PetscSection cSection;
3586: PetscScalar *coords;
3587: PetscInt vStart, vEnd, v;
3589: PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
3590: PetscCall(DMGetCoordinateSection(dm, &cSection));
3591: PetscCall(DMGetCoordinatesLocal(dm, &lCoords));
3592: PetscCall(VecGetArray(lCoords, &coords));
3593: for (v = vStart; v < vEnd; ++v) {
3594: PetscReal ds;
3595: PetscInt off, c;
3597: PetscCall(PetscSectionGetOffset(cSection, v, &off));
3598: ds = PetscRealPart(coords[off + dir]);
3599: for (c = 0; c < dE; ++c) coords[off + c] += moduli[c] * ds;
3600: }
3601: PetscCall(VecRestoreArray(lCoords, &coords));
3602: } else {
3603: PetscCall(PetscDSSetConstants(cds, dE + 1, moduli));
3604: PetscCall(DMPlexRemapGeometry(dm, 0.0, f0_shear));
3605: }
3606: PetscCall(PetscFree(moduli));
3607: PetscFunctionReturn(PETSC_SUCCESS);
3608: }