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