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