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