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