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