1 #include <petsc/private/dmpleximpl.h> /*I "petscdmplex.h" I*/ 2 #include <petsc/private/petscfeimpl.h> /*I "petscfe.h" I*/ 3 #include <petscblaslapack.h> 4 #include <petsctime.h> 5 6 /*@ 7 DMPlexFindVertices - Try to find DAG points based on their coordinates. 8 9 Not Collective (provided DMGetCoordinatesLocalSetUp() has been called already) 10 11 Input Parameters: 12 + dm - The DMPlex object 13 . coordinates - The Vec of coordinates of the sought points 14 - eps - The tolerance or PETSC_DEFAULT 15 16 Output Parameters: 17 . points - The IS of found DAG points or -1 18 19 Level: intermediate 20 21 Notes: 22 The length of Vec coordinates must be npoints * dim where dim is the spatial dimension returned by DMGetCoordinateDim() and npoints is the number of sought points. 23 24 The output IS is living on PETSC_COMM_SELF and its length is npoints. 25 Each rank does the search independently. 26 If this rank's local DMPlex portion contains the DAG point corresponding to the i-th tuple of coordinates, the i-th entry of the output IS is set to that DAG point, otherwise to -1. 27 28 The output IS must be destroyed by user. 29 30 The tolerance is interpreted as the maximum Euclidean (L2) distance of the sought point from the specified coordinates. 31 32 Complexity of this function is currently O(mn) with m number of vertices to find and n number of vertices in the local mesh. This could probably be improved if needed. 33 34 .seealso: `DMPlexCreate()`, `DMGetCoordinatesLocal()` 35 @*/ 36 PetscErrorCode DMPlexFindVertices(DM dm, Vec coordinates, PetscReal eps, IS *points) 37 { 38 PetscInt c, cdim, i, j, o, p, vStart, vEnd; 39 PetscInt npoints; 40 const PetscScalar *coord; 41 Vec allCoordsVec; 42 const PetscScalar *allCoords; 43 PetscInt *dagPoints; 44 45 PetscFunctionBegin; 46 if (eps < 0) eps = PETSC_SQRT_MACHINE_EPSILON; 47 PetscCall(DMGetCoordinateDim(dm, &cdim)); 48 { 49 PetscInt n; 50 51 PetscCall(VecGetLocalSize(coordinates, &n)); 52 PetscCheck(n % cdim == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Given coordinates Vec has local length %" PetscInt_FMT " not divisible by coordinate dimension %" PetscInt_FMT " of given DM", n, cdim); 53 npoints = n / cdim; 54 } 55 PetscCall(DMGetCoordinatesLocal(dm, &allCoordsVec)); 56 PetscCall(VecGetArrayRead(allCoordsVec, &allCoords)); 57 PetscCall(VecGetArrayRead(coordinates, &coord)); 58 PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd)); 59 if (PetscDefined(USE_DEBUG)) { 60 /* check coordinate section is consistent with DM dimension */ 61 PetscSection cs; 62 PetscInt ndof; 63 64 PetscCall(DMGetCoordinateSection(dm, &cs)); 65 for (p = vStart; p < vEnd; p++) { 66 PetscCall(PetscSectionGetDof(cs, p, &ndof)); 67 PetscCheck(ndof == cdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "point %" PetscInt_FMT ": ndof = %" PetscInt_FMT " != %" PetscInt_FMT " = cdim", p, ndof, cdim); 68 } 69 } 70 PetscCall(PetscMalloc1(npoints, &dagPoints)); 71 if (eps == 0.0) { 72 for (i = 0, j = 0; i < npoints; i++, j += cdim) { 73 dagPoints[i] = -1; 74 for (p = vStart, o = 0; p < vEnd; p++, o += cdim) { 75 for (c = 0; c < cdim; c++) { 76 if (coord[j + c] != allCoords[o + c]) break; 77 } 78 if (c == cdim) { 79 dagPoints[i] = p; 80 break; 81 } 82 } 83 } 84 } else { 85 for (i = 0, j = 0; i < npoints; i++, j += cdim) { 86 PetscReal norm; 87 88 dagPoints[i] = -1; 89 for (p = vStart, o = 0; p < vEnd; p++, o += cdim) { 90 norm = 0.0; 91 for (c = 0; c < cdim; c++) norm += PetscRealPart(PetscSqr(coord[j + c] - allCoords[o + c])); 92 norm = PetscSqrtReal(norm); 93 if (norm <= eps) { 94 dagPoints[i] = p; 95 break; 96 } 97 } 98 } 99 } 100 PetscCall(VecRestoreArrayRead(allCoordsVec, &allCoords)); 101 PetscCall(VecRestoreArrayRead(coordinates, &coord)); 102 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, npoints, dagPoints, PETSC_OWN_POINTER, points)); 103 PetscFunctionReturn(PETSC_SUCCESS); 104 } 105 106 static PetscErrorCode DMPlexGetLineIntersection_2D_Internal(const PetscReal segmentA[], const PetscReal segmentB[], PetscReal intersection[], PetscBool *hasIntersection) 107 { 108 const PetscReal p0_x = segmentA[0 * 2 + 0]; 109 const PetscReal p0_y = segmentA[0 * 2 + 1]; 110 const PetscReal p1_x = segmentA[1 * 2 + 0]; 111 const PetscReal p1_y = segmentA[1 * 2 + 1]; 112 const PetscReal p2_x = segmentB[0 * 2 + 0]; 113 const PetscReal p2_y = segmentB[0 * 2 + 1]; 114 const PetscReal p3_x = segmentB[1 * 2 + 0]; 115 const PetscReal p3_y = segmentB[1 * 2 + 1]; 116 const PetscReal s1_x = p1_x - p0_x; 117 const PetscReal s1_y = p1_y - p0_y; 118 const PetscReal s2_x = p3_x - p2_x; 119 const PetscReal s2_y = p3_y - p2_y; 120 const PetscReal denom = (-s2_x * s1_y + s1_x * s2_y); 121 122 PetscFunctionBegin; 123 *hasIntersection = PETSC_FALSE; 124 /* Non-parallel lines */ 125 if (denom != 0.0) { 126 const PetscReal s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / denom; 127 const PetscReal t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / denom; 128 129 if (s >= 0 && s <= 1 && t >= 0 && t <= 1) { 130 *hasIntersection = PETSC_TRUE; 131 if (intersection) { 132 intersection[0] = p0_x + (t * s1_x); 133 intersection[1] = p0_y + (t * s1_y); 134 } 135 } 136 } 137 PetscFunctionReturn(PETSC_SUCCESS); 138 } 139 140 /* The plane is segmentB x segmentC: https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection */ 141 static PetscErrorCode DMPlexGetLinePlaneIntersection_3D_Internal(const PetscReal segmentA[], const PetscReal segmentB[], const PetscReal segmentC[], PetscReal intersection[], PetscBool *hasIntersection) 142 { 143 const PetscReal p0_x = segmentA[0 * 3 + 0]; 144 const PetscReal p0_y = segmentA[0 * 3 + 1]; 145 const PetscReal p0_z = segmentA[0 * 3 + 2]; 146 const PetscReal p1_x = segmentA[1 * 3 + 0]; 147 const PetscReal p1_y = segmentA[1 * 3 + 1]; 148 const PetscReal p1_z = segmentA[1 * 3 + 2]; 149 const PetscReal q0_x = segmentB[0 * 3 + 0]; 150 const PetscReal q0_y = segmentB[0 * 3 + 1]; 151 const PetscReal q0_z = segmentB[0 * 3 + 2]; 152 const PetscReal q1_x = segmentB[1 * 3 + 0]; 153 const PetscReal q1_y = segmentB[1 * 3 + 1]; 154 const PetscReal q1_z = segmentB[1 * 3 + 2]; 155 const PetscReal r0_x = segmentC[0 * 3 + 0]; 156 const PetscReal r0_y = segmentC[0 * 3 + 1]; 157 const PetscReal r0_z = segmentC[0 * 3 + 2]; 158 const PetscReal r1_x = segmentC[1 * 3 + 0]; 159 const PetscReal r1_y = segmentC[1 * 3 + 1]; 160 const PetscReal r1_z = segmentC[1 * 3 + 2]; 161 const PetscReal s0_x = p1_x - p0_x; 162 const PetscReal s0_y = p1_y - p0_y; 163 const PetscReal s0_z = p1_z - p0_z; 164 const PetscReal s1_x = q1_x - q0_x; 165 const PetscReal s1_y = q1_y - q0_y; 166 const PetscReal s1_z = q1_z - q0_z; 167 const PetscReal s2_x = r1_x - r0_x; 168 const PetscReal s2_y = r1_y - r0_y; 169 const PetscReal s2_z = r1_z - r0_z; 170 const PetscReal s3_x = s1_y * s2_z - s1_z * s2_y; /* s1 x s2 */ 171 const PetscReal s3_y = s1_z * s2_x - s1_x * s2_z; 172 const PetscReal s3_z = s1_x * s2_y - s1_y * s2_x; 173 const PetscReal s4_x = s0_y * s2_z - s0_z * s2_y; /* s0 x s2 */ 174 const PetscReal s4_y = s0_z * s2_x - s0_x * s2_z; 175 const PetscReal s4_z = s0_x * s2_y - s0_y * s2_x; 176 const PetscReal s5_x = s1_y * s0_z - s1_z * s0_y; /* s1 x s0 */ 177 const PetscReal s5_y = s1_z * s0_x - s1_x * s0_z; 178 const PetscReal s5_z = s1_x * s0_y - s1_y * s0_x; 179 const PetscReal denom = -(s0_x * s3_x + s0_y * s3_y + s0_z * s3_z); /* -s0 . (s1 x s2) */ 180 181 PetscFunctionBegin; 182 *hasIntersection = PETSC_FALSE; 183 /* Line not parallel to plane */ 184 if (denom != 0.0) { 185 const PetscReal t = (s3_x * (p0_x - q0_x) + s3_y * (p0_y - q0_y) + s3_z * (p0_z - q0_z)) / denom; 186 const PetscReal u = (s4_x * (p0_x - q0_x) + s4_y * (p0_y - q0_y) + s4_z * (p0_z - q0_z)) / denom; 187 const PetscReal v = (s5_x * (p0_x - q0_x) + s5_y * (p0_y - q0_y) + s5_z * (p0_z - q0_z)) / denom; 188 189 if (t >= 0 && t <= 1 && u >= 0 && u <= 1 && v >= 0 && v <= 1) { 190 *hasIntersection = PETSC_TRUE; 191 if (intersection) { 192 intersection[0] = p0_x + (t * s0_x); 193 intersection[1] = p0_y + (t * s0_y); 194 intersection[2] = p0_z + (t * s0_z); 195 } 196 } 197 } 198 PetscFunctionReturn(PETSC_SUCCESS); 199 } 200 201 static PetscErrorCode DMPlexLocatePoint_Simplex_1D_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscInt *cell) 202 { 203 const PetscReal eps = PETSC_SQRT_MACHINE_EPSILON; 204 const PetscReal x = PetscRealPart(point[0]); 205 PetscReal v0, J, invJ, detJ; 206 PetscReal xi; 207 208 PetscFunctionBegin; 209 PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, &v0, &J, &invJ, &detJ)); 210 xi = invJ * (x - v0); 211 212 if ((xi >= -eps) && (xi <= 2. + eps)) *cell = c; 213 else *cell = DMLOCATEPOINT_POINT_NOT_FOUND; 214 PetscFunctionReturn(PETSC_SUCCESS); 215 } 216 217 static PetscErrorCode DMPlexLocatePoint_Simplex_2D_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscInt *cell) 218 { 219 const PetscInt embedDim = 2; 220 const PetscReal eps = PETSC_SQRT_MACHINE_EPSILON; 221 PetscReal x = PetscRealPart(point[0]); 222 PetscReal y = PetscRealPart(point[1]); 223 PetscReal v0[2], J[4], invJ[4], detJ; 224 PetscReal xi, eta; 225 226 PetscFunctionBegin; 227 PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v0, J, invJ, &detJ)); 228 xi = invJ[0 * embedDim + 0] * (x - v0[0]) + invJ[0 * embedDim + 1] * (y - v0[1]); 229 eta = invJ[1 * embedDim + 0] * (x - v0[0]) + invJ[1 * embedDim + 1] * (y - v0[1]); 230 231 if ((xi >= -eps) && (eta >= -eps) && (xi + eta <= 2.0 + eps)) *cell = c; 232 else *cell = DMLOCATEPOINT_POINT_NOT_FOUND; 233 PetscFunctionReturn(PETSC_SUCCESS); 234 } 235 236 static PetscErrorCode DMPlexClosestPoint_Simplex_2D_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscReal cpoint[]) 237 { 238 const PetscInt embedDim = 2; 239 PetscReal x = PetscRealPart(point[0]); 240 PetscReal y = PetscRealPart(point[1]); 241 PetscReal v0[2], J[4], invJ[4], detJ; 242 PetscReal xi, eta, r; 243 244 PetscFunctionBegin; 245 PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v0, J, invJ, &detJ)); 246 xi = invJ[0 * embedDim + 0] * (x - v0[0]) + invJ[0 * embedDim + 1] * (y - v0[1]); 247 eta = invJ[1 * embedDim + 0] * (x - v0[0]) + invJ[1 * embedDim + 1] * (y - v0[1]); 248 249 xi = PetscMax(xi, 0.0); 250 eta = PetscMax(eta, 0.0); 251 if (xi + eta > 2.0) { 252 r = (xi + eta) / 2.0; 253 xi /= r; 254 eta /= r; 255 } 256 cpoint[0] = J[0 * embedDim + 0] * xi + J[0 * embedDim + 1] * eta + v0[0]; 257 cpoint[1] = J[1 * embedDim + 0] * xi + J[1 * embedDim + 1] * eta + v0[1]; 258 PetscFunctionReturn(PETSC_SUCCESS); 259 } 260 261 static PetscErrorCode DMPlexLocatePoint_Quad_2D_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscInt *cell) 262 { 263 const PetscScalar *array; 264 PetscScalar *coords = NULL; 265 const PetscInt faces[8] = {0, 1, 1, 2, 2, 3, 3, 0}; 266 PetscReal x = PetscRealPart(point[0]); 267 PetscReal y = PetscRealPart(point[1]); 268 PetscInt crossings = 0, numCoords, f; 269 PetscBool isDG; 270 271 PetscFunctionBegin; 272 PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords)); 273 PetscCheck(numCoords == 8, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Quadrilateral should have 8 coordinates, not %" PetscInt_FMT, numCoords); 274 for (f = 0; f < 4; ++f) { 275 PetscReal x_i = PetscRealPart(coords[faces[2 * f + 0] * 2 + 0]); 276 PetscReal y_i = PetscRealPart(coords[faces[2 * f + 0] * 2 + 1]); 277 PetscReal x_j = PetscRealPart(coords[faces[2 * f + 1] * 2 + 0]); 278 PetscReal y_j = PetscRealPart(coords[faces[2 * f + 1] * 2 + 1]); 279 PetscReal slope = (y_j - y_i) / (x_j - x_i); 280 PetscBool cond1 = (x_i <= x) && (x < x_j) ? PETSC_TRUE : PETSC_FALSE; 281 PetscBool cond2 = (x_j <= x) && (x < x_i) ? PETSC_TRUE : PETSC_FALSE; 282 PetscBool above = (y < slope * (x - x_i) + y_i) ? PETSC_TRUE : PETSC_FALSE; 283 if ((cond1 || cond2) && above) ++crossings; 284 } 285 if (crossings % 2) *cell = c; 286 else *cell = DMLOCATEPOINT_POINT_NOT_FOUND; 287 PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords)); 288 PetscFunctionReturn(PETSC_SUCCESS); 289 } 290 291 static PetscErrorCode DMPlexLocatePoint_Simplex_3D_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscInt *cell) 292 { 293 const PetscInt embedDim = 3; 294 const PetscReal eps = PETSC_SQRT_MACHINE_EPSILON; 295 PetscReal v0[3], J[9], invJ[9], detJ; 296 PetscReal x = PetscRealPart(point[0]); 297 PetscReal y = PetscRealPart(point[1]); 298 PetscReal z = PetscRealPart(point[2]); 299 PetscReal xi, eta, zeta; 300 301 PetscFunctionBegin; 302 PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v0, J, invJ, &detJ)); 303 xi = invJ[0 * embedDim + 0] * (x - v0[0]) + invJ[0 * embedDim + 1] * (y - v0[1]) + invJ[0 * embedDim + 2] * (z - v0[2]); 304 eta = invJ[1 * embedDim + 0] * (x - v0[0]) + invJ[1 * embedDim + 1] * (y - v0[1]) + invJ[1 * embedDim + 2] * (z - v0[2]); 305 zeta = invJ[2 * embedDim + 0] * (x - v0[0]) + invJ[2 * embedDim + 1] * (y - v0[1]) + invJ[2 * embedDim + 2] * (z - v0[2]); 306 307 if ((xi >= -eps) && (eta >= -eps) && (zeta >= -eps) && (xi + eta + zeta <= 2.0 + eps)) *cell = c; 308 else *cell = DMLOCATEPOINT_POINT_NOT_FOUND; 309 PetscFunctionReturn(PETSC_SUCCESS); 310 } 311 312 static PetscErrorCode DMPlexLocatePoint_General_3D_Internal(DM dm, const PetscScalar point[], PetscInt c, PetscInt *cell) 313 { 314 const PetscScalar *array; 315 PetscScalar *coords = NULL; 316 const PetscInt faces[24] = {0, 3, 2, 1, 5, 4, 7, 6, 3, 0, 4, 5, 1, 2, 6, 7, 3, 5, 6, 2, 0, 1, 7, 4}; 317 PetscBool found = PETSC_TRUE; 318 PetscInt numCoords, f; 319 PetscBool isDG; 320 321 PetscFunctionBegin; 322 PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords)); 323 PetscCheck(numCoords == 24, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Quadrilateral should have 8 coordinates, not %" PetscInt_FMT, numCoords); 324 for (f = 0; f < 6; ++f) { 325 /* Check the point is under plane */ 326 /* Get face normal */ 327 PetscReal v_i[3]; 328 PetscReal v_j[3]; 329 PetscReal normal[3]; 330 PetscReal pp[3]; 331 PetscReal dot; 332 333 v_i[0] = PetscRealPart(coords[faces[f * 4 + 3] * 3 + 0] - coords[faces[f * 4 + 0] * 3 + 0]); 334 v_i[1] = PetscRealPart(coords[faces[f * 4 + 3] * 3 + 1] - coords[faces[f * 4 + 0] * 3 + 1]); 335 v_i[2] = PetscRealPart(coords[faces[f * 4 + 3] * 3 + 2] - coords[faces[f * 4 + 0] * 3 + 2]); 336 v_j[0] = PetscRealPart(coords[faces[f * 4 + 1] * 3 + 0] - coords[faces[f * 4 + 0] * 3 + 0]); 337 v_j[1] = PetscRealPart(coords[faces[f * 4 + 1] * 3 + 1] - coords[faces[f * 4 + 0] * 3 + 1]); 338 v_j[2] = PetscRealPart(coords[faces[f * 4 + 1] * 3 + 2] - coords[faces[f * 4 + 0] * 3 + 2]); 339 normal[0] = v_i[1] * v_j[2] - v_i[2] * v_j[1]; 340 normal[1] = v_i[2] * v_j[0] - v_i[0] * v_j[2]; 341 normal[2] = v_i[0] * v_j[1] - v_i[1] * v_j[0]; 342 pp[0] = PetscRealPart(coords[faces[f * 4 + 0] * 3 + 0] - point[0]); 343 pp[1] = PetscRealPart(coords[faces[f * 4 + 0] * 3 + 1] - point[1]); 344 pp[2] = PetscRealPart(coords[faces[f * 4 + 0] * 3 + 2] - point[2]); 345 dot = normal[0] * pp[0] + normal[1] * pp[1] + normal[2] * pp[2]; 346 347 /* Check that projected point is in face (2D location problem) */ 348 if (dot < 0.0) { 349 found = PETSC_FALSE; 350 break; 351 } 352 } 353 if (found) *cell = c; 354 else *cell = DMLOCATEPOINT_POINT_NOT_FOUND; 355 PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &coords)); 356 PetscFunctionReturn(PETSC_SUCCESS); 357 } 358 359 static PetscErrorCode PetscGridHashInitialize_Internal(PetscGridHash box, PetscInt dim, const PetscScalar point[]) 360 { 361 PetscInt d; 362 363 PetscFunctionBegin; 364 box->dim = dim; 365 for (d = 0; d < dim; ++d) box->lower[d] = box->upper[d] = PetscRealPart(point[d]); 366 PetscFunctionReturn(PETSC_SUCCESS); 367 } 368 369 PetscErrorCode PetscGridHashCreate(MPI_Comm comm, PetscInt dim, const PetscScalar point[], PetscGridHash *box) 370 { 371 PetscFunctionBegin; 372 PetscCall(PetscMalloc1(1, box)); 373 PetscCall(PetscGridHashInitialize_Internal(*box, dim, point)); 374 PetscFunctionReturn(PETSC_SUCCESS); 375 } 376 377 PetscErrorCode PetscGridHashEnlarge(PetscGridHash box, const PetscScalar point[]) 378 { 379 PetscInt d; 380 381 PetscFunctionBegin; 382 for (d = 0; d < box->dim; ++d) { 383 box->lower[d] = PetscMin(box->lower[d], PetscRealPart(point[d])); 384 box->upper[d] = PetscMax(box->upper[d], PetscRealPart(point[d])); 385 } 386 PetscFunctionReturn(PETSC_SUCCESS); 387 } 388 389 /* 390 PetscGridHashSetGrid - Divide the grid into boxes 391 392 Not collective 393 394 Input Parameters: 395 + box - The grid hash object 396 . n - The number of boxes in each dimension, or PETSC_DETERMINE 397 - h - The box size in each dimension, only used if n[d] == PETSC_DETERMINE 398 399 Level: developer 400 401 .seealso: `PetscGridHashCreate()` 402 */ 403 PetscErrorCode PetscGridHashSetGrid(PetscGridHash box, const PetscInt n[], const PetscReal h[]) 404 { 405 PetscInt d; 406 407 PetscFunctionBegin; 408 for (d = 0; d < box->dim; ++d) { 409 box->extent[d] = box->upper[d] - box->lower[d]; 410 if (n[d] == PETSC_DETERMINE) { 411 box->h[d] = h[d]; 412 box->n[d] = PetscCeilReal(box->extent[d] / h[d]); 413 } else { 414 box->n[d] = n[d]; 415 box->h[d] = box->extent[d] / n[d]; 416 } 417 } 418 PetscFunctionReturn(PETSC_SUCCESS); 419 } 420 421 /* 422 PetscGridHashGetEnclosingBox - Find the grid boxes containing each input point 423 424 Not collective 425 426 Input Parameters: 427 + box - The grid hash object 428 . numPoints - The number of input points 429 - points - The input point coordinates 430 431 Output Parameters: 432 + dboxes - An array of numPoints*dim integers expressing the enclosing box as (i_0, i_1, ..., i_dim) 433 - boxes - An array of numPoints integers expressing the enclosing box as single number, or NULL 434 435 Level: developer 436 437 Note: 438 This only guarantees that a box contains a point, not that a cell does. 439 440 .seealso: `PetscGridHashCreate()` 441 */ 442 PetscErrorCode PetscGridHashGetEnclosingBox(PetscGridHash box, PetscInt numPoints, const PetscScalar points[], PetscInt dboxes[], PetscInt boxes[]) 443 { 444 const PetscReal *lower = box->lower; 445 const PetscReal *upper = box->upper; 446 const PetscReal *h = box->h; 447 const PetscInt *n = box->n; 448 const PetscInt dim = box->dim; 449 PetscInt d, p; 450 451 PetscFunctionBegin; 452 for (p = 0; p < numPoints; ++p) { 453 for (d = 0; d < dim; ++d) { 454 PetscInt dbox = PetscFloorReal((PetscRealPart(points[p * dim + d]) - lower[d]) / h[d]); 455 456 if (dbox == n[d] && PetscAbsReal(PetscRealPart(points[p * dim + d]) - upper[d]) < 1.0e-9) dbox = n[d] - 1; 457 if (dbox == -1 && PetscAbsReal(PetscRealPart(points[p * dim + d]) - lower[d]) < 1.0e-9) dbox = 0; 458 PetscCheck(dbox >= 0 && dbox<n[d], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Input point %" PetscInt_FMT " (%g, %g, %g) is outside of our bounding box", p, (double)PetscRealPart(points[p * dim + 0]), dim> 1 ? (double)PetscRealPart(points[p * dim + 1]) : 0.0, dim > 2 ? (double)PetscRealPart(points[p * dim + 2]) : 0.0); 459 dboxes[p * dim + d] = dbox; 460 } 461 if (boxes) 462 for (d = dim - 2, boxes[p] = dboxes[p * dim + dim - 1]; d >= 0; --d) boxes[p] = boxes[p] * n[d] + dboxes[p * dim + d]; 463 } 464 PetscFunctionReturn(PETSC_SUCCESS); 465 } 466 467 /* 468 PetscGridHashGetEnclosingBoxQuery - Find the grid boxes containing each input point 469 470 Not collective 471 472 Input Parameters: 473 + box - The grid hash object 474 . cellSection - The PetscSection mapping cells to boxes 475 . numPoints - The number of input points 476 - points - The input point coordinates 477 478 Output Parameters: 479 + dboxes - An array of numPoints*dim integers expressing the enclosing box as (i_0, i_1, ..., i_dim) 480 . boxes - An array of numPoints integers expressing the enclosing box as single number, or NULL 481 - found - Flag indicating if point was located within a box 482 483 Level: developer 484 485 Note: 486 This does an additional check that a cell actually contains the point, and found is PETSC_FALSE if no cell does. Thus, this function requires that the cellSection is already constructed. 487 488 .seealso: `PetscGridHashGetEnclosingBox()` 489 */ 490 PetscErrorCode PetscGridHashGetEnclosingBoxQuery(PetscGridHash box, PetscSection cellSection, PetscInt numPoints, const PetscScalar points[], PetscInt dboxes[], PetscInt boxes[], PetscBool *found) 491 { 492 const PetscReal *lower = box->lower; 493 const PetscReal *upper = box->upper; 494 const PetscReal *h = box->h; 495 const PetscInt *n = box->n; 496 const PetscInt dim = box->dim; 497 PetscInt bStart, bEnd, d, p; 498 499 PetscFunctionBegin; 500 PetscValidHeaderSpecific(cellSection, PETSC_SECTION_CLASSID, 2); 501 *found = PETSC_FALSE; 502 PetscCall(PetscSectionGetChart(box->cellSection, &bStart, &bEnd)); 503 for (p = 0; p < numPoints; ++p) { 504 for (d = 0; d < dim; ++d) { 505 PetscInt dbox = PetscFloorReal((PetscRealPart(points[p * dim + d]) - lower[d]) / h[d]); 506 507 if (dbox == n[d] && PetscAbsReal(PetscRealPart(points[p * dim + d]) - upper[d]) < 1.0e-9) dbox = n[d] - 1; 508 if (dbox < 0 || dbox >= n[d]) PetscFunctionReturn(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 } 519 520 PetscErrorCode PetscGridHashDestroy(PetscGridHash *box) 521 { 522 PetscFunctionBegin; 523 if (*box) { 524 PetscCall(PetscSectionDestroy(&(*box)->cellSection)); 525 PetscCall(ISDestroy(&(*box)->cells)); 526 PetscCall(DMLabelDestroy(&(*box)->cellsSparse)); 527 } 528 PetscCall(PetscFree(*box)); 529 PetscFunctionReturn(PETSC_SUCCESS); 530 } 531 532 PetscErrorCode DMPlexLocatePoint_Internal(DM dm, PetscInt dim, const PetscScalar point[], PetscInt cellStart, PetscInt *cell) 533 { 534 DMPolytopeType ct; 535 536 PetscFunctionBegin; 537 PetscCall(DMPlexGetCellType(dm, cellStart, &ct)); 538 switch (ct) { 539 case DM_POLYTOPE_SEGMENT: 540 PetscCall(DMPlexLocatePoint_Simplex_1D_Internal(dm, point, cellStart, cell)); 541 break; 542 case DM_POLYTOPE_TRIANGLE: 543 PetscCall(DMPlexLocatePoint_Simplex_2D_Internal(dm, point, cellStart, cell)); 544 break; 545 case DM_POLYTOPE_QUADRILATERAL: 546 PetscCall(DMPlexLocatePoint_Quad_2D_Internal(dm, point, cellStart, cell)); 547 break; 548 case DM_POLYTOPE_TETRAHEDRON: 549 PetscCall(DMPlexLocatePoint_Simplex_3D_Internal(dm, point, cellStart, cell)); 550 break; 551 case DM_POLYTOPE_HEXAHEDRON: 552 PetscCall(DMPlexLocatePoint_General_3D_Internal(dm, point, cellStart, cell)); 553 break; 554 default: 555 SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No point location for cell %" PetscInt_FMT " with type %s", cellStart, DMPolytopeTypes[ct]); 556 } 557 PetscFunctionReturn(PETSC_SUCCESS); 558 } 559 560 /* 561 DMPlexClosestPoint_Internal - Returns the closest point in the cell to the given point 562 */ 563 PetscErrorCode DMPlexClosestPoint_Internal(DM dm, PetscInt dim, const PetscScalar point[], PetscInt cell, PetscReal cpoint[]) 564 { 565 DMPolytopeType ct; 566 567 PetscFunctionBegin; 568 PetscCall(DMPlexGetCellType(dm, cell, &ct)); 569 switch (ct) { 570 case DM_POLYTOPE_TRIANGLE: 571 PetscCall(DMPlexClosestPoint_Simplex_2D_Internal(dm, point, cell, cpoint)); 572 break; 573 #if 0 574 case DM_POLYTOPE_QUADRILATERAL: 575 PetscCall(DMPlexClosestPoint_General_2D_Internal(dm, point, cell, cpoint));break; 576 case DM_POLYTOPE_TETRAHEDRON: 577 PetscCall(DMPlexClosestPoint_Simplex_3D_Internal(dm, point, cell, cpoint));break; 578 case DM_POLYTOPE_HEXAHEDRON: 579 PetscCall(DMPlexClosestPoint_General_3D_Internal(dm, point, cell, cpoint));break; 580 #endif 581 default: 582 SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No closest point location for cell %" PetscInt_FMT " with type %s", cell, DMPolytopeTypes[ct]); 583 } 584 PetscFunctionReturn(PETSC_SUCCESS); 585 } 586 587 /* 588 DMPlexComputeGridHash_Internal - Create a grid hash structure covering the Plex 589 590 Collective on dm 591 592 Input Parameter: 593 . dm - The Plex 594 595 Output Parameter: 596 . localBox - The grid hash object 597 598 Level: developer 599 600 .seealso: `PetscGridHashCreate()`, `PetscGridHashGetEnclosingBox()` 601 */ 602 PetscErrorCode DMPlexComputeGridHash_Internal(DM dm, PetscGridHash *localBox) 603 { 604 PetscInt debug = ((DM_Plex *)dm->data)->printLocate; 605 MPI_Comm comm; 606 PetscGridHash lbox; 607 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; 618 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; 668 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; 677 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; 709 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.}; 732 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; 752 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 } 798 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}; 815 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; 835 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; 867 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 } 885 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 } 901 902 if (hash) { 903 PetscBool found_box; 904 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; 929 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; 949 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 } 996 997 /*@C 998 DMPlexComputeProjection2Dto1D - Rewrite coordinates to be the 1D projection of the 2D coordinates 999 1000 Not collective 1001 1002 Input/Output Parameter: 1003 . coords - The coordinates of a segment, on output the new y-coordinate, and 0 for x 1004 1005 Output Parameter: 1006 . R - The rotation which accomplishes the projection 1007 1008 Level: developer 1009 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; 1017 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 } 1027 1028 /*@C 1029 DMPlexComputeProjection3Dto1D - Rewrite coordinates to be the 1D projection of the 3D coordinates 1030 1031 Not collective 1032 1033 Input/Output Parameter: 1034 . coords - The coordinates of a segment; on output, the new y-coordinate, and 0 for x and z 1035 1036 Output Parameter: 1037 . R - The rotation which accomplishes the projection 1038 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 1040 1041 Level: developer 1042 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; 1053 1054 x *= rinv; 1055 y *= rinv; 1056 z *= rinv; 1057 if (x > 0.) { 1058 PetscReal inv1pX = 1. / (1. + x); 1059 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); 1071 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 } 1086 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. 1090 1091 Not collective 1092 1093 Input Parameter: 1094 . coordSize - Length of coordinate array (3x number of points); must be at least 9 (3 points) 1095 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 1099 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. 1102 1103 Level: developer 1104 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; 1112 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 } 1144 1145 PETSC_UNUSED static inline void Volume_Triangle_Internal(PetscReal *vol, PetscReal coords[]) 1146 { 1147 /* Signed volume is 1/2 the determinant 1148 1149 | 1 1 1 | 1150 | x0 x1 x2 | 1151 | y0 y1 y2 | 1152 1153 but if x0,y0 is the origin, we have 1154 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 } 1169 1170 PETSC_UNUSED static inline void Volume_Tetrahedron_Internal(PetscReal *vol, PetscReal coords[]) 1171 { 1172 /* Signed volume is 1/6th of the determinant 1173 1174 | 1 1 1 1 | 1175 | x0 x1 x2 x3 | 1176 | y0 y1 y2 y3 | 1177 | z0 z1 z2 z3 | 1178 1179 but if x0,y0,z0 is the origin, we have 1180 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 } 1203 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 } 1210 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; 1217 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 } 1240 1241 /*@C 1242 DMPlexGetCellCoordinates - Get coordinates for a cell, taking into account periodicity 1243 1244 Not collective 1245 1246 Input Parameters: 1247 + dm - The DM 1248 - cell - The cell number 1249 1250 Output Parameters: 1251 + isDG - Using cellwise coordinates 1252 . Nc - The number of coordinates 1253 . array - The coordinate array 1254 - coords - The cell coordinates 1255 1256 Level: developer 1257 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; 1267 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 } 1302 1303 /*@C 1304 DMPlexRestoreCellCoordinates - Get coordinates for a cell, taking into account periodicity 1305 1306 Not collective 1307 1308 Input Parameters: 1309 + dm - The DM 1310 - cell - The cell number 1311 1312 Output Parameters: 1313 + isDG - Using cellwise coordinates 1314 . Nc - The number of coordinates 1315 . array - The coordinate array 1316 - coords - The cell coordinates 1317 1318 Level: developer 1319 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; 1327 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 } 1340 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; 1347 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; 1355 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; 1377 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; 1393 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 } 1410 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; 1417 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}; 1425 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; 1432 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; 1449 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 } 1465 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; 1472 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}; 1478 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}; 1487 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; 1494 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; 1512 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; 1533 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]; 1545 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]; 1562 1563 if (v) { 1564 PetscReal extPoint[4]; 1565 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.; 1572 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]; 1579 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.; 1591 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; 1600 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 } 1620 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; 1628 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 } 1650 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; 1658 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; 1685 1686 for (i = 0; i < Nv; i++) { 1687 PetscInt zi = zToPlex[i]; 1688 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]; 1703 1704 if (v) { 1705 PetscReal extPoint[8]; 1706 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.; 1717 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]; 1724 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; 1749 1750 for (j = 0; j < dim; j++) { 1751 for (k = 0; k < dimR; k++) { 1752 PetscReal val = 0.; 1753 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 } 1766 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; 1774 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; 1801 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) 1809 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) 1813 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 / 1817 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]; 1851 1852 if (v) { 1853 PetscReal extPoint[6]; 1854 PetscInt c; 1855 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.; 1864 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]; 1871 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; 1890 1891 for (j = 0; j < dim; j++) { 1892 for (k = 0; k < dimR; k++) { 1893 PetscReal val = 0.; 1894 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 } 1907 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; 1917 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; 1974 1975 for (i = 0, k = 0; i < Nq; i++) { 1976 PetscInt j; 1977 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; 1998 1999 for (j = 0; j < coordDim * coordDim; j++, k++) invJ[k] = invJ[j]; 2000 } 2001 } 2002 } 2003 PetscFunctionReturn(PETSC_SUCCESS); 2004 } 2005 2006 /*@C 2007 DMPlexComputeCellGeometryAffineFEM - Assuming an affine map, compute the Jacobian, inverse Jacobian, and Jacobian determinant for a given cell 2008 2009 Collective on dm 2010 2011 Input Parameters: 2012 + dm - the DM 2013 - cell - the cell 2014 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 2020 2021 Level: advanced 2022 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. 2026 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 } 2035 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; 2046 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; 2053 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; 2074 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; 2085 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; 2097 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 } 2133 2134 /*@C 2135 DMPlexComputeCellGeometryFEM - Compute the Jacobian, inverse Jacobian, and Jacobian determinant at each quadrature point in the given cell 2136 2137 Collective on dm 2138 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 2144 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 2150 2151 Level: advanced 2152 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. 2156 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; 2163 2164 PetscFunctionBegin; 2165 PetscValidRealPointer(detJ, 7); 2166 PetscCall(DMGetCoordinateDM(dm, &cdm)); 2167 if (cdm) { 2168 PetscClassId id; 2169 PetscInt numFields; 2170 PetscDS prob; 2171 PetscObject disc; 2172 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 } 2185 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; 2192 2193 PetscFunctionBegin; 2194 PetscCall(DMGetCoordinatesLocal(dm, &coordinates)); 2195 PetscCall(DMGetCoordinateSection(dm, &coordSection)); 2196 PetscCall(VecGetArrayRead(coordinates, &coords)); 2197 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; 2208 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)); 2213 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]); 2218 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; 2222 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 } 2230 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; 2237 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; 2247 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 } 2272 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; 2283 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; 2300 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); 2312 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 } 2334 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; 2349 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 } 2363 2364 if (centroid) 2365 for (d = 0; d < dim; ++d) centroid[d] = 0.0; 2366 PetscCall(DMPlexGetCone(dm, cell, &cone)); 2367 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 */ 2373 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}; 2398 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 } 2449 2450 /*@C 2451 DMPlexComputeCellGeometryFVM - Compute the volume for a given cell 2452 2453 Collective on dm 2454 2455 Input Parameters: 2456 + dm - the DM 2457 - cell - the cell 2458 2459 Output Parameters: 2460 + volume - the cell volume 2461 . centroid - the cell centroid 2462 - normal - the cell normal, if appropriate 2463 2464 Level: advanced 2465 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. 2469 2470 .seealso: `DMGetCoordinateSection()`, `DMGetCoordinates()` 2471 @*/ 2472 PetscErrorCode DMPlexComputeCellGeometryFVM(DM dm, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[]) 2473 { 2474 PetscInt depth, dim; 2475 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 } 2499 2500 /*@ 2501 DMPlexComputeGeometryFVM - Computes the cell and face geometry for a finite volume method 2502 2503 Input Parameter: 2504 . dm - The DM 2505 2506 Output Parameters: 2507 + cellgeom - A Vec of PetscFVCellGeom data 2508 - facegeom - A Vec of PetscFVFaceGeom data 2509 2510 Level: developer 2511 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; 2524 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; 2546 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; 2569 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]; 2584 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; 2621 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; 2635 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 } 2651 2652 /*@C 2653 DMPlexGetMinRadius - Returns the minimum distance from any cell centroid to a face 2654 2655 Not collective 2656 2657 Input Parameter: 2658 . dm - the DM 2659 2660 Output Parameter: 2661 . minradius - the minimum cell radius 2662 2663 Level: developer 2664 2665 .seealso: `DMGetCoordinates()` 2666 @*/ 2667 PetscErrorCode DMPlexGetMinRadius(DM dm, PetscReal *minradius) 2668 { 2669 PetscFunctionBegin; 2670 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 2671 PetscValidRealPointer(minradius, 2); 2672 *minradius = ((DM_Plex *)dm->data)->minradius; 2673 PetscFunctionReturn(PETSC_SUCCESS); 2674 } 2675 2676 /*@C 2677 DMPlexSetMinRadius - Sets the minimum distance from the cell centroid to a face 2678 2679 Logically collective 2680 2681 Input Parameters: 2682 + dm - the DM 2683 - minradius - the minimum cell radius 2684 2685 Level: developer 2686 2687 .seealso: `DMSetCoordinates()` 2688 @*/ 2689 PetscErrorCode DMPlexSetMinRadius(DM dm, PetscReal minradius) 2690 { 2691 PetscFunctionBegin; 2692 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 2693 ((DM_Plex *)dm->data)->minradius = minradius; 2694 PetscFunctionReturn(PETSC_SUCCESS); 2695 } 2696 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; 2702 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; 2718 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; 2722 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; 2732 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 } 2757 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; 2766 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; 2781 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]; 2791 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; 2808 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; 2818 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; 2833 2834 PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg)); 2835 PetscCall(PetscSectionGetDof(neighSec, c, &numFaces)); 2836 PetscCall(PetscSectionGetOffset(neighSec, c, &off)); 2837 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; 2841 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; 2848 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 } 2868 2869 /*@ 2870 DMPlexComputeGradientFVM - Compute geometric factors for gradient reconstruction, which are stored in the geometry data, and compute layout for gradient data 2871 2872 Collective on dm 2873 2874 Input Parameters: 2875 + dm - The DM 2876 . fvm - The PetscFV 2877 - cellGeometry - The face geometry from DMPlexComputeCellGeometryFVM() 2878 2879 Input/Output Parameter: 2880 . faceGeometry - The face geometry from DMPlexComputeFaceGeometryFVM(); on output 2881 the geometric factors for gradient calculation are inserted 2882 2883 Output Parameter: 2884 . dmGrad - The DM describing the layout of gradient data 2885 2886 Level: developer 2887 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; 2896 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 } 2925 2926 /*@ 2927 DMPlexGetDataFVM - Retrieve precomputed cell geometry 2928 2929 Collective on dm 2930 2931 Input Parameters: 2932 + dm - The DM 2933 - fv - The PetscFV 2934 2935 Output Parameters: 2936 + cellGeometry - The cell geometry 2937 . faceGeometry - The face geometry 2938 - gradDM - The gradient matrices 2939 2940 Level: developer 2941 2942 .seealso: `DMPlexComputeGeometryFVM()` 2943 @*/ 2944 PetscErrorCode DMPlexGetDataFVM(DM dm, PetscFV fv, Vec *cellgeom, Vec *facegeom, DM *gradDM) 2945 { 2946 PetscObject cellgeomobj, facegeomobj; 2947 2948 PetscFunctionBegin; 2949 PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_cellgeom_fvm", &cellgeomobj)); 2950 if (!cellgeomobj) { 2951 Vec cellgeomInt, facegeomInt; 2952 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; 2966 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; 2975 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 } 2985 2986 static PetscErrorCode DMPlexCoordinatesToReference_NewtonUpdate(PetscInt dimC, PetscInt dimR, PetscScalar *J, PetscScalar *invJ, PetscScalar *work, PetscReal *resNeg, PetscReal *guess) 2987 { 2988 PetscInt l, m; 2989 2990 PetscFunctionBeginHot; 2991 if (dimC == dimR && dimR <= 3) { 2992 /* invert Jacobian, multiply */ 2993 PetscScalar det, idet; 2994 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; 3037 3038 for (l = 0; l < dimC; l++) invJ[l] = resNeg[l]; 3039 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"); 3042 3043 for (l = 0; l < dimR; l++) guess[l] += PetscRealPart(invJ[l]); 3044 } 3045 PetscFunctionReturn(PETSC_SUCCESS); 3046 } 3047 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; 3054 3055 PetscFunctionBegin; 3056 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 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}; 3069 3070 for (i = 0; i < 4; i++) { 3071 PetscInt plexI = zToPlex[i]; 3072 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}; 3077 3078 for (i = 0; i < 8; i++) { 3079 PetscInt plexI = zToPlex[i]; 3080 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; 3089 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 } 3096 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]; 3107 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; 3116 3117 extCoord *= dep * coord + !dep; 3118 extJ[l] = dep; 3119 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; 3124 3125 extJ[l] *= mult; 3126 } 3127 } 3128 for (l = 0; l < dimC; l++) { 3129 PetscReal coeff = cellCoeffs[dimC * k + l]; 3130 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.; 3137 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 } 3141 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 } 3150 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; 3156 3157 PetscFunctionBegin; 3158 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 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}; 3166 3167 for (i = 0; i < 4; i++) { 3168 PetscInt plexI = zToPlex[i]; 3169 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}; 3174 3175 for (i = 0; i < 8; i++) { 3176 PetscInt plexI = zToPlex[i]; 3177 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; 3186 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 } 3193 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]; 3204 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; 3210 3211 extCoord *= dep * coord + !dep; 3212 } 3213 for (l = 0; l < dimC; l++) { 3214 PetscReal coeff = cellCoeffs[dimC * k + l]; 3215 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 } 3224 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; 3233 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.; 3267 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 } 3280 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; 3288 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]; 3306 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 } 3316 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) 3321 3322 Not collective 3323 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()) 3331 3332 Output Parameters: 3333 . refCoords - (numPoints x dimension) array of reference coordinates (see DMGetDimension()) 3334 3335 Level: intermediate 3336 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; 3345 3346 PetscFunctionBegin; 3347 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 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; 3356 3357 PetscCall(DMGetNumFields(coordDM, &coordFields)); 3358 if (coordFields) { 3359 PetscClassId id; 3360 PetscObject disc; 3361 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; 3372 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; 3378 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.}; 3385 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 } 3397 3398 /*@ 3399 DMPlexReferenceToCoordinates - Map references coordinates to coordinates in the the mesh for a single element map. 3400 3401 Not collective 3402 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()) 3410 3411 Output Parameters: 3412 . realCoords - (numPoints x coordinate dimension) array of coordinates (see DMGetCoordinateDim()) 3413 3414 Level: intermediate 3415 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; 3424 3425 PetscFunctionBegin; 3426 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 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; 3435 3436 PetscCall(DMGetNumFields(coordDM, &coordFields)); 3437 if (coordFields) { 3438 PetscClassId id; 3439 PetscObject disc; 3440 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; 3451 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; 3457 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.}; 3463 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 } 3475 3476 /*@C 3477 DMPlexRemapGeometry - This function maps the original DM coordinates to new coordinates. 3478 3479 Not collective 3480 3481 Input Parameters: 3482 + dm - The DM 3483 . time - The time 3484 - func - The function transforming current coordinates to new coordaintes 3485 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[]); 3491 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 3510 3511 Level: intermediate 3512 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; 3520 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 } 3535 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; 3546 3547 for (c = 0; c < Nc; ++c) coords[c] = u[c] + constants[c + 1] * u[ax]; 3548 } 3549 3550 /*@C 3551 DMPlexShearGeometry - This shears the domain, meaning adds a multiple of the shear coordinate to all other coordinates. 3552 3553 Not collective 3554 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 3559 3560 Level: intermediate 3561 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; 3573 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; 3588 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; 3596 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 } 3609