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 PetscCall(MPIU_Allreduce(lbox->lower, gbox->lower, 3, MPIU_REAL, MPI_MIN, comm)); 623 PetscCall(MPIU_Allreduce(lbox->upper, gbox->upper, 3, MPIU_REAL, MPI_MAX, comm)); 624 #endif 625 /* Is there a reason to snap the local bounding box to a division of the global box? */ 626 /* Should we compute all overlaps of local boxes? We could do this with a rendevouz scheme partitioning the global box */ 627 /* Create label */ 628 PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd)); 629 if (dim < 2) eStart = eEnd = -1; 630 PetscCall(DMLabelCreate(PETSC_COMM_SELF, "cells", &lbox->cellsSparse)); 631 PetscCall(DMLabelCreateIndex(lbox->cellsSparse, cStart, cEnd)); 632 /* Compute boxes which overlap each cell: https://stackoverflow.com/questions/13790208/triangle-square-intersection-test-in-2d */ 633 PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal)); 634 PetscCall(DMGetCoordinateSection(dm, &coordSection)); 635 PetscCall(PetscCalloc3(16 * dim, &dboxes, 16, &boxes, PetscPowInt(maxConeSize, dim) * dim, &edgeCoords)); 636 for (c = cStart; c < cEnd; ++c) { 637 const PetscReal *h = lbox->h; 638 PetscScalar *ccoords = NULL; 639 PetscInt csize = 0; 640 PetscInt *closure = NULL; 641 PetscInt Ncl, cl, Ne = 0; 642 PetscScalar point[3]; 643 PetscInt dlim[6], d, e, i, j, k; 644 645 /* Get all edges in cell */ 646 PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &Ncl, &closure)); 647 for (cl = 0; cl < Ncl*2; ++cl) { 648 if ((closure[cl] >= eStart) && (closure[cl] < eEnd)) { 649 PetscScalar *ecoords = &edgeCoords[Ne*dim*2]; 650 PetscInt ecsize = dim*2; 651 652 PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, closure[cl], &ecsize, &ecoords)); 653 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, meaning the translation to the origin (not the first vertex of the reference cell) 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 PetscCall(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 // do not attempt to compute a gradient reconstruction stencil in a ghost cell. It will never be used 2587 PetscCall(DMLabelGetValue(ghostLabel, c, &ghost)); 2588 if (ghost >= 0) continue; 2589 2590 PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg)); 2591 PetscCall(DMPlexGetConeSize(dm, c, &numFaces)); 2592 PetscCall(DMPlexGetCone(dm, c, &faces)); 2593 PetscCheckFalse(numFaces < dim,PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Cell %D has only %D faces, not enough for gradient reconstruction", c, numFaces); 2594 for (f = 0, usedFaces = 0; f < numFaces; ++f) { 2595 PetscFVCellGeom *cg1; 2596 PetscFVFaceGeom *fg; 2597 const PetscInt *fcells; 2598 PetscInt ncell, side; 2599 2600 PetscCall(DMLabelGetValue(ghostLabel, faces[f], &ghost)); 2601 PetscCall(DMIsBoundaryPoint(dm, faces[f], &boundary)); 2602 if ((ghost >= 0) || boundary) continue; 2603 PetscCall(DMPlexGetSupport(dm, faces[f], &fcells)); 2604 side = (c != fcells[0]); /* c is on left=0 or right=1 of face */ 2605 ncell = fcells[!side]; /* the neighbor */ 2606 PetscCall(DMPlexPointLocalRef(dmFace, faces[f], fgeom, &fg)); 2607 PetscCall(DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1)); 2608 for (d = 0; d < dim; ++d) dx[usedFaces*dim+d] = cg1->centroid[d] - cg->centroid[d]; 2609 gref[usedFaces++] = fg->grad[side]; /* Gradient reconstruction term will go here */ 2610 } 2611 PetscCheck(usedFaces,PETSC_COMM_SELF, PETSC_ERR_USER, "Mesh contains isolated cell (no neighbors). Is it intentional?"); 2612 PetscCall(PetscFVComputeGradient(fvm, usedFaces, dx, grad)); 2613 for (f = 0, usedFaces = 0; f < numFaces; ++f) { 2614 PetscCall(DMLabelGetValue(ghostLabel, faces[f], &ghost)); 2615 PetscCall(DMIsBoundaryPoint(dm, faces[f], &boundary)); 2616 if ((ghost >= 0) || boundary) continue; 2617 for (d = 0; d < dim; ++d) gref[usedFaces][d] = grad[usedFaces*dim+d]; 2618 ++usedFaces; 2619 } 2620 } 2621 PetscCall(PetscFree3(dx, grad, gref)); 2622 PetscFunctionReturn(0); 2623 } 2624 2625 static PetscErrorCode BuildGradientReconstruction_Internal_Tree(DM dm, PetscFV fvm, DM dmFace, PetscScalar *fgeom, DM dmCell, PetscScalar *cgeom) 2626 { 2627 DMLabel ghostLabel; 2628 PetscScalar *dx, *grad, **gref; 2629 PetscInt dim, cStart, cEnd, c, cEndInterior, fStart, fEnd, f, nStart, nEnd, maxNumFaces = 0; 2630 PetscSection neighSec; 2631 PetscInt (*neighbors)[2]; 2632 PetscInt *counter; 2633 2634 PetscFunctionBegin; 2635 PetscCall(DMGetDimension(dm, &dim)); 2636 PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd)); 2637 PetscCall(DMPlexGetGhostCellStratum(dm, &cEndInterior, NULL)); 2638 if (cEndInterior < 0) cEndInterior = cEnd; 2639 PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm),&neighSec)); 2640 PetscCall(PetscSectionSetChart(neighSec,cStart,cEndInterior)); 2641 PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 2642 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 2643 for (f = fStart; f < fEnd; f++) { 2644 const PetscInt *fcells; 2645 PetscBool boundary; 2646 PetscInt ghost = -1; 2647 PetscInt numChildren, numCells, c; 2648 2649 if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost)); 2650 PetscCall(DMIsBoundaryPoint(dm, f, &boundary)); 2651 PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL)); 2652 if ((ghost >= 0) || boundary || numChildren) continue; 2653 PetscCall(DMPlexGetSupportSize(dm, f, &numCells)); 2654 if (numCells == 2) { 2655 PetscCall(DMPlexGetSupport(dm, f, &fcells)); 2656 for (c = 0; c < 2; c++) { 2657 PetscInt cell = fcells[c]; 2658 2659 if (cell >= cStart && cell < cEndInterior) { 2660 PetscCall(PetscSectionAddDof(neighSec,cell,1)); 2661 } 2662 } 2663 } 2664 } 2665 PetscCall(PetscSectionSetUp(neighSec)); 2666 PetscCall(PetscSectionGetMaxDof(neighSec,&maxNumFaces)); 2667 PetscCall(PetscFVLeastSquaresSetMaxFaces(fvm, maxNumFaces)); 2668 nStart = 0; 2669 PetscCall(PetscSectionGetStorageSize(neighSec,&nEnd)); 2670 PetscCall(PetscMalloc1((nEnd-nStart),&neighbors)); 2671 PetscCall(PetscCalloc1((cEndInterior-cStart),&counter)); 2672 for (f = fStart; f < fEnd; f++) { 2673 const PetscInt *fcells; 2674 PetscBool boundary; 2675 PetscInt ghost = -1; 2676 PetscInt numChildren, numCells, c; 2677 2678 if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost)); 2679 PetscCall(DMIsBoundaryPoint(dm, f, &boundary)); 2680 PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL)); 2681 if ((ghost >= 0) || boundary || numChildren) continue; 2682 PetscCall(DMPlexGetSupportSize(dm, f, &numCells)); 2683 if (numCells == 2) { 2684 PetscCall(DMPlexGetSupport(dm, f, &fcells)); 2685 for (c = 0; c < 2; c++) { 2686 PetscInt cell = fcells[c], off; 2687 2688 if (cell >= cStart && cell < cEndInterior) { 2689 PetscCall(PetscSectionGetOffset(neighSec,cell,&off)); 2690 off += counter[cell - cStart]++; 2691 neighbors[off][0] = f; 2692 neighbors[off][1] = fcells[1 - c]; 2693 } 2694 } 2695 } 2696 } 2697 PetscCall(PetscFree(counter)); 2698 PetscCall(PetscMalloc3(maxNumFaces*dim, &dx, maxNumFaces*dim, &grad, maxNumFaces, &gref)); 2699 for (c = cStart; c < cEndInterior; c++) { 2700 PetscInt numFaces, f, d, off, ghost = -1; 2701 PetscFVCellGeom *cg; 2702 2703 PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg)); 2704 PetscCall(PetscSectionGetDof(neighSec, c, &numFaces)); 2705 PetscCall(PetscSectionGetOffset(neighSec, c, &off)); 2706 2707 // do not attempt to compute a gradient reconstruction stencil in a ghost cell. It will never be used 2708 if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, c, &ghost)); 2709 if (ghost >= 0) continue; 2710 2711 PetscCheckFalse(numFaces < dim,PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Cell %D has only %D faces, not enough for gradient reconstruction", c, numFaces); 2712 for (f = 0; f < numFaces; ++f) { 2713 PetscFVCellGeom *cg1; 2714 PetscFVFaceGeom *fg; 2715 const PetscInt *fcells; 2716 PetscInt ncell, side, nface; 2717 2718 nface = neighbors[off + f][0]; 2719 ncell = neighbors[off + f][1]; 2720 PetscCall(DMPlexGetSupport(dm,nface,&fcells)); 2721 side = (c != fcells[0]); 2722 PetscCall(DMPlexPointLocalRef(dmFace, nface, fgeom, &fg)); 2723 PetscCall(DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1)); 2724 for (d = 0; d < dim; ++d) dx[f*dim+d] = cg1->centroid[d] - cg->centroid[d]; 2725 gref[f] = fg->grad[side]; /* Gradient reconstruction term will go here */ 2726 } 2727 PetscCall(PetscFVComputeGradient(fvm, numFaces, dx, grad)); 2728 for (f = 0; f < numFaces; ++f) { 2729 for (d = 0; d < dim; ++d) gref[f][d] = grad[f*dim+d]; 2730 } 2731 } 2732 PetscCall(PetscFree3(dx, grad, gref)); 2733 PetscCall(PetscSectionDestroy(&neighSec)); 2734 PetscCall(PetscFree(neighbors)); 2735 PetscFunctionReturn(0); 2736 } 2737 2738 /*@ 2739 DMPlexComputeGradientFVM - Compute geometric factors for gradient reconstruction, which are stored in the geometry data, and compute layout for gradient data 2740 2741 Collective on dm 2742 2743 Input Parameters: 2744 + dm - The DM 2745 . fvm - The PetscFV 2746 - cellGeometry - The face geometry from DMPlexComputeCellGeometryFVM() 2747 2748 Input/Output Parameter: 2749 . faceGeometry - The face geometry from DMPlexComputeFaceGeometryFVM(); on output 2750 the geometric factors for gradient calculation are inserted 2751 2752 Output Parameter: 2753 . dmGrad - The DM describing the layout of gradient data 2754 2755 Level: developer 2756 2757 .seealso: DMPlexGetFaceGeometryFVM(), DMPlexGetCellGeometryFVM() 2758 @*/ 2759 PetscErrorCode DMPlexComputeGradientFVM(DM dm, PetscFV fvm, Vec faceGeometry, Vec cellGeometry, DM *dmGrad) 2760 { 2761 DM dmFace, dmCell; 2762 PetscScalar *fgeom, *cgeom; 2763 PetscSection sectionGrad, parentSection; 2764 PetscInt dim, pdim, cStart, cEnd, cEndInterior, c; 2765 2766 PetscFunctionBegin; 2767 PetscCall(DMGetDimension(dm, &dim)); 2768 PetscCall(PetscFVGetNumComponents(fvm, &pdim)); 2769 PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd)); 2770 PetscCall(DMPlexGetGhostCellStratum(dm, &cEndInterior, NULL)); 2771 /* Construct the interpolant corresponding to each face from the least-square solution over the cell neighborhood */ 2772 PetscCall(VecGetDM(faceGeometry, &dmFace)); 2773 PetscCall(VecGetDM(cellGeometry, &dmCell)); 2774 PetscCall(VecGetArray(faceGeometry, &fgeom)); 2775 PetscCall(VecGetArray(cellGeometry, &cgeom)); 2776 PetscCall(DMPlexGetTree(dm,&parentSection,NULL,NULL,NULL,NULL)); 2777 if (!parentSection) { 2778 PetscCall(BuildGradientReconstruction_Internal(dm, fvm, dmFace, fgeom, dmCell, cgeom)); 2779 } else { 2780 PetscCall(BuildGradientReconstruction_Internal_Tree(dm, fvm, dmFace, fgeom, dmCell, cgeom)); 2781 } 2782 PetscCall(VecRestoreArray(faceGeometry, &fgeom)); 2783 PetscCall(VecRestoreArray(cellGeometry, &cgeom)); 2784 /* Create storage for gradients */ 2785 PetscCall(DMClone(dm, dmGrad)); 2786 PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) dm), §ionGrad)); 2787 PetscCall(PetscSectionSetChart(sectionGrad, cStart, cEnd)); 2788 for (c = cStart; c < cEnd; ++c) PetscCall(PetscSectionSetDof(sectionGrad, c, pdim*dim)); 2789 PetscCall(PetscSectionSetUp(sectionGrad)); 2790 PetscCall(DMSetLocalSection(*dmGrad, sectionGrad)); 2791 PetscCall(PetscSectionDestroy(§ionGrad)); 2792 PetscFunctionReturn(0); 2793 } 2794 2795 /*@ 2796 DMPlexGetDataFVM - Retrieve precomputed cell geometry 2797 2798 Collective on dm 2799 2800 Input Parameters: 2801 + dm - The DM 2802 - fv - The PetscFV 2803 2804 Output Parameters: 2805 + cellGeometry - The cell geometry 2806 . faceGeometry - The face geometry 2807 - gradDM - The gradient matrices 2808 2809 Level: developer 2810 2811 .seealso: DMPlexComputeGeometryFVM() 2812 @*/ 2813 PetscErrorCode DMPlexGetDataFVM(DM dm, PetscFV fv, Vec *cellgeom, Vec *facegeom, DM *gradDM) 2814 { 2815 PetscObject cellgeomobj, facegeomobj; 2816 2817 PetscFunctionBegin; 2818 PetscCall(PetscObjectQuery((PetscObject) dm, "DMPlex_cellgeom_fvm", &cellgeomobj)); 2819 if (!cellgeomobj) { 2820 Vec cellgeomInt, facegeomInt; 2821 2822 PetscCall(DMPlexComputeGeometryFVM(dm, &cellgeomInt, &facegeomInt)); 2823 PetscCall(PetscObjectCompose((PetscObject) dm, "DMPlex_cellgeom_fvm",(PetscObject)cellgeomInt)); 2824 PetscCall(PetscObjectCompose((PetscObject) dm, "DMPlex_facegeom_fvm",(PetscObject)facegeomInt)); 2825 PetscCall(VecDestroy(&cellgeomInt)); 2826 PetscCall(VecDestroy(&facegeomInt)); 2827 PetscCall(PetscObjectQuery((PetscObject) dm, "DMPlex_cellgeom_fvm", &cellgeomobj)); 2828 } 2829 PetscCall(PetscObjectQuery((PetscObject) dm, "DMPlex_facegeom_fvm", &facegeomobj)); 2830 if (cellgeom) *cellgeom = (Vec) cellgeomobj; 2831 if (facegeom) *facegeom = (Vec) facegeomobj; 2832 if (gradDM) { 2833 PetscObject gradobj; 2834 PetscBool computeGradients; 2835 2836 PetscCall(PetscFVGetComputeGradients(fv,&computeGradients)); 2837 if (!computeGradients) { 2838 *gradDM = NULL; 2839 PetscFunctionReturn(0); 2840 } 2841 PetscCall(PetscObjectQuery((PetscObject) dm, "DMPlex_dmgrad_fvm", &gradobj)); 2842 if (!gradobj) { 2843 DM dmGradInt; 2844 2845 PetscCall(DMPlexComputeGradientFVM(dm,fv,(Vec) facegeomobj,(Vec) cellgeomobj,&dmGradInt)); 2846 PetscCall(PetscObjectCompose((PetscObject) dm, "DMPlex_dmgrad_fvm", (PetscObject)dmGradInt)); 2847 PetscCall(DMDestroy(&dmGradInt)); 2848 PetscCall(PetscObjectQuery((PetscObject) dm, "DMPlex_dmgrad_fvm", &gradobj)); 2849 } 2850 *gradDM = (DM) gradobj; 2851 } 2852 PetscFunctionReturn(0); 2853 } 2854 2855 static PetscErrorCode DMPlexCoordinatesToReference_NewtonUpdate(PetscInt dimC, PetscInt dimR, PetscScalar *J, PetscScalar *invJ, PetscScalar *work, PetscReal *resNeg, PetscReal *guess) 2856 { 2857 PetscInt l, m; 2858 2859 PetscFunctionBeginHot; 2860 if (dimC == dimR && dimR <= 3) { 2861 /* invert Jacobian, multiply */ 2862 PetscScalar det, idet; 2863 2864 switch (dimR) { 2865 case 1: 2866 invJ[0] = 1./ J[0]; 2867 break; 2868 case 2: 2869 det = J[0] * J[3] - J[1] * J[2]; 2870 idet = 1./det; 2871 invJ[0] = J[3] * idet; 2872 invJ[1] = -J[1] * idet; 2873 invJ[2] = -J[2] * idet; 2874 invJ[3] = J[0] * idet; 2875 break; 2876 case 3: 2877 { 2878 invJ[0] = J[4] * J[8] - J[5] * J[7]; 2879 invJ[1] = J[2] * J[7] - J[1] * J[8]; 2880 invJ[2] = J[1] * J[5] - J[2] * J[4]; 2881 det = invJ[0] * J[0] + invJ[1] * J[3] + invJ[2] * J[6]; 2882 idet = 1./det; 2883 invJ[0] *= idet; 2884 invJ[1] *= idet; 2885 invJ[2] *= idet; 2886 invJ[3] = idet * (J[5] * J[6] - J[3] * J[8]); 2887 invJ[4] = idet * (J[0] * J[8] - J[2] * J[6]); 2888 invJ[5] = idet * (J[2] * J[3] - J[0] * J[5]); 2889 invJ[6] = idet * (J[3] * J[7] - J[4] * J[6]); 2890 invJ[7] = idet * (J[1] * J[6] - J[0] * J[7]); 2891 invJ[8] = idet * (J[0] * J[4] - J[1] * J[3]); 2892 } 2893 break; 2894 } 2895 for (l = 0; l < dimR; l++) { 2896 for (m = 0; m < dimC; m++) { 2897 guess[l] += PetscRealPart(invJ[l * dimC + m]) * resNeg[m]; 2898 } 2899 } 2900 } else { 2901 #if defined(PETSC_USE_COMPLEX) 2902 char transpose = 'C'; 2903 #else 2904 char transpose = 'T'; 2905 #endif 2906 PetscBLASInt m = dimR; 2907 PetscBLASInt n = dimC; 2908 PetscBLASInt one = 1; 2909 PetscBLASInt worksize = dimR * dimC, info; 2910 2911 for (l = 0; l < dimC; l++) {invJ[l] = resNeg[l];} 2912 2913 PetscStackCallBLAS("LAPACKgels",LAPACKgels_(&transpose,&m,&n,&one,J,&m,invJ,&n,work,&worksize, &info)); 2914 PetscCheckFalse(info != 0,PETSC_COMM_SELF,PETSC_ERR_LIB,"Bad argument to GELS"); 2915 2916 for (l = 0; l < dimR; l++) {guess[l] += PetscRealPart(invJ[l]);} 2917 } 2918 PetscFunctionReturn(0); 2919 } 2920 2921 static PetscErrorCode DMPlexCoordinatesToReference_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[], Vec coords, PetscInt dimC, PetscInt dimR) 2922 { 2923 PetscInt coordSize, i, j, k, l, m, maxIts = 7, numV = (1 << dimR); 2924 PetscScalar *coordsScalar = NULL; 2925 PetscReal *cellData, *cellCoords, *cellCoeffs, *extJ, *resNeg; 2926 PetscScalar *J, *invJ, *work; 2927 2928 PetscFunctionBegin; 2929 PetscValidHeaderSpecific(dm,DM_CLASSID,1); 2930 PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar)); 2931 PetscCheckFalse(coordSize < dimC * numV,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Expecting at least %D coordinates, got %D",dimC * (1 << dimR), coordSize); 2932 PetscCall(DMGetWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData)); 2933 PetscCall(DMGetWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J)); 2934 cellCoords = &cellData[0]; 2935 cellCoeffs = &cellData[coordSize]; 2936 extJ = &cellData[2 * coordSize]; 2937 resNeg = &cellData[2 * coordSize + dimR]; 2938 invJ = &J[dimR * dimC]; 2939 work = &J[2 * dimR * dimC]; 2940 if (dimR == 2) { 2941 const PetscInt zToPlex[4] = {0, 1, 3, 2}; 2942 2943 for (i = 0; i < 4; i++) { 2944 PetscInt plexI = zToPlex[i]; 2945 2946 for (j = 0; j < dimC; j++) { 2947 cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]); 2948 } 2949 } 2950 } else if (dimR == 3) { 2951 const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6}; 2952 2953 for (i = 0; i < 8; i++) { 2954 PetscInt plexI = zToPlex[i]; 2955 2956 for (j = 0; j < dimC; j++) { 2957 cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]); 2958 } 2959 } 2960 } else { 2961 for (i = 0; i < coordSize; i++) {cellCoords[i] = PetscRealPart(coordsScalar[i]);} 2962 } 2963 /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */ 2964 for (i = 0; i < dimR; i++) { 2965 PetscReal *swap; 2966 2967 for (j = 0; j < (numV / 2); j++) { 2968 for (k = 0; k < dimC; k++) { 2969 cellCoeffs[dimC * j + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]); 2970 cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]); 2971 } 2972 } 2973 2974 if (i < dimR - 1) { 2975 swap = cellCoeffs; 2976 cellCoeffs = cellCoords; 2977 cellCoords = swap; 2978 } 2979 } 2980 PetscCall(PetscArrayzero(refCoords,numPoints * dimR)); 2981 for (j = 0; j < numPoints; j++) { 2982 for (i = 0; i < maxIts; i++) { 2983 PetscReal *guess = &refCoords[dimR * j]; 2984 2985 /* compute -residual and Jacobian */ 2986 for (k = 0; k < dimC; k++) {resNeg[k] = realCoords[dimC * j + k];} 2987 for (k = 0; k < dimC * dimR; k++) {J[k] = 0.;} 2988 for (k = 0; k < numV; k++) { 2989 PetscReal extCoord = 1.; 2990 for (l = 0; l < dimR; l++) { 2991 PetscReal coord = guess[l]; 2992 PetscInt dep = (k & (1 << l)) >> l; 2993 2994 extCoord *= dep * coord + !dep; 2995 extJ[l] = dep; 2996 2997 for (m = 0; m < dimR; m++) { 2998 PetscReal coord = guess[m]; 2999 PetscInt dep = ((k & (1 << m)) >> m) && (m != l); 3000 PetscReal mult = dep * coord + !dep; 3001 3002 extJ[l] *= mult; 3003 } 3004 } 3005 for (l = 0; l < dimC; l++) { 3006 PetscReal coeff = cellCoeffs[dimC * k + l]; 3007 3008 resNeg[l] -= coeff * extCoord; 3009 for (m = 0; m < dimR; m++) { 3010 J[dimR * l + m] += coeff * extJ[m]; 3011 } 3012 } 3013 } 3014 if (0 && PetscDefined(USE_DEBUG)) { 3015 PetscReal maxAbs = 0.; 3016 3017 for (l = 0; l < dimC; l++) { 3018 maxAbs = PetscMax(maxAbs,PetscAbsReal(resNeg[l])); 3019 } 3020 PetscCall(PetscInfo(dm,"cell %D, point %D, iter %D: res %g\n",cell,j,i,(double) maxAbs)); 3021 } 3022 3023 PetscCall(DMPlexCoordinatesToReference_NewtonUpdate(dimC,dimR,J,invJ,work,resNeg,guess)); 3024 } 3025 } 3026 PetscCall(DMRestoreWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J)); 3027 PetscCall(DMRestoreWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData)); 3028 PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar)); 3029 PetscFunctionReturn(0); 3030 } 3031 3032 static PetscErrorCode DMPlexReferenceToCoordinates_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt dimC, PetscInt dimR) 3033 { 3034 PetscInt coordSize, i, j, k, l, numV = (1 << dimR); 3035 PetscScalar *coordsScalar = NULL; 3036 PetscReal *cellData, *cellCoords, *cellCoeffs; 3037 3038 PetscFunctionBegin; 3039 PetscValidHeaderSpecific(dm,DM_CLASSID,1); 3040 PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar)); 3041 PetscCheckFalse(coordSize < dimC * numV,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Expecting at least %D coordinates, got %D",dimC * (1 << dimR), coordSize); 3042 PetscCall(DMGetWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData)); 3043 cellCoords = &cellData[0]; 3044 cellCoeffs = &cellData[coordSize]; 3045 if (dimR == 2) { 3046 const PetscInt zToPlex[4] = {0, 1, 3, 2}; 3047 3048 for (i = 0; i < 4; i++) { 3049 PetscInt plexI = zToPlex[i]; 3050 3051 for (j = 0; j < dimC; j++) { 3052 cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]); 3053 } 3054 } 3055 } else if (dimR == 3) { 3056 const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6}; 3057 3058 for (i = 0; i < 8; i++) { 3059 PetscInt plexI = zToPlex[i]; 3060 3061 for (j = 0; j < dimC; j++) { 3062 cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]); 3063 } 3064 } 3065 } else { 3066 for (i = 0; i < coordSize; i++) {cellCoords[i] = PetscRealPart(coordsScalar[i]);} 3067 } 3068 /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */ 3069 for (i = 0; i < dimR; i++) { 3070 PetscReal *swap; 3071 3072 for (j = 0; j < (numV / 2); j++) { 3073 for (k = 0; k < dimC; k++) { 3074 cellCoeffs[dimC * j + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]); 3075 cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]); 3076 } 3077 } 3078 3079 if (i < dimR - 1) { 3080 swap = cellCoeffs; 3081 cellCoeffs = cellCoords; 3082 cellCoords = swap; 3083 } 3084 } 3085 PetscCall(PetscArrayzero(realCoords,numPoints * dimC)); 3086 for (j = 0; j < numPoints; j++) { 3087 const PetscReal *guess = &refCoords[dimR * j]; 3088 PetscReal *mapped = &realCoords[dimC * j]; 3089 3090 for (k = 0; k < numV; k++) { 3091 PetscReal extCoord = 1.; 3092 for (l = 0; l < dimR; l++) { 3093 PetscReal coord = guess[l]; 3094 PetscInt dep = (k & (1 << l)) >> l; 3095 3096 extCoord *= dep * coord + !dep; 3097 } 3098 for (l = 0; l < dimC; l++) { 3099 PetscReal coeff = cellCoeffs[dimC * k + l]; 3100 3101 mapped[l] += coeff * extCoord; 3102 } 3103 } 3104 } 3105 PetscCall(DMRestoreWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData)); 3106 PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar)); 3107 PetscFunctionReturn(0); 3108 } 3109 3110 /* TODO: TOBY please fix this for Nc > 1 */ 3111 static PetscErrorCode DMPlexCoordinatesToReference_FE(DM dm, PetscFE fe, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[], Vec coords, PetscInt Nc, PetscInt dimR) 3112 { 3113 PetscInt numComp, pdim, i, j, k, l, m, maxIter = 7, coordSize; 3114 PetscScalar *nodes = NULL; 3115 PetscReal *invV, *modes; 3116 PetscReal *B, *D, *resNeg; 3117 PetscScalar *J, *invJ, *work; 3118 3119 PetscFunctionBegin; 3120 PetscCall(PetscFEGetDimension(fe, &pdim)); 3121 PetscCall(PetscFEGetNumComponents(fe, &numComp)); 3122 PetscCheckFalse(numComp != Nc,PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"coordinate discretization must have as many components (%D) as embedding dimension (!= %D)",numComp,Nc); 3123 PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &nodes)); 3124 /* convert nodes to values in the stable evaluation basis */ 3125 PetscCall(DMGetWorkArray(dm,pdim,MPIU_REAL,&modes)); 3126 invV = fe->invV; 3127 for (i = 0; i < pdim; ++i) { 3128 modes[i] = 0.; 3129 for (j = 0; j < pdim; ++j) { 3130 modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]); 3131 } 3132 } 3133 PetscCall(DMGetWorkArray(dm,pdim * Nc + pdim * Nc * dimR + Nc,MPIU_REAL,&B)); 3134 D = &B[pdim*Nc]; 3135 resNeg = &D[pdim*Nc * dimR]; 3136 PetscCall(DMGetWorkArray(dm,3 * Nc * dimR,MPIU_SCALAR,&J)); 3137 invJ = &J[Nc * dimR]; 3138 work = &invJ[Nc * dimR]; 3139 for (i = 0; i < numPoints * dimR; i++) {refCoords[i] = 0.;} 3140 for (j = 0; j < numPoints; j++) { 3141 for (i = 0; i < maxIter; i++) { /* we could batch this so that we're not making big B and D arrays all the time */ 3142 PetscReal *guess = &refCoords[j * dimR]; 3143 PetscCall(PetscSpaceEvaluate(fe->basisSpace, 1, guess, B, D, NULL)); 3144 for (k = 0; k < Nc; k++) {resNeg[k] = realCoords[j * Nc + k];} 3145 for (k = 0; k < Nc * dimR; k++) {J[k] = 0.;} 3146 for (k = 0; k < pdim; k++) { 3147 for (l = 0; l < Nc; l++) { 3148 resNeg[l] -= modes[k] * B[k * Nc + l]; 3149 for (m = 0; m < dimR; m++) { 3150 J[l * dimR + m] += modes[k] * D[(k * Nc + l) * dimR + m]; 3151 } 3152 } 3153 } 3154 if (0 && PetscDefined(USE_DEBUG)) { 3155 PetscReal maxAbs = 0.; 3156 3157 for (l = 0; l < Nc; l++) { 3158 maxAbs = PetscMax(maxAbs,PetscAbsReal(resNeg[l])); 3159 } 3160 PetscCall(PetscInfo(dm,"cell %D, point %D, iter %D: res %g\n",cell,j,i,(double) maxAbs)); 3161 } 3162 PetscCall(DMPlexCoordinatesToReference_NewtonUpdate(Nc,dimR,J,invJ,work,resNeg,guess)); 3163 } 3164 } 3165 PetscCall(DMRestoreWorkArray(dm,3 * Nc * dimR,MPIU_SCALAR,&J)); 3166 PetscCall(DMRestoreWorkArray(dm,pdim * Nc + pdim * Nc * dimR + Nc,MPIU_REAL,&B)); 3167 PetscCall(DMRestoreWorkArray(dm,pdim,MPIU_REAL,&modes)); 3168 PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes)); 3169 PetscFunctionReturn(0); 3170 } 3171 3172 /* TODO: TOBY please fix this for Nc > 1 */ 3173 static PetscErrorCode DMPlexReferenceToCoordinates_FE(DM dm, PetscFE fe, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt Nc, PetscInt dimR) 3174 { 3175 PetscInt numComp, pdim, i, j, k, l, coordSize; 3176 PetscScalar *nodes = NULL; 3177 PetscReal *invV, *modes; 3178 PetscReal *B; 3179 3180 PetscFunctionBegin; 3181 PetscCall(PetscFEGetDimension(fe, &pdim)); 3182 PetscCall(PetscFEGetNumComponents(fe, &numComp)); 3183 PetscCheckFalse(numComp != Nc,PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"coordinate discretization must have as many components (%D) as embedding dimension (!= %D)",numComp,Nc); 3184 PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &nodes)); 3185 /* convert nodes to values in the stable evaluation basis */ 3186 PetscCall(DMGetWorkArray(dm,pdim,MPIU_REAL,&modes)); 3187 invV = fe->invV; 3188 for (i = 0; i < pdim; ++i) { 3189 modes[i] = 0.; 3190 for (j = 0; j < pdim; ++j) { 3191 modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]); 3192 } 3193 } 3194 PetscCall(DMGetWorkArray(dm,numPoints * pdim * Nc,MPIU_REAL,&B)); 3195 PetscCall(PetscSpaceEvaluate(fe->basisSpace, numPoints, refCoords, B, NULL, NULL)); 3196 for (i = 0; i < numPoints * Nc; i++) {realCoords[i] = 0.;} 3197 for (j = 0; j < numPoints; j++) { 3198 PetscReal *mapped = &realCoords[j * Nc]; 3199 3200 for (k = 0; k < pdim; k++) { 3201 for (l = 0; l < Nc; l++) { 3202 mapped[l] += modes[k] * B[(j * pdim + k) * Nc + l]; 3203 } 3204 } 3205 } 3206 PetscCall(DMRestoreWorkArray(dm,numPoints * pdim * Nc,MPIU_REAL,&B)); 3207 PetscCall(DMRestoreWorkArray(dm,pdim,MPIU_REAL,&modes)); 3208 PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes)); 3209 PetscFunctionReturn(0); 3210 } 3211 3212 /*@ 3213 DMPlexCoordinatesToReference - Pull coordinates back from the mesh to the reference element using a single element 3214 map. This inversion will be accurate inside the reference element, but may be inaccurate for mappings that do not 3215 extend uniquely outside the reference cell (e.g, most non-affine maps) 3216 3217 Not collective 3218 3219 Input Parameters: 3220 + dm - The mesh, with coordinate maps defined either by a PetscDS for the coordinate DM (see DMGetCoordinateDM()) or 3221 implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or 3222 as a multilinear map for tensor-product elements 3223 . cell - the cell whose map is used. 3224 . numPoints - the number of points to locate 3225 - realCoords - (numPoints x coordinate dimension) array of coordinates (see DMGetCoordinateDim()) 3226 3227 Output Parameters: 3228 . refCoords - (numPoints x dimension) array of reference coordinates (see DMGetDimension()) 3229 3230 Level: intermediate 3231 3232 .seealso: DMPlexReferenceToCoordinates() 3233 @*/ 3234 PetscErrorCode DMPlexCoordinatesToReference(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[]) 3235 { 3236 PetscInt dimC, dimR, depth, cStart, cEnd, i; 3237 DM coordDM = NULL; 3238 Vec coords; 3239 PetscFE fe = NULL; 3240 3241 PetscFunctionBegin; 3242 PetscValidHeaderSpecific(dm,DM_CLASSID,1); 3243 PetscCall(DMGetDimension(dm,&dimR)); 3244 PetscCall(DMGetCoordinateDim(dm,&dimC)); 3245 if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(0); 3246 PetscCall(DMPlexGetDepth(dm,&depth)); 3247 PetscCall(DMGetCoordinatesLocal(dm,&coords)); 3248 PetscCall(DMGetCoordinateDM(dm,&coordDM)); 3249 if (coordDM) { 3250 PetscInt coordFields; 3251 3252 PetscCall(DMGetNumFields(coordDM,&coordFields)); 3253 if (coordFields) { 3254 PetscClassId id; 3255 PetscObject disc; 3256 3257 PetscCall(DMGetField(coordDM,0,NULL,&disc)); 3258 PetscCall(PetscObjectGetClassId(disc,&id)); 3259 if (id == PETSCFE_CLASSID) { 3260 fe = (PetscFE) disc; 3261 } 3262 } 3263 } 3264 PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd)); 3265 PetscCheckFalse(cell < cStart || cell >= cEnd,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"point %D not in cell range [%D,%D)",cell,cStart,cEnd); 3266 if (!fe) { /* implicit discretization: affine or multilinear */ 3267 PetscInt coneSize; 3268 PetscBool isSimplex, isTensor; 3269 3270 PetscCall(DMPlexGetConeSize(dm,cell,&coneSize)); 3271 isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE; 3272 isTensor = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE; 3273 if (isSimplex) { 3274 PetscReal detJ, *v0, *J, *invJ; 3275 3276 PetscCall(DMGetWorkArray(dm,dimC + 2 * dimC * dimC, MPIU_REAL, &v0)); 3277 J = &v0[dimC]; 3278 invJ = &J[dimC * dimC]; 3279 PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, invJ, &detJ)); 3280 for (i = 0; i < numPoints; i++) { /* Apply the inverse affine transformation for each point */ 3281 const PetscReal x0[3] = {-1.,-1.,-1.}; 3282 3283 CoordinatesRealToRef(dimC, dimR, x0, v0, invJ, &realCoords[dimC * i], &refCoords[dimR * i]); 3284 } 3285 PetscCall(DMRestoreWorkArray(dm,dimC + 2 * dimC * dimC, MPIU_REAL, &v0)); 3286 } else if (isTensor) { 3287 PetscCall(DMPlexCoordinatesToReference_Tensor(coordDM, cell, numPoints, realCoords, refCoords, coords, dimC, dimR)); 3288 } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Unrecognized cone size %D",coneSize); 3289 } else { 3290 PetscCall(DMPlexCoordinatesToReference_FE(coordDM, fe, cell, numPoints, realCoords, refCoords, coords, dimC, dimR)); 3291 } 3292 PetscFunctionReturn(0); 3293 } 3294 3295 /*@ 3296 DMPlexReferenceToCoordinates - Map references coordinates to coordinates in the the mesh for a single element map. 3297 3298 Not collective 3299 3300 Input Parameters: 3301 + dm - The mesh, with coordinate maps defined either by a PetscDS for the coordinate DM (see DMGetCoordinateDM()) or 3302 implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or 3303 as a multilinear map for tensor-product elements 3304 . cell - the cell whose map is used. 3305 . numPoints - the number of points to locate 3306 - refCoords - (numPoints x dimension) array of reference coordinates (see DMGetDimension()) 3307 3308 Output Parameters: 3309 . realCoords - (numPoints x coordinate dimension) array of coordinates (see DMGetCoordinateDim()) 3310 3311 Level: intermediate 3312 3313 .seealso: DMPlexCoordinatesToReference() 3314 @*/ 3315 PetscErrorCode DMPlexReferenceToCoordinates(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[]) 3316 { 3317 PetscInt dimC, dimR, depth, cStart, cEnd, i; 3318 DM coordDM = NULL; 3319 Vec coords; 3320 PetscFE fe = NULL; 3321 3322 PetscFunctionBegin; 3323 PetscValidHeaderSpecific(dm,DM_CLASSID,1); 3324 PetscCall(DMGetDimension(dm,&dimR)); 3325 PetscCall(DMGetCoordinateDim(dm,&dimC)); 3326 if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(0); 3327 PetscCall(DMPlexGetDepth(dm,&depth)); 3328 PetscCall(DMGetCoordinatesLocal(dm,&coords)); 3329 PetscCall(DMGetCoordinateDM(dm,&coordDM)); 3330 if (coordDM) { 3331 PetscInt coordFields; 3332 3333 PetscCall(DMGetNumFields(coordDM,&coordFields)); 3334 if (coordFields) { 3335 PetscClassId id; 3336 PetscObject disc; 3337 3338 PetscCall(DMGetField(coordDM,0,NULL,&disc)); 3339 PetscCall(PetscObjectGetClassId(disc,&id)); 3340 if (id == PETSCFE_CLASSID) { 3341 fe = (PetscFE) disc; 3342 } 3343 } 3344 } 3345 PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd)); 3346 PetscCheckFalse(cell < cStart || cell >= cEnd,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"point %D not in cell range [%D,%D)",cell,cStart,cEnd); 3347 if (!fe) { /* implicit discretization: affine or multilinear */ 3348 PetscInt coneSize; 3349 PetscBool isSimplex, isTensor; 3350 3351 PetscCall(DMPlexGetConeSize(dm,cell,&coneSize)); 3352 isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE; 3353 isTensor = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE; 3354 if (isSimplex) { 3355 PetscReal detJ, *v0, *J; 3356 3357 PetscCall(DMGetWorkArray(dm,dimC + 2 * dimC * dimC, MPIU_REAL, &v0)); 3358 J = &v0[dimC]; 3359 PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, NULL, &detJ)); 3360 for (i = 0; i < numPoints; i++) { /* Apply the affine transformation for each point */ 3361 const PetscReal xi0[3] = {-1.,-1.,-1.}; 3362 3363 CoordinatesRefToReal(dimC, dimR, xi0, v0, J, &refCoords[dimR * i], &realCoords[dimC * i]); 3364 } 3365 PetscCall(DMRestoreWorkArray(dm,dimC + 2 * dimC * dimC, MPIU_REAL, &v0)); 3366 } else if (isTensor) { 3367 PetscCall(DMPlexReferenceToCoordinates_Tensor(coordDM, cell, numPoints, refCoords, realCoords, coords, dimC, dimR)); 3368 } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Unrecognized cone size %D",coneSize); 3369 } else { 3370 PetscCall(DMPlexReferenceToCoordinates_FE(coordDM, fe, cell, numPoints, refCoords, realCoords, coords, dimC, dimR)); 3371 } 3372 PetscFunctionReturn(0); 3373 } 3374 3375 /*@C 3376 DMPlexRemapGeometry - This function maps the original DM coordinates to new coordinates. 3377 3378 Not collective 3379 3380 Input Parameters: 3381 + dm - The DM 3382 . time - The time 3383 - func - The function transforming current coordinates to new coordaintes 3384 3385 Calling sequence of func: 3386 $ func(PetscInt dim, PetscInt Nf, PetscInt NfAux, 3387 $ const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], 3388 $ const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], 3389 $ PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f[]); 3390 3391 + dim - The spatial dimension 3392 . Nf - The number of input fields (here 1) 3393 . NfAux - The number of input auxiliary fields 3394 . uOff - The offset of the coordinates in u[] (here 0) 3395 . uOff_x - The offset of the coordinates in u_x[] (here 0) 3396 . u - The coordinate values at this point in space 3397 . u_t - The coordinate time derivative at this point in space (here NULL) 3398 . u_x - The coordinate derivatives at this point in space 3399 . aOff - The offset of each auxiliary field in u[] 3400 . aOff_x - The offset of each auxiliary field in u_x[] 3401 . a - The auxiliary field values at this point in space 3402 . a_t - The auxiliary field time derivative at this point in space (or NULL) 3403 . a_x - The auxiliary field derivatives at this point in space 3404 . t - The current time 3405 . x - The coordinates of this point (here not used) 3406 . numConstants - The number of constants 3407 . constants - The value of each constant 3408 - f - The new coordinates at this point in space 3409 3410 Level: intermediate 3411 3412 .seealso: DMGetCoordinates(), DMGetCoordinatesLocal(), DMGetCoordinateDM(), DMProjectFieldLocal(), DMProjectFieldLabelLocal() 3413 @*/ 3414 PetscErrorCode DMPlexRemapGeometry(DM dm, PetscReal time, 3415 void (*func)(PetscInt, PetscInt, PetscInt, 3416 const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], 3417 const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], 3418 PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[])) 3419 { 3420 DM cdm; 3421 DMField cf; 3422 Vec lCoords, tmpCoords; 3423 3424 PetscFunctionBegin; 3425 PetscCall(DMGetCoordinateDM(dm, &cdm)); 3426 PetscCall(DMGetCoordinatesLocal(dm, &lCoords)); 3427 PetscCall(DMGetLocalVector(cdm, &tmpCoords)); 3428 PetscCall(VecCopy(lCoords, tmpCoords)); 3429 /* We have to do the coordinate field manually right now since the coordinate DM will not have its own */ 3430 PetscCall(DMGetCoordinateField(dm, &cf)); 3431 cdm->coordinateField = cf; 3432 PetscCall(DMProjectFieldLocal(cdm, time, tmpCoords, &func, INSERT_VALUES, lCoords)); 3433 cdm->coordinateField = NULL; 3434 PetscCall(DMRestoreLocalVector(cdm, &tmpCoords)); 3435 PetscCall(DMSetCoordinatesLocal(dm, lCoords)); 3436 PetscFunctionReturn(0); 3437 } 3438 3439 /* Shear applies the transformation, assuming we fix z, 3440 / 1 0 m_0 \ 3441 | 0 1 m_1 | 3442 \ 0 0 1 / 3443 */ 3444 static void f0_shear(PetscInt dim, PetscInt Nf, PetscInt NfAux, 3445 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], 3446 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], 3447 PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar coords[]) 3448 { 3449 const PetscInt Nc = uOff[1]-uOff[0]; 3450 const PetscInt ax = (PetscInt) PetscRealPart(constants[0]); 3451 PetscInt c; 3452 3453 for (c = 0; c < Nc; ++c) { 3454 coords[c] = u[c] + constants[c+1]*u[ax]; 3455 } 3456 } 3457 3458 /*@C 3459 DMPlexShearGeometry - This shears the domain, meaning adds a multiple of the shear coordinate to all other coordinates. 3460 3461 Not collective 3462 3463 Input Parameters: 3464 + dm - The DM 3465 . direction - The shear coordinate direction, e.g. 0 is the x-axis 3466 - multipliers - The multiplier m for each direction which is not the shear direction 3467 3468 Level: intermediate 3469 3470 .seealso: DMPlexRemapGeometry() 3471 @*/ 3472 PetscErrorCode DMPlexShearGeometry(DM dm, DMDirection direction, PetscReal multipliers[]) 3473 { 3474 DM cdm; 3475 PetscDS cds; 3476 PetscObject obj; 3477 PetscClassId id; 3478 PetscScalar *moduli; 3479 const PetscInt dir = (PetscInt) direction; 3480 PetscInt dE, d, e; 3481 3482 PetscFunctionBegin; 3483 PetscCall(DMGetCoordinateDM(dm, &cdm)); 3484 PetscCall(DMGetCoordinateDim(dm, &dE)); 3485 PetscCall(PetscMalloc1(dE+1, &moduli)); 3486 moduli[0] = dir; 3487 for (d = 0, e = 0; d < dE; ++d) moduli[d+1] = d == dir ? 0.0 : (multipliers ? multipliers[e++] : 1.0); 3488 PetscCall(DMGetDS(cdm, &cds)); 3489 PetscCall(PetscDSGetDiscretization(cds, 0, &obj)); 3490 PetscCall(PetscObjectGetClassId(obj, &id)); 3491 if (id != PETSCFE_CLASSID) { 3492 Vec lCoords; 3493 PetscSection cSection; 3494 PetscScalar *coords; 3495 PetscInt vStart, vEnd, v; 3496 3497 PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd)); 3498 PetscCall(DMGetCoordinateSection(dm, &cSection)); 3499 PetscCall(DMGetCoordinatesLocal(dm, &lCoords)); 3500 PetscCall(VecGetArray(lCoords, &coords)); 3501 for (v = vStart; v < vEnd; ++v) { 3502 PetscReal ds; 3503 PetscInt off, c; 3504 3505 PetscCall(PetscSectionGetOffset(cSection, v, &off)); 3506 ds = PetscRealPart(coords[off+dir]); 3507 for (c = 0; c < dE; ++c) coords[off+c] += moduli[c]*ds; 3508 } 3509 PetscCall(VecRestoreArray(lCoords, &coords)); 3510 } else { 3511 PetscCall(PetscDSSetConstants(cds, dE+1, moduli)); 3512 PetscCall(DMPlexRemapGeometry(dm, 0.0, f0_shear)); 3513 } 3514 PetscCall(PetscFree(moduli)); 3515 PetscFunctionReturn(0); 3516 } 3517