Initial commit
This commit is contained in:
@@ -0,0 +1,340 @@
|
||||
/**
|
||||
* CUDA kernels for Poisson equation solving
|
||||
*
|
||||
* Implements iterative solvers for the Poisson equation:
|
||||
* ∇²φ = f
|
||||
*
|
||||
* Includes Jacobi, Gauss-Seidel, and SOR methods for 2D and 3D domains.
|
||||
* These are essential for pressure correction in CFD solvers.
|
||||
*/
|
||||
|
||||
extern "C" __global__ void poisson_jacobi_2d(
|
||||
float* __restrict__ phi_new,
|
||||
const float* __restrict__ phi,
|
||||
const float* __restrict__ source,
|
||||
float factor, // 1 / (2 * (1/dx² + 1/dy²))
|
||||
float dx2_inv, // 1/dx²
|
||||
float dy2_inv, // 1/dy²
|
||||
int nx,
|
||||
int ny
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int j = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
|
||||
if (i >= nx || j >= ny) return;
|
||||
|
||||
int idx = j * nx + i;
|
||||
|
||||
// Boundary conditions (Dirichlet - zero on boundaries for now)
|
||||
if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) {
|
||||
phi_new[idx] = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
int idx_ip1 = j * nx + (i + 1);
|
||||
int idx_im1 = j * nx + (i - 1);
|
||||
int idx_jp1 = (j + 1) * nx + i;
|
||||
int idx_jm1 = (j - 1) * nx + i;
|
||||
|
||||
// Jacobi iteration: φ^{k+1}_ij = factor * (source_ij + (1/dx²)(φ_{i+1,j} + φ_{i-1,j}) + (1/dy²)(φ_{i,j+1} + φ_{i,j-1}))
|
||||
phi_new[idx] = factor * (
|
||||
source[idx] +
|
||||
dx2_inv * (phi[idx_ip1] + phi[idx_im1]) +
|
||||
dy2_inv * (phi[idx_jp1] + phi[idx_jm1])
|
||||
);
|
||||
}
|
||||
|
||||
extern "C" __global__ void poisson_gauss_seidel_2d(
|
||||
float* __restrict__ phi,
|
||||
const float* __restrict__ source,
|
||||
float factor,
|
||||
float dx2_inv,
|
||||
float dy2_inv,
|
||||
int nx,
|
||||
int ny,
|
||||
int red_black_flag // 0 for red, 1 for black
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int j = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
|
||||
if (i >= nx || j >= ny) return;
|
||||
|
||||
// Red-black ordering for parallelization
|
||||
if (((i + j) % 2) != red_black_flag) return;
|
||||
|
||||
int idx = j * nx + i;
|
||||
|
||||
// Boundary conditions
|
||||
if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) {
|
||||
phi[idx] = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
int idx_ip1 = j * nx + (i + 1);
|
||||
int idx_im1 = j * nx + (i - 1);
|
||||
int idx_jp1 = (j + 1) * nx + i;
|
||||
int idx_jm1 = (j - 1) * nx + i;
|
||||
|
||||
// Gauss-Seidel update (in-place)
|
||||
phi[idx] = factor * (
|
||||
source[idx] +
|
||||
dx2_inv * (phi[idx_ip1] + phi[idx_im1]) +
|
||||
dy2_inv * (phi[idx_jp1] + phi[idx_jm1])
|
||||
);
|
||||
}
|
||||
|
||||
extern "C" __global__ void poisson_sor_2d(
|
||||
float* __restrict__ phi,
|
||||
const float* __restrict__ source,
|
||||
float factor,
|
||||
float dx2_inv,
|
||||
float dy2_inv,
|
||||
float omega, // SOR relaxation parameter (1 < ω < 2)
|
||||
int nx,
|
||||
int ny,
|
||||
int red_black_flag
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int j = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
|
||||
if (i >= nx || j >= ny) return;
|
||||
|
||||
if (((i + j) % 2) != red_black_flag) return;
|
||||
|
||||
int idx = j * nx + i;
|
||||
|
||||
// Boundary conditions
|
||||
if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) {
|
||||
phi[idx] = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
int idx_ip1 = j * nx + (i + 1);
|
||||
int idx_im1 = j * nx + (i - 1);
|
||||
int idx_jp1 = (j + 1) * nx + i;
|
||||
int idx_jm1 = (j - 1) * nx + i;
|
||||
|
||||
float phi_old = phi[idx];
|
||||
|
||||
// Compute Gauss-Seidel update
|
||||
float phi_gs = factor * (
|
||||
source[idx] +
|
||||
dx2_inv * (phi[idx_ip1] + phi[idx_im1]) +
|
||||
dy2_inv * (phi[idx_jp1] + phi[idx_jm1])
|
||||
);
|
||||
|
||||
// SOR update: φ^{new} = (1-ω)φ^{old} + ω*φ^{GS}
|
||||
phi[idx] = (1.0f - omega) * phi_old + omega * phi_gs;
|
||||
}
|
||||
|
||||
extern "C" __global__ void poisson_residual_2d(
|
||||
float* __restrict__ residual,
|
||||
const float* __restrict__ phi,
|
||||
const float* __restrict__ source,
|
||||
float dx2_inv,
|
||||
float dy2_inv,
|
||||
int nx,
|
||||
int ny
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int j = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
|
||||
if (i >= nx || j >= ny) return;
|
||||
|
||||
int idx = j * nx + i;
|
||||
|
||||
// Boundary points have zero residual
|
||||
if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) {
|
||||
residual[idx] = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
int idx_ip1 = j * nx + (i + 1);
|
||||
int idx_im1 = j * nx + (i - 1);
|
||||
int idx_jp1 = (j + 1) * nx + i;
|
||||
int idx_jm1 = (j - 1) * nx + i;
|
||||
|
||||
// Compute residual: r = f - L*φ where L is the Laplacian operator
|
||||
float laplacian = dx2_inv * (phi[idx_ip1] - 2.0f * phi[idx] + phi[idx_im1]) +
|
||||
dy2_inv * (phi[idx_jp1] - 2.0f * phi[idx] + phi[idx_jm1]);
|
||||
|
||||
residual[idx] = source[idx] - laplacian;
|
||||
}
|
||||
|
||||
// 3D Poisson solver kernels
|
||||
extern "C" __global__ void poisson_jacobi_3d(
|
||||
float* __restrict__ phi_new,
|
||||
const float* __restrict__ phi,
|
||||
const float* __restrict__ source,
|
||||
float factor, // 1 / (2 * (1/dx² + 1/dy² + 1/dz²))
|
||||
float dx2_inv,
|
||||
float dy2_inv,
|
||||
float dz2_inv,
|
||||
int nx,
|
||||
int ny,
|
||||
int nz
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int j = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
int k = blockIdx.z * blockDim.z + threadIdx.z;
|
||||
|
||||
if (i >= nx || j >= ny || k >= nz) return;
|
||||
|
||||
int idx = k * nx * ny + j * nx + i;
|
||||
|
||||
// Boundary conditions
|
||||
if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1 || k == 0 || k == nz - 1) {
|
||||
phi_new[idx] = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
int idx_ip1 = k * nx * ny + j * nx + (i + 1);
|
||||
int idx_im1 = k * nx * ny + j * nx + (i - 1);
|
||||
int idx_jp1 = k * nx * ny + (j + 1) * nx + i;
|
||||
int idx_jm1 = k * nx * ny + (j - 1) * nx + i;
|
||||
int idx_kp1 = (k + 1) * nx * ny + j * nx + i;
|
||||
int idx_km1 = (k - 1) * nx * ny + j * nx + i;
|
||||
|
||||
// 3D Jacobi iteration
|
||||
phi_new[idx] = factor * (
|
||||
source[idx] +
|
||||
dx2_inv * (phi[idx_ip1] + phi[idx_im1]) +
|
||||
dy2_inv * (phi[idx_jp1] + phi[idx_jm1]) +
|
||||
dz2_inv * (phi[idx_kp1] + phi[idx_km1])
|
||||
);
|
||||
}
|
||||
|
||||
// Multigrid helper kernels
|
||||
extern "C" __global__ void restrict_2d(
|
||||
float* __restrict__ coarse,
|
||||
const float* __restrict__ fine,
|
||||
int nx_fine,
|
||||
int ny_fine,
|
||||
int nx_coarse,
|
||||
int ny_coarse
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int j = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
|
||||
if (i >= nx_coarse || j >= ny_coarse) return;
|
||||
|
||||
int idx_coarse = j * nx_coarse + i;
|
||||
|
||||
// Full weighting restriction (9-point stencil in 2D)
|
||||
int i_fine = 2 * i;
|
||||
int j_fine = 2 * j;
|
||||
|
||||
if (i_fine >= nx_fine - 1 || j_fine >= ny_fine - 1) {
|
||||
coarse[idx_coarse] = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
float sum = 0.0f;
|
||||
|
||||
// Center point (weight 4)
|
||||
sum += 4.0f * fine[j_fine * nx_fine + i_fine];
|
||||
|
||||
// Edge neighbors (weight 2 each)
|
||||
if (i_fine > 0) sum += 2.0f * fine[j_fine * nx_fine + (i_fine - 1)];
|
||||
if (i_fine < nx_fine - 1) sum += 2.0f * fine[j_fine * nx_fine + (i_fine + 1)];
|
||||
if (j_fine > 0) sum += 2.0f * fine[(j_fine - 1) * nx_fine + i_fine];
|
||||
if (j_fine < ny_fine - 1) sum += 2.0f * fine[(j_fine + 1) * nx_fine + i_fine];
|
||||
|
||||
// Corner neighbors (weight 1 each)
|
||||
if (i_fine > 0 && j_fine > 0)
|
||||
sum += fine[(j_fine - 1) * nx_fine + (i_fine - 1)];
|
||||
if (i_fine < nx_fine - 1 && j_fine > 0)
|
||||
sum += fine[(j_fine - 1) * nx_fine + (i_fine + 1)];
|
||||
if (i_fine > 0 && j_fine < ny_fine - 1)
|
||||
sum += fine[(j_fine + 1) * nx_fine + (i_fine - 1)];
|
||||
if (i_fine < nx_fine - 1 && j_fine < ny_fine - 1)
|
||||
sum += fine[(j_fine + 1) * nx_fine + (i_fine + 1)];
|
||||
|
||||
coarse[idx_coarse] = sum / 16.0f; // Normalize by total weight
|
||||
}
|
||||
|
||||
extern "C" __global__ void prolongate_2d(
|
||||
float* __restrict__ fine,
|
||||
const float* __restrict__ coarse,
|
||||
int nx_fine,
|
||||
int ny_fine,
|
||||
int nx_coarse,
|
||||
int ny_coarse
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int j = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
|
||||
if (i >= nx_fine || j >= ny_fine) return;
|
||||
|
||||
int idx_fine = j * nx_fine + i;
|
||||
|
||||
// Bilinear interpolation from coarse grid
|
||||
float i_coarse = i * 0.5f;
|
||||
float j_coarse = j * 0.5f;
|
||||
|
||||
int i0 = (int)i_coarse;
|
||||
int j0 = (int)j_coarse;
|
||||
int i1 = min(i0 + 1, nx_coarse - 1);
|
||||
int j1 = min(j0 + 1, ny_coarse - 1);
|
||||
|
||||
float alpha = i_coarse - i0;
|
||||
float beta = j_coarse - j0;
|
||||
|
||||
float val00 = coarse[j0 * nx_coarse + i0];
|
||||
float val10 = coarse[j0 * nx_coarse + i1];
|
||||
float val01 = coarse[j1 * nx_coarse + i0];
|
||||
float val11 = coarse[j1 * nx_coarse + i1];
|
||||
|
||||
float val0 = (1.0f - alpha) * val00 + alpha * val10;
|
||||
float val1 = (1.0f - alpha) * val01 + alpha * val11;
|
||||
|
||||
fine[idx_fine] += (1.0f - beta) * val0 + beta * val1;
|
||||
}
|
||||
|
||||
// Specialized kernel for pressure Poisson equation with Neumann boundaries
|
||||
extern "C" __global__ void pressure_poisson_2d(
|
||||
float* __restrict__ pressure_new,
|
||||
const float* __restrict__ pressure,
|
||||
const float* __restrict__ divergence,
|
||||
float dx2_inv,
|
||||
float dy2_inv,
|
||||
float dt,
|
||||
float rho,
|
||||
int nx,
|
||||
int ny
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int j = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
|
||||
if (i >= nx || j >= ny) return;
|
||||
|
||||
int idx = j * nx + i;
|
||||
float factor = 1.0f / (2.0f * (dx2_inv + dy2_inv));
|
||||
|
||||
// Neumann boundary conditions (∂p/∂n = 0)
|
||||
if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) {
|
||||
// For Neumann BCs, use one-sided differences
|
||||
int idx_neighbor;
|
||||
if (i == 0) idx_neighbor = j * nx + 1;
|
||||
else if (i == nx - 1) idx_neighbor = j * nx + (nx - 2);
|
||||
else if (j == 0) idx_neighbor = 1 * nx + i;
|
||||
else idx_neighbor = (ny - 2) * nx + i;
|
||||
|
||||
pressure_new[idx] = pressure[idx_neighbor];
|
||||
return;
|
||||
}
|
||||
|
||||
int idx_ip1 = j * nx + (i + 1);
|
||||
int idx_im1 = j * nx + (i - 1);
|
||||
int idx_jp1 = (j + 1) * nx + i;
|
||||
int idx_jm1 = (j - 1) * nx + i;
|
||||
|
||||
// Source term from velocity divergence (for pressure correction)
|
||||
float source = -rho * divergence[idx] / dt;
|
||||
|
||||
pressure_new[idx] = factor * (
|
||||
source +
|
||||
dx2_inv * (pressure[idx_ip1] + pressure[idx_im1]) +
|
||||
dy2_inv * (pressure[idx_jp1] + pressure[idx_jm1])
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user