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