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