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. This is a 2593 one-dimensional array of size $cdim * Nq$ where $cdim$ is the dimension of the `DM` coordinate space and $Nq$ is the number of quadrature points 2594 . J - the Jacobian of the transform from the reference element at each quadrature point. This is a one-dimensional array of size $Nq * cdim * cdim$ containing 2595 each Jacobian in column-major order. 2596 . invJ - the inverse of the Jacobian at each quadrature point. This is a one-dimensional array of size $Nq * cdim * cdim$ containing 2597 each inverse Jacobian in column-major order. 2598 - detJ - the Jacobian determinant at each quadrature point. This is a one-dimensional array of size $Nq$. 2599 2600 Level: advanced 2601 2602 Note: 2603 Implicit cell geometry must be used when the topological mesh dimension is not equal to the coordinate dimension, for instance for embedded manifolds. 2604 2605 .seealso: `DMPLEX`, `DMGetCoordinateSection()`, `DMGetCoordinates()` 2606 @*/ 2607 PetscErrorCode DMPlexComputeCellGeometryFEM(DM dm, PetscInt cell, PetscQuadrature quad, PetscReal v[], PetscReal J[], PetscReal invJ[], PetscReal detJ[]) 2608 { 2609 DM cdm; 2610 PetscFE fe = NULL; 2611 PetscInt dim, cdim; 2612 2613 PetscFunctionBegin; 2614 PetscAssertPointer(detJ, 7); 2615 PetscCall(DMGetDimension(dm, &dim)); 2616 PetscCall(DMGetCoordinateDim(dm, &cdim)); 2617 PetscCall(DMGetCoordinateDM(dm, &cdm)); 2618 if (cdm) { 2619 PetscClassId id; 2620 PetscInt numFields; 2621 PetscDS prob; 2622 PetscObject disc; 2623 2624 PetscCall(DMGetNumFields(cdm, &numFields)); 2625 if (numFields) { 2626 PetscCall(DMGetDS(cdm, &prob)); 2627 PetscCall(PetscDSGetDiscretization(prob, 0, &disc)); 2628 PetscCall(PetscObjectGetClassId(disc, &id)); 2629 if (id == PETSCFE_CLASSID) fe = (PetscFE)disc; 2630 } 2631 } 2632 if (!fe || (dim != cdim)) PetscCall(DMPlexComputeCellGeometryFEM_Implicit(dm, cell, quad, v, J, invJ, detJ)); 2633 else PetscCall(DMPlexComputeCellGeometryFEM_FE(dm, fe, cell, quad, v, J, invJ, detJ)); 2634 PetscFunctionReturn(PETSC_SUCCESS); 2635 } 2636 2637 static PetscErrorCode DMPlexComputeGeometryFVM_0D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[]) 2638 { 2639 PetscSection coordSection; 2640 Vec coordinates; 2641 const PetscScalar *coords = NULL; 2642 PetscInt d, dof, off; 2643 2644 PetscFunctionBegin; 2645 PetscCall(DMGetCoordinatesLocal(dm, &coordinates)); 2646 PetscCall(DMGetCoordinateSection(dm, &coordSection)); 2647 PetscCall(VecGetArrayRead(coordinates, &coords)); 2648 2649 /* for a point the centroid is just the coord */ 2650 if (centroid) { 2651 PetscCall(PetscSectionGetDof(coordSection, cell, &dof)); 2652 PetscCall(PetscSectionGetOffset(coordSection, cell, &off)); 2653 for (d = 0; d < dof; d++) centroid[d] = PetscRealPart(coords[off + d]); 2654 } 2655 if (normal) { 2656 const PetscInt *support, *cones; 2657 PetscInt supportSize; 2658 PetscReal norm, sign; 2659 2660 /* compute the norm based upon the support centroids */ 2661 PetscCall(DMPlexGetSupportSize(dm, cell, &supportSize)); 2662 PetscCall(DMPlexGetSupport(dm, cell, &support)); 2663 PetscCall(DMPlexComputeCellGeometryFVM(dm, support[0], NULL, normal, NULL)); 2664 2665 /* Take the normal from the centroid of the support to the vertex*/ 2666 PetscCall(PetscSectionGetDof(coordSection, cell, &dof)); 2667 PetscCall(PetscSectionGetOffset(coordSection, cell, &off)); 2668 for (d = 0; d < dof; d++) normal[d] -= PetscRealPart(coords[off + d]); 2669 2670 /* Determine the sign of the normal based upon its location in the support */ 2671 PetscCall(DMPlexGetCone(dm, support[0], &cones)); 2672 sign = cones[0] == cell ? 1.0 : -1.0; 2673 2674 norm = DMPlex_NormD_Internal(dim, normal); 2675 for (d = 0; d < dim; ++d) normal[d] /= (norm * sign); 2676 } 2677 if (vol) *vol = 1.0; 2678 PetscCall(VecRestoreArrayRead(coordinates, &coords)); 2679 PetscFunctionReturn(PETSC_SUCCESS); 2680 } 2681 2682 static PetscErrorCode DMPlexComputeGeometryFVM_1D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[]) 2683 { 2684 const PetscScalar *array; 2685 PetscScalar *coords = NULL; 2686 PetscInt cdim, coordSize, d; 2687 PetscBool isDG; 2688 2689 PetscFunctionBegin; 2690 PetscCall(DMGetCoordinateDim(dm, &cdim)); 2691 PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords)); 2692 PetscCheck(coordSize == cdim * 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge has %" PetscInt_FMT " coordinates != %" PetscInt_FMT, coordSize, cdim * 2); 2693 if (centroid) { 2694 for (d = 0; d < cdim; ++d) centroid[d] = 0.5 * PetscRealPart(coords[d] + coords[cdim + d]); 2695 } 2696 if (normal) { 2697 PetscReal norm; 2698 2699 switch (cdim) { 2700 case 3: 2701 normal[2] = 0.; /* fall through */ 2702 case 2: 2703 normal[0] = -PetscRealPart(coords[1] - coords[cdim + 1]); 2704 normal[1] = PetscRealPart(coords[0] - coords[cdim + 0]); 2705 break; 2706 case 1: 2707 normal[0] = 1.0; 2708 break; 2709 default: 2710 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Dimension %" PetscInt_FMT " not supported", cdim); 2711 } 2712 norm = DMPlex_NormD_Internal(cdim, normal); 2713 for (d = 0; d < cdim; ++d) normal[d] /= norm; 2714 } 2715 if (vol) { 2716 *vol = 0.0; 2717 for (d = 0; d < cdim; ++d) *vol += PetscSqr(PetscRealPart(coords[d] - coords[cdim + d])); 2718 *vol = PetscSqrtReal(*vol); 2719 } 2720 PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords)); 2721 PetscFunctionReturn(PETSC_SUCCESS); 2722 } 2723 2724 /* Centroid_i = (\sum_n A_n Cn_i) / A */ 2725 static PetscErrorCode DMPlexComputeGeometryFVM_2D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[]) 2726 { 2727 DMPolytopeType ct; 2728 const PetscScalar *array; 2729 PetscScalar *coords = NULL; 2730 PetscInt coordSize; 2731 PetscBool isDG; 2732 PetscInt fv[4] = {0, 1, 2, 3}; 2733 PetscInt cdim, numCorners, p, d; 2734 2735 PetscFunctionBegin; 2736 /* Must check for hybrid cells because prisms have a different orientation scheme */ 2737 PetscCall(DMPlexGetCellType(dm, cell, &ct)); 2738 switch (ct) { 2739 case DM_POLYTOPE_SEG_PRISM_TENSOR: 2740 fv[2] = 3; 2741 fv[3] = 2; 2742 break; 2743 default: 2744 break; 2745 } 2746 PetscCall(DMGetCoordinateDim(dm, &cdim)); 2747 PetscCall(DMPlexGetConeSize(dm, cell, &numCorners)); 2748 PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords)); 2749 { 2750 PetscReal c[3] = {0., 0., 0.}, n[3] = {0., 0., 0.}, origin[3] = {0., 0., 0.}, norm; 2751 2752 for (d = 0; d < cdim; d++) origin[d] = PetscRealPart(coords[d]); 2753 for (p = 0; p < numCorners - 2; ++p) { 2754 PetscReal e0[3] = {0., 0., 0.}, e1[3] = {0., 0., 0.}; 2755 for (d = 0; d < cdim; d++) { 2756 e0[d] = PetscRealPart(coords[cdim * fv[p + 1] + d]) - origin[d]; 2757 e1[d] = PetscRealPart(coords[cdim * fv[p + 2] + d]) - origin[d]; 2758 } 2759 const PetscReal dx = e0[1] * e1[2] - e0[2] * e1[1]; 2760 const PetscReal dy = e0[2] * e1[0] - e0[0] * e1[2]; 2761 const PetscReal dz = e0[0] * e1[1] - e0[1] * e1[0]; 2762 const PetscReal a = PetscSqrtReal(dx * dx + dy * dy + dz * dz); 2763 2764 n[0] += dx; 2765 n[1] += dy; 2766 n[2] += dz; 2767 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.; 2768 } 2769 norm = PetscSqrtReal(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]); 2770 // Allow zero volume cells 2771 if (norm != 0) { 2772 n[0] /= norm; 2773 n[1] /= norm; 2774 n[2] /= norm; 2775 c[0] /= norm; 2776 c[1] /= norm; 2777 c[2] /= norm; 2778 } 2779 if (vol) *vol = 0.5 * norm; 2780 if (centroid) 2781 for (d = 0; d < cdim; ++d) centroid[d] = c[d]; 2782 if (normal) 2783 for (d = 0; d < cdim; ++d) normal[d] = n[d]; 2784 } 2785 PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords)); 2786 PetscFunctionReturn(PETSC_SUCCESS); 2787 } 2788 2789 /* Centroid_i = (\sum_n V_n Cn_i) / V */ 2790 static PetscErrorCode DMPlexComputeGeometryFVM_3D_Internal(DM dm, PetscInt dim, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[]) 2791 { 2792 DMPolytopeType ct; 2793 const PetscScalar *array; 2794 PetscScalar *coords = NULL; 2795 PetscInt coordSize; 2796 PetscBool isDG; 2797 PetscReal vsum = 0.0, vtmp, coordsTmp[3 * 3], origin[3]; 2798 const PetscInt order[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; 2799 const PetscInt *cone, *faceSizes, *faces; 2800 const DMPolytopeType *faceTypes; 2801 PetscBool isHybrid = PETSC_FALSE; 2802 PetscInt numFaces, f, fOff = 0, p, d; 2803 2804 PetscFunctionBegin; 2805 PetscCheck(dim <= 3, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No support for dim %" PetscInt_FMT " > 3", dim); 2806 /* Must check for hybrid cells because prisms have a different orientation scheme */ 2807 PetscCall(DMPlexGetCellType(dm, cell, &ct)); 2808 switch (ct) { 2809 case DM_POLYTOPE_POINT_PRISM_TENSOR: 2810 case DM_POLYTOPE_SEG_PRISM_TENSOR: 2811 case DM_POLYTOPE_TRI_PRISM_TENSOR: 2812 case DM_POLYTOPE_QUAD_PRISM_TENSOR: 2813 isHybrid = PETSC_TRUE; 2814 default: 2815 break; 2816 } 2817 2818 if (centroid) 2819 for (d = 0; d < dim; ++d) centroid[d] = 0.0; 2820 PetscCall(DMPlexGetCone(dm, cell, &cone)); 2821 2822 // Using the closure of faces for coordinates does not work in periodic geometries, so we index into the cell coordinates 2823 PetscCall(DMPlexGetRawFaces_Internal(dm, ct, order, &numFaces, &faceTypes, &faceSizes, &faces)); 2824 PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords)); 2825 for (f = 0; f < numFaces; ++f) { 2826 PetscBool flip = isHybrid && f == 0 ? PETSC_TRUE : PETSC_FALSE; /* The first hybrid face is reversed */ 2827 2828 // If using zero as the origin vertex for each tetrahedron, an element far from the origin will have positive and 2829 // negative volumes that nearly cancel, thus incurring rounding error. Here we define origin[] as the first vertex 2830 // so that all tetrahedra have positive volume. 2831 if (f == 0) 2832 for (d = 0; d < dim; d++) origin[d] = PetscRealPart(coords[d]); 2833 switch (faceTypes[f]) { 2834 case DM_POLYTOPE_TRIANGLE: 2835 for (d = 0; d < dim; ++d) { 2836 coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + 0] * dim + d]) - origin[d]; 2837 coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + 1] * dim + d]) - origin[d]; 2838 coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + 2] * dim + d]) - origin[d]; 2839 } 2840 Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp); 2841 if (flip) vtmp = -vtmp; 2842 vsum += vtmp; 2843 if (centroid) { /* Centroid of OABC = (a+b+c)/4 */ 2844 for (d = 0; d < dim; ++d) { 2845 for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp; 2846 } 2847 } 2848 break; 2849 case DM_POLYTOPE_QUADRILATERAL: 2850 case DM_POLYTOPE_SEG_PRISM_TENSOR: { 2851 PetscInt fv[4] = {0, 1, 2, 3}; 2852 2853 /* Side faces for hybrid cells are stored as tensor products */ 2854 if (isHybrid && f > 1) { 2855 fv[2] = 3; 2856 fv[3] = 2; 2857 } 2858 /* DO FOR PYRAMID */ 2859 /* First tet */ 2860 for (d = 0; d < dim; ++d) { 2861 coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + fv[0]] * dim + d]) - origin[d]; 2862 coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + fv[1]] * dim + d]) - origin[d]; 2863 coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + fv[3]] * dim + d]) - origin[d]; 2864 } 2865 Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp); 2866 if (flip) vtmp = -vtmp; 2867 vsum += vtmp; 2868 if (centroid) { 2869 for (d = 0; d < dim; ++d) { 2870 for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp; 2871 } 2872 } 2873 /* Second tet */ 2874 for (d = 0; d < dim; ++d) { 2875 coordsTmp[0 * dim + d] = PetscRealPart(coords[faces[fOff + fv[1]] * dim + d]) - origin[d]; 2876 coordsTmp[1 * dim + d] = PetscRealPart(coords[faces[fOff + fv[2]] * dim + d]) - origin[d]; 2877 coordsTmp[2 * dim + d] = PetscRealPart(coords[faces[fOff + fv[3]] * dim + d]) - origin[d]; 2878 } 2879 Volume_Tetrahedron_Origin_Internal(&vtmp, coordsTmp); 2880 if (flip) vtmp = -vtmp; 2881 vsum += vtmp; 2882 if (centroid) { 2883 for (d = 0; d < dim; ++d) { 2884 for (p = 0; p < 3; ++p) centroid[d] += coordsTmp[p * dim + d] * vtmp; 2885 } 2886 } 2887 break; 2888 } 2889 default: 2890 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle face %" PetscInt_FMT " of type %s", cone[f], DMPolytopeTypes[ct]); 2891 } 2892 fOff += faceSizes[f]; 2893 } 2894 PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, order, &numFaces, &faceTypes, &faceSizes, &faces)); 2895 PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &coordSize, &array, &coords)); 2896 if (vol) *vol = PetscAbsReal(vsum); 2897 if (normal) 2898 for (d = 0; d < dim; ++d) normal[d] = 0.0; 2899 if (centroid) 2900 for (d = 0; d < dim; ++d) centroid[d] = centroid[d] / (vsum * 4) + origin[d]; 2901 PetscFunctionReturn(PETSC_SUCCESS); 2902 } 2903 2904 /*@C 2905 DMPlexComputeCellGeometryFVM - Compute the volume for a given cell 2906 2907 Collective 2908 2909 Input Parameters: 2910 + dm - the `DMPLEX` 2911 - cell - the cell 2912 2913 Output Parameters: 2914 + vol - the cell volume 2915 . centroid - the cell centroid 2916 - normal - the cell normal, if appropriate 2917 2918 Level: advanced 2919 2920 .seealso: `DMPLEX`, `DMGetCoordinateSection()`, `DMGetCoordinates()` 2921 @*/ 2922 PetscErrorCode DMPlexComputeCellGeometryFVM(DM dm, PetscInt cell, PetscReal *vol, PetscReal centroid[], PetscReal normal[]) 2923 { 2924 PetscInt depth, dim; 2925 2926 PetscFunctionBegin; 2927 PetscCall(DMPlexGetDepth(dm, &depth)); 2928 PetscCall(DMGetDimension(dm, &dim)); 2929 PetscCheck(depth == dim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh must be interpolated"); 2930 PetscCall(DMPlexGetPointDepth(dm, cell, &depth)); 2931 switch (depth) { 2932 case 0: 2933 PetscCall(DMPlexComputeGeometryFVM_0D_Internal(dm, dim, cell, vol, centroid, normal)); 2934 break; 2935 case 1: 2936 PetscCall(DMPlexComputeGeometryFVM_1D_Internal(dm, dim, cell, vol, centroid, normal)); 2937 break; 2938 case 2: 2939 PetscCall(DMPlexComputeGeometryFVM_2D_Internal(dm, dim, cell, vol, centroid, normal)); 2940 break; 2941 case 3: 2942 PetscCall(DMPlexComputeGeometryFVM_3D_Internal(dm, dim, cell, vol, centroid, normal)); 2943 break; 2944 default: 2945 SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unsupported dimension %" PetscInt_FMT " (depth %" PetscInt_FMT ") for element geometry computation", dim, depth); 2946 } 2947 PetscFunctionReturn(PETSC_SUCCESS); 2948 } 2949 2950 /*@ 2951 DMPlexComputeGeometryFVM - Computes the cell and face geometry for a finite volume method 2952 2953 Input Parameter: 2954 . dm - The `DMPLEX` 2955 2956 Output Parameters: 2957 + cellgeom - A `Vec` of `PetscFVCellGeom` data 2958 - facegeom - A `Vec` of `PetscFVFaceGeom` data 2959 2960 Level: developer 2961 2962 .seealso: `DMPLEX`, `PetscFVFaceGeom`, `PetscFVCellGeom` 2963 @*/ 2964 PetscErrorCode DMPlexComputeGeometryFVM(DM dm, Vec *cellgeom, Vec *facegeom) 2965 { 2966 DM dmFace, dmCell; 2967 DMLabel ghostLabel; 2968 PetscSection sectionFace, sectionCell; 2969 PetscSection coordSection; 2970 Vec coordinates; 2971 PetscScalar *fgeom, *cgeom; 2972 PetscReal minradius, gminradius; 2973 PetscInt dim, cStart, cEnd, cEndInterior, c, fStart, fEnd, f; 2974 2975 PetscFunctionBegin; 2976 PetscCall(DMGetDimension(dm, &dim)); 2977 PetscCall(DMGetCoordinateSection(dm, &coordSection)); 2978 PetscCall(DMGetCoordinatesLocal(dm, &coordinates)); 2979 /* Make cell centroids and volumes */ 2980 PetscCall(DMClone(dm, &dmCell)); 2981 PetscCall(DMSetCoordinateSection(dmCell, PETSC_DETERMINE, coordSection)); 2982 PetscCall(DMSetCoordinatesLocal(dmCell, coordinates)); 2983 PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), §ionCell)); 2984 PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd)); 2985 PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL)); 2986 PetscCall(PetscSectionSetChart(sectionCell, cStart, cEnd)); 2987 for (c = cStart; c < cEnd; ++c) PetscCall(PetscSectionSetDof(sectionCell, c, (PetscInt)PetscCeilReal(((PetscReal)sizeof(PetscFVCellGeom)) / sizeof(PetscScalar)))); 2988 PetscCall(PetscSectionSetUp(sectionCell)); 2989 PetscCall(DMSetLocalSection(dmCell, sectionCell)); 2990 PetscCall(PetscSectionDestroy(§ionCell)); 2991 PetscCall(DMCreateLocalVector(dmCell, cellgeom)); 2992 if (cEndInterior < 0) cEndInterior = cEnd; 2993 PetscCall(VecGetArray(*cellgeom, &cgeom)); 2994 for (c = cStart; c < cEndInterior; ++c) { 2995 PetscFVCellGeom *cg; 2996 2997 PetscCall(DMPlexPointLocalRef(dmCell, c, cgeom, &cg)); 2998 PetscCall(PetscArrayzero(cg, 1)); 2999 PetscCall(DMPlexComputeCellGeometryFVM(dmCell, c, &cg->volume, cg->centroid, NULL)); 3000 } 3001 /* Compute face normals and minimum cell radius */ 3002 PetscCall(DMClone(dm, &dmFace)); 3003 PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), §ionFace)); 3004 PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 3005 PetscCall(PetscSectionSetChart(sectionFace, fStart, fEnd)); 3006 for (f = fStart; f < fEnd; ++f) PetscCall(PetscSectionSetDof(sectionFace, f, (PetscInt)PetscCeilReal(((PetscReal)sizeof(PetscFVFaceGeom)) / sizeof(PetscScalar)))); 3007 PetscCall(PetscSectionSetUp(sectionFace)); 3008 PetscCall(DMSetLocalSection(dmFace, sectionFace)); 3009 PetscCall(PetscSectionDestroy(§ionFace)); 3010 PetscCall(DMCreateLocalVector(dmFace, facegeom)); 3011 PetscCall(VecGetArray(*facegeom, &fgeom)); 3012 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 3013 minradius = PETSC_MAX_REAL; 3014 for (f = fStart; f < fEnd; ++f) { 3015 PetscFVFaceGeom *fg; 3016 PetscReal area; 3017 const PetscInt *cells; 3018 PetscInt ncells, ghost = -1, d, numChildren; 3019 3020 if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost)); 3021 PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL)); 3022 PetscCall(DMPlexGetSupport(dm, f, &cells)); 3023 PetscCall(DMPlexGetSupportSize(dm, f, &ncells)); 3024 /* It is possible to get a face with no support when using partition overlap */ 3025 if (!ncells || ghost >= 0 || numChildren) continue; 3026 PetscCall(DMPlexPointLocalRef(dmFace, f, fgeom, &fg)); 3027 PetscCall(DMPlexComputeCellGeometryFVM(dm, f, &area, fg->centroid, fg->normal)); 3028 for (d = 0; d < dim; ++d) fg->normal[d] *= area; 3029 /* Flip face orientation if necessary to match ordering in support, and Update minimum radius */ 3030 { 3031 PetscFVCellGeom *cL, *cR; 3032 PetscReal *lcentroid, *rcentroid; 3033 PetscReal l[3], r[3], v[3]; 3034 3035 PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cgeom, &cL)); 3036 lcentroid = cells[0] >= cEndInterior ? fg->centroid : cL->centroid; 3037 if (ncells > 1) { 3038 PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cgeom, &cR)); 3039 rcentroid = cells[1] >= cEndInterior ? fg->centroid : cR->centroid; 3040 } else { 3041 rcentroid = fg->centroid; 3042 } 3043 PetscCall(DMLocalizeCoordinateReal_Internal(dm, dim, fg->centroid, lcentroid, l)); 3044 PetscCall(DMLocalizeCoordinateReal_Internal(dm, dim, fg->centroid, rcentroid, r)); 3045 DMPlex_WaxpyD_Internal(dim, -1, l, r, v); 3046 if (DMPlex_DotRealD_Internal(dim, fg->normal, v) < 0) { 3047 for (d = 0; d < dim; ++d) fg->normal[d] = -fg->normal[d]; 3048 } 3049 if (DMPlex_DotRealD_Internal(dim, fg->normal, v) <= 0) { 3050 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]); 3051 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]); 3052 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Direction for face %" PetscInt_FMT " could not be fixed", f); 3053 } 3054 if (cells[0] < cEndInterior) { 3055 DMPlex_WaxpyD_Internal(dim, -1, fg->centroid, cL->centroid, v); 3056 minradius = PetscMin(minradius, DMPlex_NormD_Internal(dim, v)); 3057 } 3058 if (ncells > 1 && cells[1] < cEndInterior) { 3059 DMPlex_WaxpyD_Internal(dim, -1, fg->centroid, cR->centroid, v); 3060 minradius = PetscMin(minradius, DMPlex_NormD_Internal(dim, v)); 3061 } 3062 } 3063 } 3064 PetscCallMPI(MPIU_Allreduce(&minradius, &gminradius, 1, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm))); 3065 PetscCall(DMPlexSetMinRadius(dm, gminradius)); 3066 /* Compute centroids of ghost cells */ 3067 for (c = cEndInterior; c < cEnd; ++c) { 3068 PetscFVFaceGeom *fg; 3069 const PetscInt *cone, *support; 3070 PetscInt coneSize, supportSize, s; 3071 3072 PetscCall(DMPlexGetConeSize(dmCell, c, &coneSize)); 3073 PetscCheck(coneSize == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Ghost cell %" PetscInt_FMT " has cone size %" PetscInt_FMT " != 1", c, coneSize); 3074 PetscCall(DMPlexGetCone(dmCell, c, &cone)); 3075 PetscCall(DMPlexGetSupportSize(dmCell, cone[0], &supportSize)); 3076 PetscCheck(supportSize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[0], supportSize); 3077 PetscCall(DMPlexGetSupport(dmCell, cone[0], &support)); 3078 PetscCall(DMPlexPointLocalRef(dmFace, cone[0], fgeom, &fg)); 3079 for (s = 0; s < 2; ++s) { 3080 /* Reflect ghost centroid across plane of face */ 3081 if (support[s] == c) { 3082 PetscFVCellGeom *ci; 3083 PetscFVCellGeom *cg; 3084 PetscReal c2f[3], a; 3085 3086 PetscCall(DMPlexPointLocalRead(dmCell, support[(s + 1) % 2], cgeom, &ci)); 3087 DMPlex_WaxpyD_Internal(dim, -1, ci->centroid, fg->centroid, c2f); /* cell to face centroid */ 3088 a = DMPlex_DotRealD_Internal(dim, c2f, fg->normal) / DMPlex_DotRealD_Internal(dim, fg->normal, fg->normal); 3089 PetscCall(DMPlexPointLocalRef(dmCell, support[s], cgeom, &cg)); 3090 DMPlex_WaxpyD_Internal(dim, 2 * a, fg->normal, ci->centroid, cg->centroid); 3091 cg->volume = ci->volume; 3092 } 3093 } 3094 } 3095 PetscCall(VecRestoreArray(*facegeom, &fgeom)); 3096 PetscCall(VecRestoreArray(*cellgeom, &cgeom)); 3097 PetscCall(DMDestroy(&dmCell)); 3098 PetscCall(DMDestroy(&dmFace)); 3099 PetscFunctionReturn(PETSC_SUCCESS); 3100 } 3101 3102 /*@ 3103 DMPlexGetMinRadius - Returns the minimum distance from any cell centroid to a face 3104 3105 Not Collective 3106 3107 Input Parameter: 3108 . dm - the `DMPLEX` 3109 3110 Output Parameter: 3111 . minradius - the minimum cell radius 3112 3113 Level: developer 3114 3115 .seealso: `DMPLEX`, `DMGetCoordinates()` 3116 @*/ 3117 PetscErrorCode DMPlexGetMinRadius(DM dm, PetscReal *minradius) 3118 { 3119 PetscFunctionBegin; 3120 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 3121 PetscAssertPointer(minradius, 2); 3122 *minradius = ((DM_Plex *)dm->data)->minradius; 3123 PetscFunctionReturn(PETSC_SUCCESS); 3124 } 3125 3126 /*@ 3127 DMPlexSetMinRadius - Sets the minimum distance from the cell centroid to a face 3128 3129 Logically Collective 3130 3131 Input Parameters: 3132 + dm - the `DMPLEX` 3133 - minradius - the minimum cell radius 3134 3135 Level: developer 3136 3137 .seealso: `DMPLEX`, `DMSetCoordinates()` 3138 @*/ 3139 PetscErrorCode DMPlexSetMinRadius(DM dm, PetscReal minradius) 3140 { 3141 PetscFunctionBegin; 3142 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 3143 ((DM_Plex *)dm->data)->minradius = minradius; 3144 PetscFunctionReturn(PETSC_SUCCESS); 3145 } 3146 3147 static PetscErrorCode BuildGradientReconstruction_Internal(DM dm, PetscFV fvm, DM dmFace, PetscScalar *fgeom, DM dmCell, PetscScalar *cgeom) 3148 { 3149 DMLabel ghostLabel; 3150 PetscScalar *dx, *grad, **gref; 3151 PetscInt dim, cStart, cEnd, c, cEndInterior, maxNumFaces; 3152 3153 PetscFunctionBegin; 3154 PetscCall(DMGetDimension(dm, &dim)); 3155 PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd)); 3156 PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL)); 3157 cEndInterior = cEndInterior < 0 ? cEnd : cEndInterior; 3158 PetscCall(DMPlexGetMaxSizes(dm, &maxNumFaces, NULL)); 3159 PetscCall(PetscFVLeastSquaresSetMaxFaces(fvm, maxNumFaces)); 3160 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 3161 PetscCall(PetscMalloc3(maxNumFaces * dim, &dx, maxNumFaces * dim, &grad, maxNumFaces, &gref)); 3162 for (c = cStart; c < cEndInterior; c++) { 3163 const PetscInt *faces; 3164 PetscInt numFaces, usedFaces, f, d; 3165 PetscFVCellGeom *cg; 3166 PetscBool boundary; 3167 PetscInt ghost; 3168 3169 // do not attempt to compute a gradient reconstruction stencil in a ghost cell. It will never be used 3170 PetscCall(DMLabelGetValue(ghostLabel, c, &ghost)); 3171 if (ghost >= 0) continue; 3172 3173 PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg)); 3174 PetscCall(DMPlexGetConeSize(dm, c, &numFaces)); 3175 PetscCall(DMPlexGetCone(dm, c, &faces)); 3176 PetscCheck(numFaces >= dim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Cell %" PetscInt_FMT " has only %" PetscInt_FMT " faces, not enough for gradient reconstruction", c, numFaces); 3177 for (f = 0, usedFaces = 0; f < numFaces; ++f) { 3178 PetscFVCellGeom *cg1; 3179 PetscFVFaceGeom *fg; 3180 const PetscInt *fcells; 3181 PetscInt ncell, side; 3182 3183 PetscCall(DMLabelGetValue(ghostLabel, faces[f], &ghost)); 3184 PetscCall(DMIsBoundaryPoint(dm, faces[f], &boundary)); 3185 if ((ghost >= 0) || boundary) continue; 3186 PetscCall(DMPlexGetSupport(dm, faces[f], &fcells)); 3187 side = (c != fcells[0]); /* c is on left=0 or right=1 of face */ 3188 ncell = fcells[!side]; /* the neighbor */ 3189 PetscCall(DMPlexPointLocalRef(dmFace, faces[f], fgeom, &fg)); 3190 PetscCall(DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1)); 3191 for (d = 0; d < dim; ++d) dx[usedFaces * dim + d] = cg1->centroid[d] - cg->centroid[d]; 3192 gref[usedFaces++] = fg->grad[side]; /* Gradient reconstruction term will go here */ 3193 } 3194 PetscCheck(usedFaces, PETSC_COMM_SELF, PETSC_ERR_USER, "Mesh contains isolated cell (no neighbors). Is it intentional?"); 3195 PetscCall(PetscFVComputeGradient(fvm, usedFaces, dx, grad)); 3196 for (f = 0, usedFaces = 0; f < numFaces; ++f) { 3197 PetscCall(DMLabelGetValue(ghostLabel, faces[f], &ghost)); 3198 PetscCall(DMIsBoundaryPoint(dm, faces[f], &boundary)); 3199 if ((ghost >= 0) || boundary) continue; 3200 for (d = 0; d < dim; ++d) gref[usedFaces][d] = grad[usedFaces * dim + d]; 3201 ++usedFaces; 3202 } 3203 } 3204 PetscCall(PetscFree3(dx, grad, gref)); 3205 PetscFunctionReturn(PETSC_SUCCESS); 3206 } 3207 3208 static PetscErrorCode BuildGradientReconstruction_Internal_Tree(DM dm, PetscFV fvm, DM dmFace, PetscScalar *fgeom, DM dmCell, PetscScalar *cgeom) 3209 { 3210 DMLabel ghostLabel; 3211 PetscScalar *dx, *grad, **gref; 3212 PetscInt dim, cStart, cEnd, c, cEndInterior, fStart, fEnd, f, nStart, nEnd, maxNumFaces = 0; 3213 PetscSection neighSec; 3214 PetscInt (*neighbors)[2]; 3215 PetscInt *counter; 3216 3217 PetscFunctionBegin; 3218 PetscCall(DMGetDimension(dm, &dim)); 3219 PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd)); 3220 PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL)); 3221 if (cEndInterior < 0) cEndInterior = cEnd; 3222 PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &neighSec)); 3223 PetscCall(PetscSectionSetChart(neighSec, cStart, cEndInterior)); 3224 PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 3225 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 3226 for (f = fStart; f < fEnd; f++) { 3227 const PetscInt *fcells; 3228 PetscBool boundary; 3229 PetscInt ghost = -1; 3230 PetscInt numChildren, numCells, c; 3231 3232 if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost)); 3233 PetscCall(DMIsBoundaryPoint(dm, f, &boundary)); 3234 PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL)); 3235 if ((ghost >= 0) || boundary || numChildren) continue; 3236 PetscCall(DMPlexGetSupportSize(dm, f, &numCells)); 3237 if (numCells == 2) { 3238 PetscCall(DMPlexGetSupport(dm, f, &fcells)); 3239 for (c = 0; c < 2; c++) { 3240 PetscInt cell = fcells[c]; 3241 3242 if (cell >= cStart && cell < cEndInterior) PetscCall(PetscSectionAddDof(neighSec, cell, 1)); 3243 } 3244 } 3245 } 3246 PetscCall(PetscSectionSetUp(neighSec)); 3247 PetscCall(PetscSectionGetMaxDof(neighSec, &maxNumFaces)); 3248 PetscCall(PetscFVLeastSquaresSetMaxFaces(fvm, maxNumFaces)); 3249 nStart = 0; 3250 PetscCall(PetscSectionGetStorageSize(neighSec, &nEnd)); 3251 PetscCall(PetscMalloc1(nEnd - nStart, &neighbors)); 3252 PetscCall(PetscCalloc1(cEndInterior - cStart, &counter)); 3253 for (f = fStart; f < fEnd; f++) { 3254 const PetscInt *fcells; 3255 PetscBool boundary; 3256 PetscInt ghost = -1; 3257 PetscInt numChildren, numCells, c; 3258 3259 if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, f, &ghost)); 3260 PetscCall(DMIsBoundaryPoint(dm, f, &boundary)); 3261 PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL)); 3262 if ((ghost >= 0) || boundary || numChildren) continue; 3263 PetscCall(DMPlexGetSupportSize(dm, f, &numCells)); 3264 if (numCells == 2) { 3265 PetscCall(DMPlexGetSupport(dm, f, &fcells)); 3266 for (c = 0; c < 2; c++) { 3267 PetscInt cell = fcells[c], off; 3268 3269 if (cell >= cStart && cell < cEndInterior) { 3270 PetscCall(PetscSectionGetOffset(neighSec, cell, &off)); 3271 off += counter[cell - cStart]++; 3272 neighbors[off][0] = f; 3273 neighbors[off][1] = fcells[1 - c]; 3274 } 3275 } 3276 } 3277 } 3278 PetscCall(PetscFree(counter)); 3279 PetscCall(PetscMalloc3(maxNumFaces * dim, &dx, maxNumFaces * dim, &grad, maxNumFaces, &gref)); 3280 for (c = cStart; c < cEndInterior; c++) { 3281 PetscInt numFaces, f, d, off, ghost = -1; 3282 PetscFVCellGeom *cg; 3283 3284 PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg)); 3285 PetscCall(PetscSectionGetDof(neighSec, c, &numFaces)); 3286 PetscCall(PetscSectionGetOffset(neighSec, c, &off)); 3287 3288 // do not attempt to compute a gradient reconstruction stencil in a ghost cell. It will never be used 3289 if (ghostLabel) PetscCall(DMLabelGetValue(ghostLabel, c, &ghost)); 3290 if (ghost >= 0) continue; 3291 3292 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); 3293 for (f = 0; f < numFaces; ++f) { 3294 PetscFVCellGeom *cg1; 3295 PetscFVFaceGeom *fg; 3296 const PetscInt *fcells; 3297 PetscInt ncell, side, nface; 3298 3299 nface = neighbors[off + f][0]; 3300 ncell = neighbors[off + f][1]; 3301 PetscCall(DMPlexGetSupport(dm, nface, &fcells)); 3302 side = (c != fcells[0]); 3303 PetscCall(DMPlexPointLocalRef(dmFace, nface, fgeom, &fg)); 3304 PetscCall(DMPlexPointLocalRead(dmCell, ncell, cgeom, &cg1)); 3305 for (d = 0; d < dim; ++d) dx[f * dim + d] = cg1->centroid[d] - cg->centroid[d]; 3306 gref[f] = fg->grad[side]; /* Gradient reconstruction term will go here */ 3307 } 3308 PetscCall(PetscFVComputeGradient(fvm, numFaces, dx, grad)); 3309 for (f = 0; f < numFaces; ++f) { 3310 for (d = 0; d < dim; ++d) gref[f][d] = grad[f * dim + d]; 3311 } 3312 } 3313 PetscCall(PetscFree3(dx, grad, gref)); 3314 PetscCall(PetscSectionDestroy(&neighSec)); 3315 PetscCall(PetscFree(neighbors)); 3316 PetscFunctionReturn(PETSC_SUCCESS); 3317 } 3318 3319 /*@ 3320 DMPlexComputeGradientFVM - Compute geometric factors for gradient reconstruction, which are stored in the geometry data, and compute layout for gradient data 3321 3322 Collective 3323 3324 Input Parameters: 3325 + dm - The `DMPLEX` 3326 . fvm - The `PetscFV` 3327 - cellGeometry - The face geometry from `DMPlexComputeCellGeometryFVM()` 3328 3329 Input/Output Parameter: 3330 . faceGeometry - The face geometry from `DMPlexComputeFaceGeometryFVM()`; on output 3331 the geometric factors for gradient calculation are inserted 3332 3333 Output Parameter: 3334 . dmGrad - The `DM` describing the layout of gradient data 3335 3336 Level: developer 3337 3338 .seealso: `DMPLEX`, `DMPlexGetFaceGeometryFVM()`, `DMPlexGetCellGeometryFVM()` 3339 @*/ 3340 PetscErrorCode DMPlexComputeGradientFVM(DM dm, PetscFV fvm, Vec faceGeometry, Vec cellGeometry, DM *dmGrad) 3341 { 3342 DM dmFace, dmCell; 3343 PetscScalar *fgeom, *cgeom; 3344 PetscSection sectionGrad, parentSection; 3345 PetscInt dim, pdim, cStart, cEnd, cEndInterior, c; 3346 3347 PetscFunctionBegin; 3348 PetscCall(DMGetDimension(dm, &dim)); 3349 PetscCall(PetscFVGetNumComponents(fvm, &pdim)); 3350 PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd)); 3351 PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL)); 3352 /* Construct the interpolant corresponding to each face from the least-square solution over the cell neighborhood */ 3353 PetscCall(VecGetDM(faceGeometry, &dmFace)); 3354 PetscCall(VecGetDM(cellGeometry, &dmCell)); 3355 PetscCall(VecGetArray(faceGeometry, &fgeom)); 3356 PetscCall(VecGetArray(cellGeometry, &cgeom)); 3357 PetscCall(DMPlexGetTree(dm, &parentSection, NULL, NULL, NULL, NULL)); 3358 if (!parentSection) { 3359 PetscCall(BuildGradientReconstruction_Internal(dm, fvm, dmFace, fgeom, dmCell, cgeom)); 3360 } else { 3361 PetscCall(BuildGradientReconstruction_Internal_Tree(dm, fvm, dmFace, fgeom, dmCell, cgeom)); 3362 } 3363 PetscCall(VecRestoreArray(faceGeometry, &fgeom)); 3364 PetscCall(VecRestoreArray(cellGeometry, &cgeom)); 3365 /* Create storage for gradients */ 3366 PetscCall(DMClone(dm, dmGrad)); 3367 PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), §ionGrad)); 3368 PetscCall(PetscSectionSetChart(sectionGrad, cStart, cEnd)); 3369 for (c = cStart; c < cEnd; ++c) PetscCall(PetscSectionSetDof(sectionGrad, c, pdim * dim)); 3370 PetscCall(PetscSectionSetUp(sectionGrad)); 3371 PetscCall(DMSetLocalSection(*dmGrad, sectionGrad)); 3372 PetscCall(PetscSectionDestroy(§ionGrad)); 3373 PetscFunctionReturn(PETSC_SUCCESS); 3374 } 3375 3376 /*@ 3377 DMPlexGetDataFVM - Retrieve precomputed cell geometry 3378 3379 Collective 3380 3381 Input Parameters: 3382 + dm - The `DM` 3383 - fv - The `PetscFV` 3384 3385 Output Parameters: 3386 + cellgeom - The cell geometry 3387 . facegeom - The face geometry 3388 - gradDM - The gradient matrices 3389 3390 Level: developer 3391 3392 .seealso: `DMPLEX`, `DMPlexComputeGeometryFVM()` 3393 @*/ 3394 PetscErrorCode DMPlexGetDataFVM(DM dm, PetscFV fv, Vec *cellgeom, Vec *facegeom, DM *gradDM) 3395 { 3396 PetscObject cellgeomobj, facegeomobj; 3397 3398 PetscFunctionBegin; 3399 PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_cellgeom_fvm", &cellgeomobj)); 3400 if (!cellgeomobj) { 3401 Vec cellgeomInt, facegeomInt; 3402 3403 PetscCall(DMPlexComputeGeometryFVM(dm, &cellgeomInt, &facegeomInt)); 3404 PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_cellgeom_fvm", (PetscObject)cellgeomInt)); 3405 PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_facegeom_fvm", (PetscObject)facegeomInt)); 3406 PetscCall(VecDestroy(&cellgeomInt)); 3407 PetscCall(VecDestroy(&facegeomInt)); 3408 PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_cellgeom_fvm", &cellgeomobj)); 3409 } 3410 PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_facegeom_fvm", &facegeomobj)); 3411 if (cellgeom) *cellgeom = (Vec)cellgeomobj; 3412 if (facegeom) *facegeom = (Vec)facegeomobj; 3413 if (gradDM) { 3414 PetscObject gradobj; 3415 PetscBool computeGradients; 3416 3417 PetscCall(PetscFVGetComputeGradients(fv, &computeGradients)); 3418 if (!computeGradients) { 3419 *gradDM = NULL; 3420 PetscFunctionReturn(PETSC_SUCCESS); 3421 } 3422 PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_dmgrad_fvm", &gradobj)); 3423 if (!gradobj) { 3424 DM dmGradInt; 3425 3426 PetscCall(DMPlexComputeGradientFVM(dm, fv, (Vec)facegeomobj, (Vec)cellgeomobj, &dmGradInt)); 3427 PetscCall(PetscObjectCompose((PetscObject)dm, "DMPlex_dmgrad_fvm", (PetscObject)dmGradInt)); 3428 PetscCall(DMDestroy(&dmGradInt)); 3429 PetscCall(PetscObjectQuery((PetscObject)dm, "DMPlex_dmgrad_fvm", &gradobj)); 3430 } 3431 *gradDM = (DM)gradobj; 3432 } 3433 PetscFunctionReturn(PETSC_SUCCESS); 3434 } 3435 3436 static PetscErrorCode DMPlexCoordinatesToReference_NewtonUpdate(PetscInt dimC, PetscInt dimR, PetscScalar *J, PetscScalar *invJ, PetscScalar *work, PetscReal *resNeg, PetscReal *guess) 3437 { 3438 PetscInt l, m; 3439 3440 PetscFunctionBeginHot; 3441 if (dimC == dimR && dimR <= 3) { 3442 /* invert Jacobian, multiply */ 3443 PetscScalar det, idet; 3444 3445 switch (dimR) { 3446 case 1: 3447 invJ[0] = 1. / J[0]; 3448 break; 3449 case 2: 3450 det = J[0] * J[3] - J[1] * J[2]; 3451 idet = 1. / det; 3452 invJ[0] = J[3] * idet; 3453 invJ[1] = -J[1] * idet; 3454 invJ[2] = -J[2] * idet; 3455 invJ[3] = J[0] * idet; 3456 break; 3457 case 3: { 3458 invJ[0] = J[4] * J[8] - J[5] * J[7]; 3459 invJ[1] = J[2] * J[7] - J[1] * J[8]; 3460 invJ[2] = J[1] * J[5] - J[2] * J[4]; 3461 det = invJ[0] * J[0] + invJ[1] * J[3] + invJ[2] * J[6]; 3462 idet = 1. / det; 3463 invJ[0] *= idet; 3464 invJ[1] *= idet; 3465 invJ[2] *= idet; 3466 invJ[3] = idet * (J[5] * J[6] - J[3] * J[8]); 3467 invJ[4] = idet * (J[0] * J[8] - J[2] * J[6]); 3468 invJ[5] = idet * (J[2] * J[3] - J[0] * J[5]); 3469 invJ[6] = idet * (J[3] * J[7] - J[4] * J[6]); 3470 invJ[7] = idet * (J[1] * J[6] - J[0] * J[7]); 3471 invJ[8] = idet * (J[0] * J[4] - J[1] * J[3]); 3472 } break; 3473 } 3474 for (l = 0; l < dimR; l++) { 3475 for (m = 0; m < dimC; m++) guess[l] += PetscRealPart(invJ[l * dimC + m]) * resNeg[m]; 3476 } 3477 } else { 3478 #if defined(PETSC_USE_COMPLEX) 3479 char transpose = 'C'; 3480 #else 3481 char transpose = 'T'; 3482 #endif 3483 PetscBLASInt m, n, one = 1, worksize, info; 3484 3485 PetscCall(PetscBLASIntCast(dimR, &m)); 3486 PetscCall(PetscBLASIntCast(dimC, &n)); 3487 PetscCall(PetscBLASIntCast(dimC * dimC, &worksize)); 3488 for (l = 0; l < dimC; l++) invJ[l] = resNeg[l]; 3489 3490 PetscCallBLAS("LAPACKgels", LAPACKgels_(&transpose, &m, &n, &one, J, &m, invJ, &n, work, &worksize, &info)); 3491 PetscCheck(info == 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "Bad argument to GELS %" PetscBLASInt_FMT, info); 3492 3493 for (l = 0; l < dimR; l++) guess[l] += PetscRealPart(invJ[l]); 3494 } 3495 PetscFunctionReturn(PETSC_SUCCESS); 3496 } 3497 3498 static PetscErrorCode DMPlexCoordinatesToReference_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[], Vec coords, PetscInt dimC, PetscInt dimR) 3499 { 3500 PetscInt coordSize, i, j, k, l, m, maxIts = 7, numV = (1 << dimR); 3501 PetscScalar *coordsScalar = NULL; 3502 PetscReal *cellData, *cellCoords, *cellCoeffs, *extJ, *resNeg; 3503 PetscScalar *J, *invJ, *work; 3504 3505 PetscFunctionBegin; 3506 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 3507 PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar)); 3508 PetscCheck(coordSize >= dimC * numV, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expecting at least %" PetscInt_FMT " coordinates, got %" PetscInt_FMT, dimC * (1 << dimR), coordSize); 3509 PetscCall(DMGetWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData)); 3510 PetscCall(DMGetWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J)); 3511 cellCoords = &cellData[0]; 3512 cellCoeffs = &cellData[coordSize]; 3513 extJ = &cellData[2 * coordSize]; 3514 resNeg = &cellData[2 * coordSize + dimR]; 3515 invJ = &J[dimR * dimC]; 3516 work = &J[2 * dimR * dimC]; 3517 if (dimR == 2) { 3518 const PetscInt zToPlex[4] = {0, 1, 3, 2}; 3519 3520 for (i = 0; i < 4; i++) { 3521 PetscInt plexI = zToPlex[i]; 3522 3523 for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]); 3524 } 3525 } else if (dimR == 3) { 3526 const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6}; 3527 3528 for (i = 0; i < 8; i++) { 3529 PetscInt plexI = zToPlex[i]; 3530 3531 for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]); 3532 } 3533 } else { 3534 for (i = 0; i < coordSize; i++) cellCoords[i] = PetscRealPart(coordsScalar[i]); 3535 } 3536 /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */ 3537 for (i = 0; i < dimR; i++) { 3538 PetscReal *swap; 3539 3540 for (j = 0; j < (numV / 2); j++) { 3541 for (k = 0; k < dimC; k++) { 3542 cellCoeffs[dimC * j + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]); 3543 cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]); 3544 } 3545 } 3546 3547 if (i < dimR - 1) { 3548 swap = cellCoeffs; 3549 cellCoeffs = cellCoords; 3550 cellCoords = swap; 3551 } 3552 } 3553 PetscCall(PetscArrayzero(refCoords, numPoints * dimR)); 3554 for (j = 0; j < numPoints; j++) { 3555 for (i = 0; i < maxIts; i++) { 3556 PetscReal *guess = &refCoords[dimR * j]; 3557 3558 /* compute -residual and Jacobian */ 3559 for (k = 0; k < dimC; k++) resNeg[k] = realCoords[dimC * j + k]; 3560 for (k = 0; k < dimC * dimR; k++) J[k] = 0.; 3561 for (k = 0; k < numV; k++) { 3562 PetscReal extCoord = 1.; 3563 for (l = 0; l < dimR; l++) { 3564 PetscReal coord = guess[l]; 3565 PetscInt dep = (k & (1 << l)) >> l; 3566 3567 extCoord *= dep * coord + !dep; 3568 extJ[l] = dep; 3569 3570 for (m = 0; m < dimR; m++) { 3571 PetscReal coord = guess[m]; 3572 PetscInt dep = ((k & (1 << m)) >> m) && (m != l); 3573 PetscReal mult = dep * coord + !dep; 3574 3575 extJ[l] *= mult; 3576 } 3577 } 3578 for (l = 0; l < dimC; l++) { 3579 PetscReal coeff = cellCoeffs[dimC * k + l]; 3580 3581 resNeg[l] -= coeff * extCoord; 3582 for (m = 0; m < dimR; m++) J[dimR * l + m] += coeff * extJ[m]; 3583 } 3584 } 3585 if (0 && PetscDefined(USE_DEBUG)) { 3586 PetscReal maxAbs = 0.; 3587 3588 for (l = 0; l < dimC; l++) maxAbs = PetscMax(maxAbs, PetscAbsReal(resNeg[l])); 3589 PetscCall(PetscInfo(dm, "cell %" PetscInt_FMT ", point %" PetscInt_FMT ", iter %" PetscInt_FMT ": res %g\n", cell, j, i, (double)maxAbs)); 3590 } 3591 3592 PetscCall(DMPlexCoordinatesToReference_NewtonUpdate(dimC, dimR, J, invJ, work, resNeg, guess)); 3593 } 3594 } 3595 PetscCall(DMRestoreWorkArray(dm, 3 * dimR * dimC, MPIU_SCALAR, &J)); 3596 PetscCall(DMRestoreWorkArray(dm, 2 * coordSize + dimR + dimC, MPIU_REAL, &cellData)); 3597 PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar)); 3598 PetscFunctionReturn(PETSC_SUCCESS); 3599 } 3600 3601 static PetscErrorCode DMPlexReferenceToCoordinates_Tensor(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt dimC, PetscInt dimR) 3602 { 3603 PetscInt coordSize, i, j, k, l, numV = (1 << dimR); 3604 PetscScalar *coordsScalar = NULL; 3605 PetscReal *cellData, *cellCoords, *cellCoeffs; 3606 3607 PetscFunctionBegin; 3608 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 3609 PetscCall(DMPlexVecGetClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar)); 3610 PetscCheck(coordSize >= dimC * numV, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expecting at least %" PetscInt_FMT " coordinates, got %" PetscInt_FMT, dimC * (1 << dimR), coordSize); 3611 PetscCall(DMGetWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData)); 3612 cellCoords = &cellData[0]; 3613 cellCoeffs = &cellData[coordSize]; 3614 if (dimR == 2) { 3615 const PetscInt zToPlex[4] = {0, 1, 3, 2}; 3616 3617 for (i = 0; i < 4; i++) { 3618 PetscInt plexI = zToPlex[i]; 3619 3620 for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]); 3621 } 3622 } else if (dimR == 3) { 3623 const PetscInt zToPlex[8] = {0, 3, 1, 2, 4, 5, 7, 6}; 3624 3625 for (i = 0; i < 8; i++) { 3626 PetscInt plexI = zToPlex[i]; 3627 3628 for (j = 0; j < dimC; j++) cellCoords[dimC * i + j] = PetscRealPart(coordsScalar[dimC * plexI + j]); 3629 } 3630 } else { 3631 for (i = 0; i < coordSize; i++) cellCoords[i] = PetscRealPart(coordsScalar[i]); 3632 } 3633 /* Perform the shuffling transform that converts values at the corners of [-1,1]^d to coefficients */ 3634 for (i = 0; i < dimR; i++) { 3635 PetscReal *swap; 3636 3637 for (j = 0; j < (numV / 2); j++) { 3638 for (k = 0; k < dimC; k++) { 3639 cellCoeffs[dimC * j + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] + cellCoords[dimC * 2 * j + k]); 3640 cellCoeffs[dimC * (j + (numV / 2)) + k] = 0.5 * (cellCoords[dimC * (2 * j + 1) + k] - cellCoords[dimC * 2 * j + k]); 3641 } 3642 } 3643 3644 if (i < dimR - 1) { 3645 swap = cellCoeffs; 3646 cellCoeffs = cellCoords; 3647 cellCoords = swap; 3648 } 3649 } 3650 PetscCall(PetscArrayzero(realCoords, numPoints * dimC)); 3651 for (j = 0; j < numPoints; j++) { 3652 const PetscReal *guess = &refCoords[dimR * j]; 3653 PetscReal *mapped = &realCoords[dimC * j]; 3654 3655 for (k = 0; k < numV; k++) { 3656 PetscReal extCoord = 1.; 3657 for (l = 0; l < dimR; l++) { 3658 PetscReal coord = guess[l]; 3659 PetscInt dep = (k & (1 << l)) >> l; 3660 3661 extCoord *= dep * coord + !dep; 3662 } 3663 for (l = 0; l < dimC; l++) { 3664 PetscReal coeff = cellCoeffs[dimC * k + l]; 3665 3666 mapped[l] += coeff * extCoord; 3667 } 3668 } 3669 } 3670 PetscCall(DMRestoreWorkArray(dm, 2 * coordSize, MPIU_REAL, &cellData)); 3671 PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &coordsScalar)); 3672 PetscFunctionReturn(PETSC_SUCCESS); 3673 } 3674 3675 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) 3676 { 3677 PetscInt numComp, pdim, i, j, k, l, m, coordSize; 3678 PetscScalar *nodes = NULL; 3679 PetscReal *invV, *modes; 3680 PetscReal *B, *D, *resNeg; 3681 PetscScalar *J, *invJ, *work; 3682 PetscReal tolerance = tol == NULL ? 0.0 : *tol; 3683 3684 PetscFunctionBegin; 3685 PetscCall(PetscFEGetDimension(fe, &pdim)); 3686 PetscCall(PetscFEGetNumComponents(fe, &numComp)); 3687 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); 3688 /* we shouldn't apply inverse closure permutation, if one exists */ 3689 PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, coords, cell, 0, &coordSize, &nodes)); 3690 /* convert nodes to values in the stable evaluation basis */ 3691 PetscCall(DMGetWorkArray(dm, pdim, MPIU_REAL, &modes)); 3692 invV = fe->invV; 3693 for (i = 0; i < pdim; ++i) { 3694 modes[i] = 0.; 3695 for (j = 0; j < pdim; ++j) modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]); 3696 } 3697 PetscCall(DMGetWorkArray(dm, pdim * Nc + pdim * Nc * dimR + Nc, MPIU_REAL, &B)); 3698 D = &B[pdim * Nc]; 3699 resNeg = &D[pdim * Nc * dimR]; 3700 PetscCall(DMGetWorkArray(dm, 3 * Nc * dimR, MPIU_SCALAR, &J)); 3701 invJ = &J[Nc * dimR]; 3702 work = &invJ[Nc * dimR]; 3703 for (i = 0; i < numPoints * dimR; i++) refCoords[i] = 0.; 3704 for (j = 0; j < numPoints; j++) { 3705 PetscReal normPoint = DMPlex_NormD_Internal(Nc, &realCoords[j * Nc]); 3706 normPoint = normPoint > PETSC_SMALL ? normPoint : 1.0; 3707 for (i = 0; i < maxIter; i++) { /* we could batch this so that we're not making big B and D arrays all the time */ 3708 PetscReal *guess = &refCoords[j * dimR], error = 0; 3709 PetscCall(PetscSpaceEvaluate(fe->basisSpace, 1, guess, B, D, NULL)); 3710 for (k = 0; k < Nc; k++) resNeg[k] = realCoords[j * Nc + k]; 3711 for (k = 0; k < Nc * dimR; k++) J[k] = 0.; 3712 for (k = 0; k < pdim; k++) { 3713 for (l = 0; l < Nc; l++) { 3714 resNeg[l] -= modes[k] * B[k * Nc + l]; 3715 for (m = 0; m < dimR; m++) J[l * dimR + m] += modes[k] * D[(k * Nc + l) * dimR + m]; 3716 } 3717 } 3718 if (0 && PetscDefined(USE_DEBUG)) { 3719 PetscReal maxAbs = 0.; 3720 3721 for (l = 0; l < Nc; l++) maxAbs = PetscMax(maxAbs, PetscAbsReal(resNeg[l])); 3722 PetscCall(PetscInfo(dm, "cell %" PetscInt_FMT ", point %" PetscInt_FMT ", iter %" PetscInt_FMT ": res %g\n", cell, j, i, (double)maxAbs)); 3723 } 3724 error = DMPlex_NormD_Internal(Nc, resNeg); 3725 if (error < tolerance * normPoint) { 3726 if (tol) *tol = error / normPoint; 3727 break; 3728 } 3729 PetscCall(DMPlexCoordinatesToReference_NewtonUpdate(Nc, dimR, J, invJ, work, resNeg, guess)); 3730 } 3731 } 3732 PetscCall(DMRestoreWorkArray(dm, 3 * Nc * dimR, MPIU_SCALAR, &J)); 3733 PetscCall(DMRestoreWorkArray(dm, pdim * Nc + pdim * Nc * dimR + Nc, MPIU_REAL, &B)); 3734 PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_REAL, &modes)); 3735 PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes)); 3736 PetscFunctionReturn(PETSC_SUCCESS); 3737 } 3738 3739 /* TODO: TOBY please fix this for Nc > 1 */ 3740 PetscErrorCode DMPlexReferenceToCoordinates_FE(DM dm, PetscFE fe, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[], Vec coords, PetscInt Nc, PetscInt dimR) 3741 { 3742 PetscInt numComp, pdim, i, j, k, l, coordSize; 3743 PetscScalar *nodes = NULL; 3744 PetscReal *invV, *modes; 3745 PetscReal *B; 3746 3747 PetscFunctionBegin; 3748 PetscCall(PetscFEGetDimension(fe, &pdim)); 3749 PetscCall(PetscFEGetNumComponents(fe, &numComp)); 3750 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); 3751 /* we shouldn't apply inverse closure permutation, if one exists */ 3752 PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, coords, cell, 0, &coordSize, &nodes)); 3753 /* convert nodes to values in the stable evaluation basis */ 3754 PetscCall(DMGetWorkArray(dm, pdim, MPIU_REAL, &modes)); 3755 invV = fe->invV; 3756 for (i = 0; i < pdim; ++i) { 3757 modes[i] = 0.; 3758 for (j = 0; j < pdim; ++j) modes[i] += invV[i * pdim + j] * PetscRealPart(nodes[j]); 3759 } 3760 PetscCall(DMGetWorkArray(dm, numPoints * pdim * Nc, MPIU_REAL, &B)); 3761 PetscCall(PetscSpaceEvaluate(fe->basisSpace, numPoints, refCoords, B, NULL, NULL)); 3762 for (i = 0; i < numPoints * Nc; i++) realCoords[i] = 0.; 3763 for (j = 0; j < numPoints; j++) { 3764 PetscReal *mapped = &realCoords[j * Nc]; 3765 3766 for (k = 0; k < pdim; k++) { 3767 for (l = 0; l < Nc; l++) mapped[l] += modes[k] * B[(j * pdim + k) * Nc + l]; 3768 } 3769 } 3770 PetscCall(DMRestoreWorkArray(dm, numPoints * pdim * Nc, MPIU_REAL, &B)); 3771 PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_REAL, &modes)); 3772 PetscCall(DMPlexVecRestoreClosure(dm, NULL, coords, cell, &coordSize, &nodes)); 3773 PetscFunctionReturn(PETSC_SUCCESS); 3774 } 3775 3776 /*@ 3777 DMPlexCoordinatesToReference - Pull coordinates back from the mesh to the reference element 3778 using a single element map. 3779 3780 Not Collective 3781 3782 Input Parameters: 3783 + dm - The mesh, with coordinate maps defined either by a `PetscDS` for the coordinate `DM` (see `DMGetCoordinateDM()`) or 3784 implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or 3785 as a multilinear map for tensor-product elements 3786 . cell - the cell whose map is used. 3787 . numPoints - the number of points to locate 3788 - realCoords - (numPoints x coordinate dimension) array of coordinates (see `DMGetCoordinateDim()`) 3789 3790 Output Parameter: 3791 . refCoords - (`numPoints` x `dimension`) array of reference coordinates (see `DMGetDimension()`) 3792 3793 Level: intermediate 3794 3795 Notes: 3796 This inversion will be accurate inside the reference element, but may be inaccurate for 3797 mappings that do not extend uniquely outside the reference cell (e.g, most non-affine maps) 3798 3799 .seealso: `DMPLEX`, `DMPlexReferenceToCoordinates()` 3800 @*/ 3801 PetscErrorCode DMPlexCoordinatesToReference(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal realCoords[], PetscReal refCoords[]) 3802 { 3803 PetscInt dimC, dimR, depth, cStart, cEnd, i; 3804 DM coordDM = NULL; 3805 Vec coords; 3806 PetscFE fe = NULL; 3807 3808 PetscFunctionBegin; 3809 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 3810 PetscCall(DMGetDimension(dm, &dimR)); 3811 PetscCall(DMGetCoordinateDim(dm, &dimC)); 3812 if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(PETSC_SUCCESS); 3813 PetscCall(DMPlexGetDepth(dm, &depth)); 3814 PetscCall(DMGetCoordinatesLocal(dm, &coords)); 3815 PetscCall(DMGetCoordinateDM(dm, &coordDM)); 3816 if (coordDM) { 3817 PetscInt coordFields; 3818 3819 PetscCall(DMGetNumFields(coordDM, &coordFields)); 3820 if (coordFields) { 3821 PetscClassId id; 3822 PetscObject disc; 3823 3824 PetscCall(DMGetField(coordDM, 0, NULL, &disc)); 3825 PetscCall(PetscObjectGetClassId(disc, &id)); 3826 if (id == PETSCFE_CLASSID) fe = (PetscFE)disc; 3827 } 3828 } 3829 PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd)); 3830 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); 3831 if (!fe) { /* implicit discretization: affine or multilinear */ 3832 PetscInt coneSize; 3833 PetscBool isSimplex, isTensor; 3834 3835 PetscCall(DMPlexGetConeSize(dm, cell, &coneSize)); 3836 isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE; 3837 isTensor = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE; 3838 if (isSimplex) { 3839 PetscReal detJ, *v0, *J, *invJ; 3840 3841 PetscCall(DMGetWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0)); 3842 J = &v0[dimC]; 3843 invJ = &J[dimC * dimC]; 3844 PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, invJ, &detJ)); 3845 for (i = 0; i < numPoints; i++) { /* Apply the inverse affine transformation for each point */ 3846 const PetscReal x0[3] = {-1., -1., -1.}; 3847 3848 CoordinatesRealToRef(dimC, dimR, x0, v0, invJ, &realCoords[dimC * i], &refCoords[dimR * i]); 3849 } 3850 PetscCall(DMRestoreWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0)); 3851 } else if (isTensor) { 3852 PetscCall(DMPlexCoordinatesToReference_Tensor(coordDM, cell, numPoints, realCoords, refCoords, coords, dimC, dimR)); 3853 } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unrecognized cone size %" PetscInt_FMT, coneSize); 3854 } else { 3855 PetscCall(DMPlexCoordinatesToReference_FE(coordDM, fe, cell, numPoints, realCoords, refCoords, coords, dimC, dimR, 7, NULL)); 3856 } 3857 PetscFunctionReturn(PETSC_SUCCESS); 3858 } 3859 3860 /*@ 3861 DMPlexReferenceToCoordinates - Map references coordinates to coordinates in the mesh for a single element map. 3862 3863 Not Collective 3864 3865 Input Parameters: 3866 + dm - The mesh, with coordinate maps defined either by a PetscDS for the coordinate `DM` (see `DMGetCoordinateDM()`) or 3867 implicitly by the coordinates of the corner vertices of the cell: as an affine map for simplicial elements, or 3868 as a multilinear map for tensor-product elements 3869 . cell - the cell whose map is used. 3870 . numPoints - the number of points to locate 3871 - refCoords - (numPoints x dimension) array of reference coordinates (see `DMGetDimension()`) 3872 3873 Output Parameter: 3874 . realCoords - (numPoints x coordinate dimension) array of coordinates (see `DMGetCoordinateDim()`) 3875 3876 Level: intermediate 3877 3878 .seealso: `DMPLEX`, `DMPlexCoordinatesToReference()` 3879 @*/ 3880 PetscErrorCode DMPlexReferenceToCoordinates(DM dm, PetscInt cell, PetscInt numPoints, const PetscReal refCoords[], PetscReal realCoords[]) 3881 { 3882 PetscInt dimC, dimR, depth, cStart, cEnd, i; 3883 DM coordDM = NULL; 3884 Vec coords; 3885 PetscFE fe = NULL; 3886 3887 PetscFunctionBegin; 3888 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 3889 PetscCall(DMGetDimension(dm, &dimR)); 3890 PetscCall(DMGetCoordinateDim(dm, &dimC)); 3891 if (dimR <= 0 || dimC <= 0 || numPoints <= 0) PetscFunctionReturn(PETSC_SUCCESS); 3892 PetscCall(DMPlexGetDepth(dm, &depth)); 3893 PetscCall(DMGetCoordinatesLocal(dm, &coords)); 3894 PetscCall(DMGetCoordinateDM(dm, &coordDM)); 3895 if (coordDM) { 3896 PetscInt coordFields; 3897 3898 PetscCall(DMGetNumFields(coordDM, &coordFields)); 3899 if (coordFields) { 3900 PetscClassId id; 3901 PetscObject disc; 3902 3903 PetscCall(DMGetField(coordDM, 0, NULL, &disc)); 3904 PetscCall(PetscObjectGetClassId(disc, &id)); 3905 if (id == PETSCFE_CLASSID) fe = (PetscFE)disc; 3906 } 3907 } 3908 PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd)); 3909 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); 3910 if (!fe) { /* implicit discretization: affine or multilinear */ 3911 PetscInt coneSize; 3912 PetscBool isSimplex, isTensor; 3913 3914 PetscCall(DMPlexGetConeSize(dm, cell, &coneSize)); 3915 isSimplex = (coneSize == (dimR + 1)) ? PETSC_TRUE : PETSC_FALSE; 3916 isTensor = (coneSize == ((depth == 1) ? (1 << dimR) : (2 * dimR))) ? PETSC_TRUE : PETSC_FALSE; 3917 if (isSimplex) { 3918 PetscReal detJ, *v0, *J; 3919 3920 PetscCall(DMGetWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0)); 3921 J = &v0[dimC]; 3922 PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, cell, v0, J, NULL, &detJ)); 3923 for (i = 0; i < numPoints; i++) { /* Apply the affine transformation for each point */ 3924 const PetscReal xi0[3] = {-1., -1., -1.}; 3925 3926 CoordinatesRefToReal(dimC, dimR, xi0, v0, J, &refCoords[dimR * i], &realCoords[dimC * i]); 3927 } 3928 PetscCall(DMRestoreWorkArray(dm, dimC + 2 * dimC * dimC, MPIU_REAL, &v0)); 3929 } else if (isTensor) { 3930 PetscCall(DMPlexReferenceToCoordinates_Tensor(coordDM, cell, numPoints, refCoords, realCoords, coords, dimC, dimR)); 3931 } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unrecognized cone size %" PetscInt_FMT, coneSize); 3932 } else { 3933 PetscCall(DMPlexReferenceToCoordinates_FE(coordDM, fe, cell, numPoints, refCoords, realCoords, coords, dimC, dimR)); 3934 } 3935 PetscFunctionReturn(PETSC_SUCCESS); 3936 } 3937 3938 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[]) 3939 { 3940 const PetscInt Nc = uOff[1] - uOff[0]; 3941 PetscInt c; 3942 3943 for (c = 0; c < Nc; ++c) f0[c] = u[c]; 3944 } 3945 3946 /* Shear applies the transformation, assuming we fix z, 3947 / 1 0 m_0 \ 3948 | 0 1 m_1 | 3949 \ 0 0 1 / 3950 */ 3951 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[]) 3952 { 3953 const PetscInt Nc = uOff[1] - uOff[0]; 3954 const PetscInt ax = (PetscInt)PetscRealPart(constants[0]); 3955 PetscInt c; 3956 3957 for (c = 0; c < Nc; ++c) coords[c] = u[c] + constants[c + 1] * u[ax]; 3958 } 3959 3960 /* Flare applies the transformation, assuming we fix x_f, 3961 3962 x_i = x_i * alpha_i x_f 3963 */ 3964 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[]) 3965 { 3966 const PetscInt Nc = uOff[1] - uOff[0]; 3967 const PetscInt cf = (PetscInt)PetscRealPart(constants[0]); 3968 PetscInt c; 3969 3970 for (c = 0; c < Nc; ++c) coords[c] = u[c] * (c == cf ? 1.0 : constants[c + 1] * u[cf]); 3971 } 3972 3973 /* 3974 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 3975 will correspond to the top and bottom of our square. So 3976 3977 (0,0)--(1,0) ==> (1,0)--(2,0) Just a shift of (1,0) 3978 (0,1)--(1,1) ==> (0,1)--(0,2) Switch x and y 3979 3980 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: 3981 3982 (x, y) ==> (x+1, \pi/2 y) in (r', \theta') space 3983 ==> ((x+1) cos(\pi/2 y), (x+1) sin(\pi/2 y)) in (x', y') space 3984 */ 3985 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[]) 3986 { 3987 const PetscReal ri = PetscRealPart(constants[0]); 3988 const PetscReal ro = PetscRealPart(constants[1]); 3989 3990 xp[0] = (x[0] * (ro - ri) + ri) * PetscCosReal(0.5 * PETSC_PI * x[1]); 3991 xp[1] = (x[0] * (ro - ri) + ri) * PetscSinReal(0.5 * PETSC_PI * x[1]); 3992 } 3993 3994 /* 3995 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 3996 lower hemisphere and the upper surface onto the top, letting z be the radius. 3997 3998 (x, y) ==> ((z+3)/2, \pi/2 (|x| or |y|), arctan y/x) in (r', \theta', \phi') space 3999 ==> ((z+3)/2 \cos(\theta') cos(\phi'), (z+3)/2 \cos(\theta') sin(\phi'), (z+3)/2 sin(\theta')) in (x', y', z') space 4000 */ 4001 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[]) 4002 { 4003 const PetscReal pi4 = PETSC_PI / 4.0; 4004 const PetscReal ri = PetscRealPart(constants[0]); 4005 const PetscReal ro = PetscRealPart(constants[1]); 4006 const PetscReal rp = (x[2] + 1) * 0.5 * (ro - ri) + ri; 4007 const PetscReal phip = PetscAtan2Real(x[1], x[0]); 4008 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]))); 4009 4010 xp[0] = rp * PetscCosReal(thetap) * PetscCosReal(phip); 4011 xp[1] = rp * PetscCosReal(thetap) * PetscSinReal(phip); 4012 xp[2] = rp * PetscSinReal(thetap); 4013 } 4014 4015 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[]) 4016 { 4017 const PetscReal c = PetscRealPart(constants[0]); 4018 const PetscReal m = PetscRealPart(constants[1]); 4019 const PetscReal n = PetscRealPart(constants[2]); 4020 4021 xp[0] = x[0]; 4022 xp[1] = x[1]; 4023 if (dim > 2) xp[2] = c * PetscCosReal(2. * m * PETSC_PI * x[0]) * PetscCosReal(2. * n * PETSC_PI * x[1]); 4024 } 4025 4026 /*@C 4027 DMPlexRemapGeometry - This function maps the original `DM` coordinates to new coordinates. 4028 4029 Not Collective 4030 4031 Input Parameters: 4032 + dm - The `DM` 4033 . time - The time 4034 - func - The function transforming current coordinates to new coordinates 4035 4036 Calling sequence of `func`: 4037 + dim - The spatial dimension 4038 . Nf - The number of input fields (here 1) 4039 . NfAux - The number of input auxiliary fields 4040 . uOff - The offset of the coordinates in u[] (here 0) 4041 . uOff_x - The offset of the coordinates in u_x[] (here 0) 4042 . u - The coordinate values at this point in space 4043 . u_t - The coordinate time derivative at this point in space (here `NULL`) 4044 . u_x - The coordinate derivatives at this point in space 4045 . aOff - The offset of each auxiliary field in u[] 4046 . aOff_x - The offset of each auxiliary field in u_x[] 4047 . a - The auxiliary field values at this point in space 4048 . a_t - The auxiliary field time derivative at this point in space (or `NULL`) 4049 . a_x - The auxiliary field derivatives at this point in space 4050 . t - The current time 4051 . x - The coordinates of this point (here not used) 4052 . numConstants - The number of constants 4053 . constants - The value of each constant 4054 - f - The new coordinates at this point in space 4055 4056 Level: intermediate 4057 4058 .seealso: `DMPLEX`, `DMGetCoordinates()`, `DMGetCoordinatesLocal()`, `DMGetCoordinateDM()`, `DMProjectFieldLocal()`, `DMProjectFieldLabelLocal()` 4059 @*/ 4060 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[])) 4061 { 4062 DM cdm; 4063 PetscDS cds; 4064 DMField cf; 4065 PetscObject obj; 4066 PetscClassId id; 4067 Vec lCoords, tmpCoords; 4068 4069 PetscFunctionBegin; 4070 PetscCall(DMGetCoordinateDM(dm, &cdm)); 4071 PetscCall(DMGetCoordinatesLocal(dm, &lCoords)); 4072 PetscCall(DMGetDS(cdm, &cds)); 4073 PetscCall(PetscDSGetDiscretization(cds, 0, &obj)); 4074 PetscCall(PetscObjectGetClassId(obj, &id)); 4075 if (id != PETSCFE_CLASSID) { 4076 PetscSection cSection; 4077 const PetscScalar *constants; 4078 PetscScalar *coords, f[16]; 4079 PetscInt dim, cdim, Nc, vStart, vEnd; 4080 4081 PetscCall(DMGetDimension(dm, &dim)); 4082 PetscCall(DMGetCoordinateDim(dm, &cdim)); 4083 PetscCheck(cdim <= 16, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Affine version of DMPlexRemapGeometry is currently limited to dimensions <= 16, not %" PetscInt_FMT, cdim); 4084 PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd)); 4085 PetscCall(DMGetCoordinateSection(dm, &cSection)); 4086 PetscCall(PetscDSGetConstants(cds, &Nc, &constants)); 4087 PetscCall(VecGetArrayWrite(lCoords, &coords)); 4088 for (PetscInt v = vStart; v < vEnd; ++v) { 4089 PetscInt uOff[2] = {0, cdim}; 4090 PetscInt off, c; 4091 4092 PetscCall(PetscSectionGetOffset(cSection, v, &off)); 4093 (*func)(dim, 1, 0, uOff, NULL, &coords[off], NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0.0, NULL, Nc, constants, f); 4094 for (c = 0; c < cdim; ++c) coords[off + c] = f[c]; 4095 } 4096 PetscCall(VecRestoreArrayWrite(lCoords, &coords)); 4097 } else { 4098 PetscCall(DMGetLocalVector(cdm, &tmpCoords)); 4099 PetscCall(VecCopy(lCoords, tmpCoords)); 4100 /* We have to do the coordinate field manually right now since the coordinate DM will not have its own */ 4101 PetscCall(DMGetCoordinateField(dm, &cf)); 4102 cdm->coordinates[0].field = cf; 4103 PetscCall(DMProjectFieldLocal(cdm, time, tmpCoords, &func, INSERT_VALUES, lCoords)); 4104 cdm->coordinates[0].field = NULL; 4105 PetscCall(DMRestoreLocalVector(cdm, &tmpCoords)); 4106 PetscCall(DMSetCoordinatesLocal(dm, lCoords)); 4107 } 4108 PetscFunctionReturn(PETSC_SUCCESS); 4109 } 4110 4111 /*@ 4112 DMPlexShearGeometry - This shears the domain, meaning adds a multiple of the shear coordinate to all other coordinates. 4113 4114 Not Collective 4115 4116 Input Parameters: 4117 + dm - The `DMPLEX` 4118 . direction - The shear coordinate direction, e.g. `DM_X` is the x-axis 4119 - multipliers - The multiplier m for each direction which is not the shear direction 4120 4121 Level: intermediate 4122 4123 .seealso: `DMPLEX`, `DMPlexRemapGeometry()`, `DMDirection`, `DM_X`, `DM_Y`, `DM_Z` 4124 @*/ 4125 PetscErrorCode DMPlexShearGeometry(DM dm, DMDirection direction, PetscReal multipliers[]) 4126 { 4127 DM cdm; 4128 PetscDS cds; 4129 PetscScalar *moduli; 4130 const PetscInt dir = (PetscInt)direction; 4131 PetscInt dE, d, e; 4132 4133 PetscFunctionBegin; 4134 PetscCall(DMGetCoordinateDM(dm, &cdm)); 4135 PetscCall(DMGetCoordinateDim(dm, &dE)); 4136 PetscCall(PetscMalloc1(dE + 1, &moduli)); 4137 moduli[0] = dir; 4138 for (d = 0, e = 0; d < dE; ++d) moduli[d + 1] = d == dir ? 0.0 : (multipliers ? multipliers[e++] : 1.0); 4139 PetscCall(DMGetDS(cdm, &cds)); 4140 PetscCall(PetscDSSetConstants(cds, dE + 1, moduli)); 4141 PetscCall(DMPlexRemapGeometry(dm, 0.0, coordMap_shear)); 4142 PetscCall(PetscFree(moduli)); 4143 PetscFunctionReturn(PETSC_SUCCESS); 4144 } 4145