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