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