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