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