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