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