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