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