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