Initial commit
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* CUDA kernel for central differencing advection scheme
|
||||
*
|
||||
* Implements second-order central differencing for the advection equation:
|
||||
* ∂φ/∂t + u·∇φ = 0
|
||||
*
|
||||
* The central scheme is more accurate than upwind but can be unstable
|
||||
* for high CFL numbers and may produce oscillations.
|
||||
*/
|
||||
|
||||
extern "C" __global__ void advection_1d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
float velocity,
|
||||
float dt,
|
||||
float dx,
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
float cfl = velocity * dt / dx;
|
||||
|
||||
// Boundary conditions (periodic for now)
|
||||
int im1 = (i == 0) ? n - 1 : i - 1;
|
||||
int ip1 = (i == n - 1) ? 0 : i + 1;
|
||||
|
||||
// Central differencing scheme
|
||||
phi_new[i] = phi[i] - 0.5f * cfl * (phi[ip1] - phi[im1]);
|
||||
}
|
||||
|
||||
extern "C" __global__ void advection_2d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
const float* __restrict__ u,
|
||||
const float* __restrict__ v,
|
||||
float dt,
|
||||
float dx,
|
||||
float 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;
|
||||
|
||||
// Get velocities at this point
|
||||
float u_val = u[idx];
|
||||
float v_val = v[idx];
|
||||
|
||||
float cfl_x = u_val * dt / dx;
|
||||
float cfl_y = v_val * dt / dy;
|
||||
|
||||
// Neighbor indices with periodic boundary conditions
|
||||
int im1 = (i == 0) ? nx - 1 : i - 1;
|
||||
int ip1 = (i == nx - 1) ? 0 : i + 1;
|
||||
int jm1 = (j == 0) ? ny - 1 : j - 1;
|
||||
int jp1 = (j == ny - 1) ? 0 : j + 1;
|
||||
|
||||
int idx_im1 = j * nx + im1;
|
||||
int idx_ip1 = j * nx + ip1;
|
||||
int idx_jm1 = jm1 * nx + i;
|
||||
int idx_jp1 = jp1 * nx + i;
|
||||
|
||||
float phi_val = phi[idx];
|
||||
|
||||
// Central differencing in both directions
|
||||
float advection_x = 0.5f * cfl_x * (phi[idx_ip1] - phi[idx_im1]);
|
||||
float advection_y = 0.5f * cfl_y * (phi[idx_jp1] - phi[idx_jm1]);
|
||||
|
||||
phi_new[idx] = phi_val - advection_x - advection_y;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* CUDA kernel for QUICK (Quadratic Upstream Interpolation for Convective Kinematics) scheme
|
||||
*
|
||||
* Implements third-order QUICK scheme for the advection equation:
|
||||
* ∂φ/∂t + u·∇φ = 0
|
||||
*
|
||||
* The QUICK scheme uses a three-point upstream-weighted quadratic interpolation
|
||||
* to achieve third-order accuracy while maintaining stability.
|
||||
*/
|
||||
|
||||
extern "C" __global__ void advection_1d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
float velocity,
|
||||
float dt,
|
||||
float dx,
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
float cfl = velocity * dt / dx;
|
||||
|
||||
// Need extended stencil for QUICK scheme
|
||||
int im2, im1, ip1, ip2;
|
||||
|
||||
// Periodic boundary conditions
|
||||
im2 = (i <= 1) ? n + i - 2 : i - 2;
|
||||
im1 = (i == 0) ? n - 1 : i - 1;
|
||||
ip1 = (i == n - 1) ? 0 : i + 1;
|
||||
ip2 = (i >= n - 2) ? i + 2 - n : i + 2;
|
||||
|
||||
float phi_val = phi[i];
|
||||
|
||||
if (velocity > 0.0f) {
|
||||
// QUICK scheme for positive velocity (upwind biased)
|
||||
// φ_face = (3φ_i + 6φ_{i-1} - φ_{i-2}) / 8
|
||||
float phi_face = (3.0f * phi_val + 6.0f * phi[im1] - phi[im2]) / 8.0f;
|
||||
float phi_face_upstream = (3.0f * phi[im1] + 6.0f * phi[im2] - phi[(im2 == 0) ? n-1 : im2-1]) / 8.0f;
|
||||
phi_new[i] = phi_val - cfl * (phi_face - phi_face_upstream);
|
||||
} else {
|
||||
// QUICK scheme for negative velocity (downwind biased)
|
||||
float phi_face = (3.0f * phi_val + 6.0f * phi[ip1] - phi[ip2]) / 8.0f;
|
||||
float phi_face_downstream = (3.0f * phi[ip1] + 6.0f * phi[ip2] - phi[(ip2 == n-1) ? 0 : ip2+1]) / 8.0f;
|
||||
phi_new[i] = phi_val - cfl * (phi_face_downstream - phi_face);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void advection_2d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
const float* __restrict__ u,
|
||||
const float* __restrict__ v,
|
||||
float dt,
|
||||
float dx,
|
||||
float 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;
|
||||
|
||||
// Get velocities at this point
|
||||
float u_val = u[idx];
|
||||
float v_val = v[idx];
|
||||
|
||||
float cfl_x = u_val * dt / dx;
|
||||
float cfl_y = v_val * dt / dy;
|
||||
|
||||
float phi_val = phi[idx];
|
||||
|
||||
// X-direction QUICK scheme
|
||||
float advection_x = 0.0f;
|
||||
if (u_val > 0.0f && i >= 2) {
|
||||
int im2 = (i <= 1) ? nx + i - 2 : i - 2;
|
||||
int im1 = (i == 0) ? nx - 1 : i - 1;
|
||||
int idx_im2 = j * nx + im2;
|
||||
int idx_im1 = j * nx + im1;
|
||||
|
||||
float phi_face = (3.0f * phi_val + 6.0f * phi[idx_im1] - phi[idx_im2]) / 8.0f;
|
||||
int im3 = (im2 == 0) ? nx - 1 : im2 - 1;
|
||||
int idx_im3 = j * nx + im3;
|
||||
float phi_face_upstream = (3.0f * phi[idx_im1] + 6.0f * phi[idx_im2] - phi[idx_im3]) / 8.0f;
|
||||
advection_x = cfl_x * (phi_face - phi_face_upstream);
|
||||
} else if (u_val < 0.0f && i <= nx - 3) {
|
||||
int ip1 = (i == nx - 1) ? 0 : i + 1;
|
||||
int ip2 = (i >= nx - 2) ? i + 2 - nx : i + 2;
|
||||
int idx_ip1 = j * nx + ip1;
|
||||
int idx_ip2 = j * nx + ip2;
|
||||
|
||||
float phi_face = (3.0f * phi_val + 6.0f * phi[idx_ip1] - phi[idx_ip2]) / 8.0f;
|
||||
int ip3 = (ip2 == nx - 1) ? 0 : ip2 + 1;
|
||||
int idx_ip3 = j * nx + ip3;
|
||||
float phi_face_downstream = (3.0f * phi[idx_ip1] + 6.0f * phi[idx_ip2] - phi[idx_ip3]) / 8.0f;
|
||||
advection_x = cfl_x * (phi_face_downstream - phi_face);
|
||||
} else {
|
||||
// Fall back to upwind for boundary regions
|
||||
int im1 = (i == 0) ? nx - 1 : i - 1;
|
||||
int ip1 = (i == nx - 1) ? 0 : i + 1;
|
||||
int idx_im1 = j * nx + im1;
|
||||
int idx_ip1 = j * nx + ip1;
|
||||
|
||||
if (u_val > 0.0f) {
|
||||
advection_x = cfl_x * (phi_val - phi[idx_im1]);
|
||||
} else {
|
||||
advection_x = cfl_x * (phi[idx_ip1] - phi_val);
|
||||
}
|
||||
}
|
||||
|
||||
// Y-direction QUICK scheme (similar logic)
|
||||
float advection_y = 0.0f;
|
||||
if (v_val > 0.0f && j >= 2) {
|
||||
int jm2 = (j <= 1) ? ny + j - 2 : j - 2;
|
||||
int jm1 = (j == 0) ? ny - 1 : j - 1;
|
||||
int idx_jm2 = jm2 * nx + i;
|
||||
int idx_jm1 = jm1 * nx + i;
|
||||
|
||||
float phi_face = (3.0f * phi_val + 6.0f * phi[idx_jm1] - phi[idx_jm2]) / 8.0f;
|
||||
int jm3 = (jm2 == 0) ? ny - 1 : jm2 - 1;
|
||||
int idx_jm3 = jm3 * nx + i;
|
||||
float phi_face_upstream = (3.0f * phi[idx_jm1] + 6.0f * phi[idx_jm2] - phi[idx_jm3]) / 8.0f;
|
||||
advection_y = cfl_y * (phi_face - phi_face_upstream);
|
||||
} else if (v_val < 0.0f && j <= ny - 3) {
|
||||
int jp1 = (j == ny - 1) ? 0 : j + 1;
|
||||
int jp2 = (j >= ny - 2) ? j + 2 - ny : j + 2;
|
||||
int idx_jp1 = jp1 * nx + i;
|
||||
int idx_jp2 = jp2 * nx + i;
|
||||
|
||||
float phi_face = (3.0f * phi_val + 6.0f * phi[idx_jp1] - phi[idx_jp2]) / 8.0f;
|
||||
int jp3 = (jp2 == ny - 1) ? 0 : jp2 + 1;
|
||||
int idx_jp3 = jp3 * nx + i;
|
||||
float phi_face_downstream = (3.0f * phi[idx_jp1] + 6.0f * phi[idx_jp2] - phi[idx_jp3]) / 8.0f;
|
||||
advection_y = cfl_y * (phi_face_downstream - phi_face);
|
||||
} else {
|
||||
// Fall back to upwind for boundary regions
|
||||
int jm1 = (j == 0) ? ny - 1 : j - 1;
|
||||
int jp1 = (j == ny - 1) ? 0 : j + 1;
|
||||
int idx_jm1 = jm1 * nx + i;
|
||||
int idx_jp1 = jp1 * nx + i;
|
||||
|
||||
if (v_val > 0.0f) {
|
||||
advection_y = cfl_y * (phi_val - phi[idx_jm1]);
|
||||
} else {
|
||||
advection_y = cfl_y * (phi[idx_jp1] - phi_val);
|
||||
}
|
||||
}
|
||||
|
||||
phi_new[idx] = phi_val - advection_x - advection_y;
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
/**
|
||||
* CUDA kernel for upwind advection scheme
|
||||
*
|
||||
* Implements first-order upwind scheme for the advection equation:
|
||||
* ∂φ/∂t + u·∇φ = 0
|
||||
*
|
||||
* The upwind scheme is stable but introduces numerical diffusion.
|
||||
*/
|
||||
|
||||
extern "C" __global__ void advection_1d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
float velocity,
|
||||
float dt,
|
||||
float dx,
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
float cfl = velocity * dt / dx;
|
||||
|
||||
// Boundary conditions (periodic for now)
|
||||
int im1 = (i == 0) ? n - 1 : i - 1;
|
||||
int ip1 = (i == n - 1) ? 0 : i + 1;
|
||||
|
||||
if (velocity > 0.0f) {
|
||||
// Upwind scheme for positive velocity
|
||||
phi_new[i] = phi[i] - cfl * (phi[i] - phi[im1]);
|
||||
} else {
|
||||
// Upwind scheme for negative velocity
|
||||
phi_new[i] = phi[i] - cfl * (phi[ip1] - phi[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Central difference scheme for 1D advection
|
||||
*/
|
||||
extern "C" __global__ void advection_central_1d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
float velocity,
|
||||
float dt,
|
||||
float dx,
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (i >= n) return;
|
||||
|
||||
float cfl = velocity * dt / dx;
|
||||
int im1 = (i == 0) ? n - 1 : i - 1;
|
||||
int ip1 = (i == n - 1) ? 0 : i + 1;
|
||||
|
||||
// Central difference scheme (second-order accurate)
|
||||
phi_new[i] = phi[i] - 0.5f * cfl * (phi[ip1] - phi[im1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* QUICK scheme for 1D advection (Quadratic Upstream Interpolation)
|
||||
*/
|
||||
extern "C" __global__ void advection_quick_1d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
float velocity,
|
||||
float dt,
|
||||
float dx,
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (i >= n) return;
|
||||
|
||||
float cfl = velocity * dt / dx;
|
||||
|
||||
// Get neighboring indices with periodic boundaries
|
||||
int im2 = (i <= 1) ? n + i - 2 : i - 2;
|
||||
int im1 = (i == 0) ? n - 1 : i - 1;
|
||||
int ip1 = (i == n - 1) ? 0 : i + 1;
|
||||
int ip2 = (i >= n - 2) ? i + 2 - n : i + 2;
|
||||
|
||||
if (velocity > 0.0f) {
|
||||
// QUICK scheme for positive velocity
|
||||
float phi_face = (3.0f * phi[i] + 6.0f * phi[im1] - phi[im2]) / 8.0f;
|
||||
phi_new[i] = phi[i] - cfl * (phi_face - phi[im1]);
|
||||
} else {
|
||||
// QUICK scheme for negative velocity
|
||||
float phi_face = (3.0f * phi[i] + 6.0f * phi[ip1] - phi[ip2]) / 8.0f;
|
||||
phi_new[i] = phi[i] - cfl * (phi[ip1] - phi_face);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WENO5 scheme for 1D advection (5th-order Weighted Essentially Non-Oscillatory)
|
||||
*/
|
||||
extern "C" __global__ void advection_weno_1d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
float velocity,
|
||||
float dt,
|
||||
float dx,
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (i >= n) return;
|
||||
|
||||
float cfl = velocity * dt / dx;
|
||||
const float eps = 1e-6f;
|
||||
|
||||
// Get 5-point stencil with periodic boundaries
|
||||
int im2 = (i <= 1) ? n + i - 2 : i - 2;
|
||||
int im1 = (i == 0) ? n - 1 : i - 1;
|
||||
int ip1 = (i == n - 1) ? 0 : i + 1;
|
||||
int ip2 = (i >= n - 2) ? i + 2 - n : i + 2;
|
||||
int ip3 = (i >= n - 3) ? i + 3 - n : i + 3;
|
||||
|
||||
float phi_face;
|
||||
if (velocity > 0.0f) {
|
||||
// WENO5 reconstruction for positive velocity
|
||||
float v1 = (2.0f * phi[im2] - 7.0f * phi[im1] + 11.0f * phi[i]) / 6.0f;
|
||||
float v2 = (-phi[im1] + 5.0f * phi[i] + 2.0f * phi[ip1]) / 6.0f;
|
||||
float v3 = (2.0f * phi[i] + 5.0f * phi[ip1] - phi[ip2]) / 6.0f;
|
||||
|
||||
// Smoothness indicators
|
||||
float s1 = 13.0f/12.0f * powf(phi[im2] - 2.0f*phi[im1] + phi[i], 2.0f) +
|
||||
0.25f * powf(phi[im2] - 4.0f*phi[im1] + 3.0f*phi[i], 2.0f);
|
||||
float s2 = 13.0f/12.0f * powf(phi[im1] - 2.0f*phi[i] + phi[ip1], 2.0f) +
|
||||
0.25f * powf(phi[im1] - phi[ip1], 2.0f);
|
||||
float s3 = 13.0f/12.0f * powf(phi[i] - 2.0f*phi[ip1] + phi[ip2], 2.0f) +
|
||||
0.25f * powf(3.0f*phi[i] - 4.0f*phi[ip1] + phi[ip2], 2.0f);
|
||||
|
||||
// Weights
|
||||
float a1 = 0.1f / powf(eps + s1, 2.0f);
|
||||
float a2 = 0.6f / powf(eps + s2, 2.0f);
|
||||
float a3 = 0.3f / powf(eps + s3, 2.0f);
|
||||
float sum = a1 + a2 + a3;
|
||||
|
||||
phi_face = (a1 * v1 + a2 * v2 + a3 * v3) / sum;
|
||||
phi_new[i] = phi[i] - cfl * (phi_face - phi[im1]);
|
||||
} else {
|
||||
// WENO5 for negative velocity (mirror stencil)
|
||||
float v1 = (11.0f * phi[i] - 7.0f * phi[ip1] + 2.0f * phi[ip2]) / 6.0f;
|
||||
float v2 = (2.0f * phi[im1] + 5.0f * phi[i] - phi[ip1]) / 6.0f;
|
||||
float v3 = (-phi[im2] + 5.0f * phi[im1] + 2.0f * phi[i]) / 6.0f;
|
||||
|
||||
float s1 = 13.0f/12.0f * powf(phi[i] - 2.0f*phi[ip1] + phi[ip2], 2.0f) +
|
||||
0.25f * powf(3.0f*phi[i] - 4.0f*phi[ip1] + phi[ip2], 2.0f);
|
||||
float s2 = 13.0f/12.0f * powf(phi[im1] - 2.0f*phi[i] + phi[ip1], 2.0f) +
|
||||
0.25f * powf(phi[im1] - phi[ip1], 2.0f);
|
||||
float s3 = 13.0f/12.0f * powf(phi[im2] - 2.0f*phi[im1] + phi[i], 2.0f) +
|
||||
0.25f * powf(phi[im2] - 4.0f*phi[im1] + 3.0f*phi[i], 2.0f);
|
||||
|
||||
float a1 = 0.1f / powf(eps + s1, 2.0f);
|
||||
float a2 = 0.6f / powf(eps + s2, 2.0f);
|
||||
float a3 = 0.3f / powf(eps + s3, 2.0f);
|
||||
float sum = a1 + a2 + a3;
|
||||
|
||||
phi_face = (a1 * v1 + a2 * v2 + a3 * v3) / sum;
|
||||
phi_new[i] = phi[i] - cfl * (phi[ip1] - phi_face);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void advection_2d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
const float* __restrict__ u,
|
||||
const float* __restrict__ v,
|
||||
float dt,
|
||||
float dx,
|
||||
float 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;
|
||||
|
||||
// Get velocities at this point
|
||||
float u_val = u[idx];
|
||||
float v_val = v[idx];
|
||||
|
||||
float cfl_x = u_val * dt / dx;
|
||||
float cfl_y = v_val * dt / dy;
|
||||
|
||||
// Neighbor indices with periodic boundary conditions
|
||||
int im1 = (i == 0) ? nx - 1 : i - 1;
|
||||
int ip1 = (i == nx - 1) ? 0 : i + 1;
|
||||
int jm1 = (j == 0) ? ny - 1 : j - 1;
|
||||
int jp1 = (j == ny - 1) ? 0 : j + 1;
|
||||
|
||||
int idx_im1 = j * nx + im1;
|
||||
int idx_ip1 = j * nx + ip1;
|
||||
int idx_jm1 = jm1 * nx + i;
|
||||
int idx_jp1 = jp1 * nx + i;
|
||||
|
||||
float phi_val = phi[idx];
|
||||
float advection_x, advection_y;
|
||||
|
||||
// X-direction advection
|
||||
if (u_val > 0.0f) {
|
||||
advection_x = cfl_x * (phi_val - phi[idx_im1]);
|
||||
} else {
|
||||
advection_x = cfl_x * (phi[idx_ip1] - phi_val);
|
||||
}
|
||||
|
||||
// Y-direction advection
|
||||
if (v_val > 0.0f) {
|
||||
advection_y = cfl_y * (phi_val - phi[idx_jm1]);
|
||||
} else {
|
||||
advection_y = cfl_y * (phi[idx_jp1] - phi_val);
|
||||
}
|
||||
|
||||
phi_new[idx] = phi_val - advection_x - advection_y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Central difference scheme for 2D advection
|
||||
*/
|
||||
extern "C" __global__ void advection_central_2d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
const float* __restrict__ u,
|
||||
const float* __restrict__ v,
|
||||
float dt,
|
||||
float dx,
|
||||
float 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;
|
||||
float u_val = u[idx];
|
||||
float v_val = v[idx];
|
||||
|
||||
// Neighbor indices
|
||||
int im1 = (i == 0) ? nx - 1 : i - 1;
|
||||
int ip1 = (i == nx - 1) ? 0 : i + 1;
|
||||
int jm1 = (j == 0) ? ny - 1 : j - 1;
|
||||
int jp1 = (j == ny - 1) ? 0 : j + 1;
|
||||
|
||||
int idx_im1 = j * nx + im1;
|
||||
int idx_ip1 = j * nx + ip1;
|
||||
int idx_jm1 = jm1 * nx + i;
|
||||
int idx_jp1 = jp1 * nx + i;
|
||||
|
||||
// Central difference in both directions
|
||||
float advection_x = u_val * dt / (2.0f * dx) * (phi[idx_ip1] - phi[idx_im1]);
|
||||
float advection_y = v_val * dt / (2.0f * dy) * (phi[idx_jp1] - phi[idx_jm1]);
|
||||
|
||||
phi_new[idx] = phi[idx] - advection_x - advection_y;
|
||||
}
|
||||
|
||||
/**
|
||||
* QUICK scheme for 2D advection
|
||||
*/
|
||||
extern "C" __global__ void advection_quick_2d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
const float* __restrict__ u,
|
||||
const float* __restrict__ v,
|
||||
float dt,
|
||||
float dx,
|
||||
float 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;
|
||||
float u_val = u[idx];
|
||||
float v_val = v[idx];
|
||||
|
||||
// Extended stencil for QUICK
|
||||
int im2 = (i <= 1) ? nx + i - 2 : i - 2;
|
||||
int im1 = (i == 0) ? nx - 1 : i - 1;
|
||||
int ip1 = (i == nx - 1) ? 0 : i + 1;
|
||||
int ip2 = (i >= nx - 2) ? i + 2 - nx : i + 2;
|
||||
|
||||
int jm2 = (j <= 1) ? ny + j - 2 : j - 2;
|
||||
int jm1 = (j == 0) ? ny - 1 : j - 1;
|
||||
int jp1 = (j == ny - 1) ? 0 : j + 1;
|
||||
int jp2 = (j >= ny - 2) ? j + 2 - ny : j + 2;
|
||||
|
||||
float advection_x = 0.0f, advection_y = 0.0f;
|
||||
|
||||
// X-direction QUICK
|
||||
if (u_val > 0.0f) {
|
||||
float phi_face = (3.0f * phi[idx] + 6.0f * phi[j*nx + im1] - phi[j*nx + im2]) / 8.0f;
|
||||
advection_x = u_val * dt / dx * (phi_face - phi[j*nx + im1]);
|
||||
} else {
|
||||
float phi_face = (3.0f * phi[idx] + 6.0f * phi[j*nx + ip1] - phi[j*nx + ip2]) / 8.0f;
|
||||
advection_x = u_val * dt / dx * (phi[j*nx + ip1] - phi_face);
|
||||
}
|
||||
|
||||
// Y-direction QUICK
|
||||
if (v_val > 0.0f) {
|
||||
float phi_face = (3.0f * phi[idx] + 6.0f * phi[jm1*nx + i] - phi[jm2*nx + i]) / 8.0f;
|
||||
advection_y = v_val * dt / dy * (phi_face - phi[jm1*nx + i]);
|
||||
} else {
|
||||
float phi_face = (3.0f * phi[idx] + 6.0f * phi[jp1*nx + i] - phi[jp2*nx + i]) / 8.0f;
|
||||
advection_y = v_val * dt / dy * (phi[jp1*nx + i] - phi_face);
|
||||
}
|
||||
|
||||
phi_new[idx] = phi[idx] - advection_x - advection_y;
|
||||
}
|
||||
|
||||
/**
|
||||
* WENO5 scheme for 2D advection
|
||||
*/
|
||||
extern "C" __global__ void advection_weno_2d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
const float* __restrict__ u,
|
||||
const float* __restrict__ v,
|
||||
float dt,
|
||||
float dx,
|
||||
float 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;
|
||||
float u_val = u[idx];
|
||||
float v_val = v[idx];
|
||||
const float eps = 1e-6f;
|
||||
|
||||
// Extended stencil for WENO5
|
||||
int im3 = (i <= 2) ? nx + i - 3 : i - 3;
|
||||
int im2 = (i <= 1) ? nx + i - 2 : i - 2;
|
||||
int im1 = (i == 0) ? nx - 1 : i - 1;
|
||||
int ip1 = (i == nx - 1) ? 0 : i + 1;
|
||||
int ip2 = (i >= nx - 2) ? i + 2 - nx : i + 2;
|
||||
int ip3 = (i >= nx - 3) ? i + 3 - nx : i + 3;
|
||||
|
||||
// X-direction WENO5 (simplified for brevity - full implementation would be similar to 1D)
|
||||
float advection_x = 0.0f;
|
||||
if (fabsf(u_val) > eps) {
|
||||
// Use simplified upwind for demonstration
|
||||
if (u_val > 0.0f) {
|
||||
advection_x = u_val * dt / dx * (phi[idx] - phi[j*nx + im1]);
|
||||
} else {
|
||||
advection_x = u_val * dt / dx * (phi[j*nx + ip1] - phi[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
// Y-direction WENO5 (simplified)
|
||||
float advection_y = 0.0f;
|
||||
if (fabsf(v_val) > eps) {
|
||||
int jm1 = (j == 0) ? ny - 1 : j - 1;
|
||||
int jp1 = (j == ny - 1) ? 0 : j + 1;
|
||||
|
||||
if (v_val > 0.0f) {
|
||||
advection_y = v_val * dt / dy * (phi[idx] - phi[jm1*nx + i]);
|
||||
} else {
|
||||
advection_y = v_val * dt / dy * (phi[jp1*nx + i] - phi[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
phi_new[idx] = phi[idx] - advection_x - advection_y;
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* CUDA kernel for WENO5 (Weighted Essentially Non-Oscillatory) scheme
|
||||
*
|
||||
* Implements fifth-order WENO scheme for the advection equation:
|
||||
* ∂φ/∂t + u·∇φ = 0
|
||||
*
|
||||
* The WENO5 scheme provides high-order accuracy and excellent shock-capturing
|
||||
* capability by adaptively choosing weights based on solution smoothness.
|
||||
*/
|
||||
|
||||
__device__ inline float weno5_reconstruction(
|
||||
float phi_m2, float phi_m1, float phi_0, float phi_p1, float phi_p2
|
||||
) {
|
||||
const float eps = 1e-6f; // Small parameter to avoid division by zero
|
||||
|
||||
// Compute the three candidate stencils
|
||||
float q1 = (2.0f * phi_m2 - 7.0f * phi_m1 + 11.0f * phi_0) / 6.0f;
|
||||
float q2 = (-phi_m1 + 5.0f * phi_0 + 2.0f * phi_p1) / 6.0f;
|
||||
float q3 = (2.0f * phi_0 + 5.0f * phi_p1 - phi_p2) / 6.0f;
|
||||
|
||||
// Compute smoothness indicators
|
||||
float beta1 = (13.0f/12.0f) * (phi_m2 - 2.0f * phi_m1 + phi_0) * (phi_m2 - 2.0f * phi_m1 + phi_0) +
|
||||
(1.0f/4.0f) * (phi_m2 - 4.0f * phi_m1 + 3.0f * phi_0) * (phi_m2 - 4.0f * phi_m1 + 3.0f * phi_0);
|
||||
|
||||
float beta2 = (13.0f/12.0f) * (phi_m1 - 2.0f * phi_0 + phi_p1) * (phi_m1 - 2.0f * phi_0 + phi_p1) +
|
||||
(1.0f/4.0f) * (phi_m1 - phi_p1) * (phi_m1 - phi_p1);
|
||||
|
||||
float beta3 = (13.0f/12.0f) * (phi_0 - 2.0f * phi_p1 + phi_p2) * (phi_0 - 2.0f * phi_p1 + phi_p2) +
|
||||
(1.0f/4.0f) * (3.0f * phi_0 - 4.0f * phi_p1 + phi_p2) * (3.0f * phi_0 - 4.0f * phi_p1 + phi_p2);
|
||||
|
||||
// Compute weights
|
||||
float alpha1 = 0.1f / ((eps + beta1) * (eps + beta1));
|
||||
float alpha2 = 0.6f / ((eps + beta2) * (eps + beta2));
|
||||
float alpha3 = 0.3f / ((eps + beta3) * (eps + beta3));
|
||||
|
||||
float sum_alpha = alpha1 + alpha2 + alpha3;
|
||||
|
||||
float w1 = alpha1 / sum_alpha;
|
||||
float w2 = alpha2 / sum_alpha;
|
||||
float w3 = alpha3 / sum_alpha;
|
||||
|
||||
// Compute final reconstruction
|
||||
return w1 * q1 + w2 * q2 + w3 * q3;
|
||||
}
|
||||
|
||||
extern "C" __global__ void advection_1d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
float velocity,
|
||||
float dt,
|
||||
float dx,
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
float cfl = velocity * dt / dx;
|
||||
|
||||
// Need extended stencil for WENO5 scheme
|
||||
int im2, im1, ip1, ip2;
|
||||
|
||||
// Periodic boundary conditions
|
||||
im2 = (i <= 1) ? n + i - 2 : i - 2;
|
||||
im1 = (i == 0) ? n - 1 : i - 1;
|
||||
ip1 = (i == n - 1) ? 0 : i + 1;
|
||||
ip2 = (i >= n - 2) ? i + 2 - n : i + 2;
|
||||
|
||||
float phi_val = phi[i];
|
||||
|
||||
if (velocity > 0.0f) {
|
||||
// WENO5 reconstruction for positive velocity
|
||||
int im3 = (im2 <= 1) ? n + im2 - 2 : im2 - 2;
|
||||
float phi_face = weno5_reconstruction(phi[im3], phi[im2], phi[im1], phi_val, phi[ip1]);
|
||||
|
||||
int im4 = (im3 == 0) ? n - 1 : im3 - 1;
|
||||
float phi_face_upstream = weno5_reconstruction(phi[im4], phi[im3], phi[im2], phi[im1], phi_val);
|
||||
|
||||
phi_new[i] = phi_val - cfl * (phi_face - phi_face_upstream);
|
||||
} else {
|
||||
// WENO5 reconstruction for negative velocity
|
||||
int ip3 = (ip2 >= n - 2) ? ip2 + 2 - n : ip2 + 2;
|
||||
float phi_face = weno5_reconstruction(phi[ip3], phi[ip2], phi[ip1], phi_val, phi[im1]);
|
||||
|
||||
int ip4 = (ip3 == n - 1) ? 0 : ip3 + 1;
|
||||
float phi_face_downstream = weno5_reconstruction(phi[ip4], phi[ip3], phi[ip2], phi[ip1], phi_val);
|
||||
|
||||
phi_new[i] = phi_val - cfl * (phi_face_downstream - phi_face);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void advection_2d(
|
||||
const float* __restrict__ phi,
|
||||
float* __restrict__ phi_new,
|
||||
const float* __restrict__ u,
|
||||
const float* __restrict__ v,
|
||||
float dt,
|
||||
float dx,
|
||||
float 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;
|
||||
|
||||
// Get velocities at this point
|
||||
float u_val = u[idx];
|
||||
float v_val = v[idx];
|
||||
|
||||
float cfl_x = u_val * dt / dx;
|
||||
float cfl_y = v_val * dt / dy;
|
||||
|
||||
float phi_val = phi[idx];
|
||||
|
||||
// X-direction WENO5 scheme
|
||||
float advection_x = 0.0f;
|
||||
if (i >= 2 && i <= nx - 3) {
|
||||
// Full WENO5 stencil available
|
||||
int im2 = i - 2;
|
||||
int im1 = i - 1;
|
||||
int ip1 = i + 1;
|
||||
int ip2 = i + 2;
|
||||
|
||||
int idx_im2 = j * nx + im2;
|
||||
int idx_im1 = j * nx + im1;
|
||||
int idx_ip1 = j * nx + ip1;
|
||||
int idx_ip2 = j * nx + ip2;
|
||||
|
||||
if (u_val > 0.0f) {
|
||||
int im3 = i - 3;
|
||||
int idx_im3 = j * nx + im3;
|
||||
float phi_face = weno5_reconstruction(phi[idx_im3], phi[idx_im2], phi[idx_im1], phi_val, phi[idx_ip1]);
|
||||
|
||||
int im4 = i - 4;
|
||||
int idx_im4 = j * nx + im4;
|
||||
float phi_face_upstream = weno5_reconstruction(phi[idx_im4], phi[idx_im3], phi[idx_im2], phi[idx_im1], phi_val);
|
||||
|
||||
advection_x = cfl_x * (phi_face - phi_face_upstream);
|
||||
} else {
|
||||
int ip3 = i + 3;
|
||||
int idx_ip3 = j * nx + ip3;
|
||||
float phi_face = weno5_reconstruction(phi[idx_ip3], phi[idx_ip2], phi[idx_ip1], phi_val, phi[idx_im1]);
|
||||
|
||||
int ip4 = i + 4;
|
||||
int idx_ip4 = j * nx + ip4;
|
||||
float phi_face_downstream = weno5_reconstruction(phi[idx_ip4], phi[idx_ip3], phi[idx_ip2], phi[idx_ip1], phi_val);
|
||||
|
||||
advection_x = cfl_x * (phi_face_downstream - phi_face);
|
||||
}
|
||||
} else {
|
||||
// Fall back to upwind near boundaries
|
||||
int im1 = (i == 0) ? nx - 1 : i - 1;
|
||||
int ip1 = (i == nx - 1) ? 0 : i + 1;
|
||||
int idx_im1 = j * nx + im1;
|
||||
int idx_ip1 = j * nx + ip1;
|
||||
|
||||
if (u_val > 0.0f) {
|
||||
advection_x = cfl_x * (phi_val - phi[idx_im1]);
|
||||
} else {
|
||||
advection_x = cfl_x * (phi[idx_ip1] - phi_val);
|
||||
}
|
||||
}
|
||||
|
||||
// Y-direction WENO5 scheme (similar logic)
|
||||
float advection_y = 0.0f;
|
||||
if (j >= 2 && j <= ny - 3) {
|
||||
// Full WENO5 stencil available
|
||||
int jm2 = j - 2;
|
||||
int jm1 = j - 1;
|
||||
int jp1 = j + 1;
|
||||
int jp2 = j + 2;
|
||||
|
||||
int idx_jm2 = jm2 * nx + i;
|
||||
int idx_jm1 = jm1 * nx + i;
|
||||
int idx_jp1 = jp1 * nx + i;
|
||||
int idx_jp2 = jp2 * nx + i;
|
||||
|
||||
if (v_val > 0.0f) {
|
||||
int jm3 = j - 3;
|
||||
int idx_jm3 = jm3 * nx + i;
|
||||
float phi_face = weno5_reconstruction(phi[idx_jm3], phi[idx_jm2], phi[idx_jm1], phi_val, phi[idx_jp1]);
|
||||
|
||||
int jm4 = j - 4;
|
||||
int idx_jm4 = jm4 * nx + i;
|
||||
float phi_face_upstream = weno5_reconstruction(phi[idx_jm4], phi[idx_jm3], phi[idx_jm2], phi[idx_jm1], phi_val);
|
||||
|
||||
advection_y = cfl_y * (phi_face - phi_face_upstream);
|
||||
} else {
|
||||
int jp3 = j + 3;
|
||||
int idx_jp3 = jp3 * nx + i;
|
||||
float phi_face = weno5_reconstruction(phi[idx_jp3], phi[idx_jp2], phi[idx_jp1], phi_val, phi[idx_jm1]);
|
||||
|
||||
int jp4 = j + 4;
|
||||
int idx_jp4 = jp4 * nx + i;
|
||||
float phi_face_downstream = weno5_reconstruction(phi[idx_jp4], phi[idx_jp3], phi[idx_jp2], phi[idx_jp1], phi_val);
|
||||
|
||||
advection_y = cfl_y * (phi_face_downstream - phi_face);
|
||||
}
|
||||
} else {
|
||||
// Fall back to upwind near boundaries
|
||||
int jm1 = (j == 0) ? ny - 1 : j - 1;
|
||||
int jp1 = (j == ny - 1) ? 0 : j + 1;
|
||||
int idx_jm1 = jm1 * nx + i;
|
||||
int idx_jp1 = jp1 * nx + i;
|
||||
|
||||
if (v_val > 0.0f) {
|
||||
advection_y = cfl_y * (phi_val - phi[idx_jm1]);
|
||||
} else {
|
||||
advection_y = cfl_y * (phi[idx_jp1] - phi_val);
|
||||
}
|
||||
}
|
||||
|
||||
phi_new[idx] = phi_val - advection_x - advection_y;
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
/**
|
||||
* CUDA kernels for diffusion equation
|
||||
*
|
||||
* Implements various schemes for the diffusion/heat equation:
|
||||
* ∂T/∂t = α∇²T
|
||||
*
|
||||
* Includes explicit, implicit, and Crank-Nicolson schemes.
|
||||
*/
|
||||
|
||||
extern "C" __global__ void diffusion_explicit_1d(
|
||||
const float* __restrict__ temp,
|
||||
float* __restrict__ temp_new,
|
||||
float alpha,
|
||||
float dt,
|
||||
float dx,
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
float r = alpha * dt / (dx * dx); // Diffusion number
|
||||
|
||||
// Stability condition: r <= 0.5 for explicit scheme
|
||||
if (r > 0.5f) {
|
||||
// Issue warning - this should be handled at higher level
|
||||
return;
|
||||
}
|
||||
|
||||
// Boundary conditions (Dirichlet for now)
|
||||
if (i == 0 || i == n - 1) {
|
||||
temp_new[i] = temp[i]; // Fixed boundary values
|
||||
return;
|
||||
}
|
||||
|
||||
// Explicit finite difference: T_i^{n+1} = T_i^n + r(T_{i+1}^n - 2T_i^n + T_{i-1}^n)
|
||||
temp_new[i] = temp[i] + r * (temp[i + 1] - 2.0f * temp[i] + temp[i - 1]);
|
||||
}
|
||||
|
||||
extern "C" __global__ void diffusion_explicit_2d(
|
||||
const float* __restrict__ temp,
|
||||
float* __restrict__ temp_new,
|
||||
float alpha,
|
||||
float dt,
|
||||
float dx,
|
||||
float 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;
|
||||
|
||||
float rx = alpha * dt / (dx * dx);
|
||||
float ry = alpha * dt / (dy * dy);
|
||||
|
||||
// Stability condition: rx + ry <= 0.5 for 2D explicit scheme
|
||||
if (rx + ry > 0.5f) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Boundary conditions
|
||||
if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) {
|
||||
temp_new[idx] = temp[idx]; // Fixed boundary values
|
||||
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;
|
||||
|
||||
// 2D explicit finite difference
|
||||
temp_new[idx] = temp[idx] +
|
||||
rx * (temp[idx_ip1] - 2.0f * temp[idx] + temp[idx_im1]) +
|
||||
ry * (temp[idx_jp1] - 2.0f * temp[idx] + temp[idx_jm1]);
|
||||
}
|
||||
|
||||
extern "C" __global__ void diffusion_implicit_1d(
|
||||
const float* __restrict__ temp,
|
||||
float* __restrict__ temp_new,
|
||||
float alpha,
|
||||
float dt,
|
||||
float dx,
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
// For implicit scheme, this kernel performs one iteration of iterative solver
|
||||
// (e.g., Jacobi iteration for the linear system)
|
||||
|
||||
float r = alpha * dt / (dx * dx);
|
||||
|
||||
// Boundary conditions
|
||||
if (i == 0 || i == n - 1) {
|
||||
temp_new[i] = temp[i];
|
||||
return;
|
||||
}
|
||||
|
||||
// Jacobi iteration for implicit scheme:
|
||||
// (1 + 2r)T_i^{n+1} - r(T_{i+1}^{n+1} + T_{i-1}^{n+1}) = T_i^n
|
||||
// T_i^{n+1} = (T_i^n + r(T_{i+1}^{n+1} + T_{i-1}^{n+1})) / (1 + 2r)
|
||||
|
||||
temp_new[i] = (temp[i] + r * (temp_new[i + 1] + temp_new[i - 1])) / (1.0f + 2.0f * r);
|
||||
}
|
||||
|
||||
extern "C" __global__ void diffusion_crank_nicolson_1d(
|
||||
const float* __restrict__ temp,
|
||||
float* __restrict__ temp_new,
|
||||
float alpha,
|
||||
float dt,
|
||||
float dx,
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
float r = alpha * dt / (dx * dx);
|
||||
|
||||
// Boundary conditions
|
||||
if (i == 0 || i == n - 1) {
|
||||
temp_new[i] = temp[i];
|
||||
return;
|
||||
}
|
||||
|
||||
// Crank-Nicolson scheme (θ = 0.5):
|
||||
// T_i^{n+1} - 0.5*r*(T_{i+1}^{n+1} - 2*T_i^{n+1} + T_{i-1}^{n+1}) =
|
||||
// T_i^n + 0.5*r*(T_{i+1}^n - 2*T_i^n + T_{i-1}^n)
|
||||
|
||||
float rhs = temp[i] + 0.5f * r * (temp[i + 1] - 2.0f * temp[i] + temp[i - 1]);
|
||||
|
||||
// Jacobi iteration for Crank-Nicolson
|
||||
temp_new[i] = (rhs + 0.5f * r * (temp_new[i + 1] + temp_new[i - 1])) / (1.0f + r);
|
||||
}
|
||||
|
||||
// Helper kernel for tridiagonal solver (Thomas algorithm)
|
||||
extern "C" __global__ void thomas_forward_elimination(
|
||||
float* __restrict__ a, // Lower diagonal
|
||||
float* __restrict__ b, // Main diagonal
|
||||
float* __restrict__ c, // Upper diagonal
|
||||
float* __restrict__ d, // Right hand side
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x + 1; // Start from i=1
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
// Forward elimination step
|
||||
float m = a[i] / b[i - 1];
|
||||
b[i] = b[i] - m * c[i - 1];
|
||||
d[i] = d[i] - m * d[i - 1];
|
||||
}
|
||||
|
||||
extern "C" __global__ void thomas_backward_substitution(
|
||||
const float* __restrict__ b, // Modified main diagonal
|
||||
const float* __restrict__ c, // Upper diagonal
|
||||
float* __restrict__ d, // Modified RHS / solution
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n - 1) return; // n-2, n-3, ..., 0
|
||||
|
||||
int idx = n - 2 - i; // Reverse order
|
||||
|
||||
if (idx < n - 1) {
|
||||
d[idx] = (d[idx] - c[idx] * d[idx + 1]) / b[idx];
|
||||
}
|
||||
}
|
||||
|
||||
// Advanced diffusion kernels with anisotropic diffusion
|
||||
extern "C" __global__ void anisotropic_diffusion_2d(
|
||||
const float* __restrict__ temp,
|
||||
float* __restrict__ temp_new,
|
||||
const float* __restrict__ kappa_x, // Diffusivity in x-direction
|
||||
const float* __restrict__ kappa_y, // Diffusivity in y-direction
|
||||
float dt,
|
||||
float dx,
|
||||
float 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
|
||||
if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) {
|
||||
temp_new[idx] = temp[idx];
|
||||
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;
|
||||
|
||||
// Anisotropic diffusion with spatially varying diffusivity
|
||||
float kx = kappa_x[idx];
|
||||
float ky = kappa_y[idx];
|
||||
|
||||
float rx = kx * dt / (dx * dx);
|
||||
float ry = ky * dt / (dy * dy);
|
||||
|
||||
// Check stability condition
|
||||
if (rx + ry > 0.5f) {
|
||||
temp_new[idx] = temp[idx]; // Skip update if unstable
|
||||
return;
|
||||
}
|
||||
|
||||
temp_new[idx] = temp[idx] +
|
||||
rx * (temp[idx_ip1] - 2.0f * temp[idx] + temp[idx_im1]) +
|
||||
ry * (temp[idx_jp1] - 2.0f * temp[idx] + temp[idx_jm1]);
|
||||
}
|
||||
|
||||
// Kernel for nonlinear diffusion (e.g., for turbulent heat transfer)
|
||||
extern "C" __global__ void nonlinear_diffusion_2d(
|
||||
const float* __restrict__ temp,
|
||||
float* __restrict__ temp_new,
|
||||
float base_alpha,
|
||||
float dt,
|
||||
float dx,
|
||||
float 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
|
||||
if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) {
|
||||
temp_new[idx] = temp[idx];
|
||||
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 local gradient magnitude
|
||||
float grad_x = (temp[idx_ip1] - temp[idx_im1]) / (2.0f * dx);
|
||||
float grad_y = (temp[idx_jp1] - temp[idx_jm1]) / (2.0f * dy);
|
||||
float grad_mag = sqrtf(grad_x * grad_x + grad_y * grad_y);
|
||||
|
||||
// Nonlinear diffusivity (example: edge-preserving diffusion)
|
||||
float alpha = base_alpha / (1.0f + grad_mag * grad_mag);
|
||||
|
||||
float rx = alpha * dt / (dx * dx);
|
||||
float ry = alpha * dt / (dy * dy);
|
||||
|
||||
// Check stability
|
||||
if (rx + ry > 0.5f) {
|
||||
temp_new[idx] = temp[idx];
|
||||
return;
|
||||
}
|
||||
|
||||
temp_new[idx] = temp[idx] +
|
||||
rx * (temp[idx_ip1] - 2.0f * temp[idx] + temp[idx_im1]) +
|
||||
ry * (temp[idx_jp1] - 2.0f * temp[idx] + temp[idx_jm1]);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 2D Implicit and Crank-Nicolson diffusion kernels using ADI method
|
||||
*/
|
||||
|
||||
/**
|
||||
* 2D Implicit diffusion using ADI (Alternating Direction Implicit) method
|
||||
* Solves the heat equation implicitly by splitting into two 1D problems
|
||||
*/
|
||||
extern "C" __global__ void diffusion_implicit_2d_adi(
|
||||
const float* __restrict__ temp,
|
||||
float* __restrict__ temp_new,
|
||||
float alpha,
|
||||
float dt,
|
||||
float dx,
|
||||
float 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
|
||||
if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) {
|
||||
temp_new[idx] = temp[idx];
|
||||
return;
|
||||
}
|
||||
|
||||
float rx = alpha * dt / (2.0f * dx * dx);
|
||||
float ry = alpha * dt / (2.0f * dy * dy);
|
||||
|
||||
// ADI method: alternating between x and y sweeps
|
||||
// This is a simplified version - full ADI requires two passes
|
||||
int idx_im1 = j * nx + (i - 1);
|
||||
int idx_ip1 = j * nx + (i + 1);
|
||||
int idx_jm1 = (j - 1) * nx + i;
|
||||
int idx_jp1 = (j + 1) * nx + i;
|
||||
|
||||
// Compute intermediate value (simplified ADI)
|
||||
float temp_intermediate = temp[idx] +
|
||||
rx * (temp[idx_ip1] - 2.0f * temp[idx] + temp[idx_im1]) +
|
||||
ry * (temp[idx_jp1] - 2.0f * temp[idx] + temp[idx_jm1]);
|
||||
|
||||
temp_new[idx] = temp_intermediate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 2D Crank-Nicolson diffusion using ADI method
|
||||
* Semi-implicit scheme that is unconditionally stable
|
||||
*/
|
||||
extern "C" __global__ void diffusion_crank_nicolson_2d_adi(
|
||||
const float* __restrict__ temp,
|
||||
float* __restrict__ temp_new,
|
||||
float alpha,
|
||||
float dt,
|
||||
float dx,
|
||||
float 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
|
||||
if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) {
|
||||
temp_new[idx] = temp[idx];
|
||||
return;
|
||||
}
|
||||
|
||||
float rx = alpha * dt / (dx * dx);
|
||||
float ry = alpha * dt / (dy * dy);
|
||||
|
||||
int idx_im1 = j * nx + (i - 1);
|
||||
int idx_ip1 = j * nx + (i + 1);
|
||||
int idx_jm1 = (j - 1) * nx + i;
|
||||
int idx_jp1 = (j + 1) * nx + i;
|
||||
|
||||
// Crank-Nicolson: average of explicit and implicit
|
||||
float explicit_part = 0.5f * (
|
||||
rx * (temp[idx_ip1] - 2.0f * temp[idx] + temp[idx_im1]) +
|
||||
ry * (temp[idx_jp1] - 2.0f * temp[idx] + temp[idx_jm1])
|
||||
);
|
||||
|
||||
// For the implicit part, we use the current temp_new values
|
||||
// This requires iterative solving - simplified here
|
||||
float implicit_part = 0.5f * (
|
||||
rx * (temp_new[idx_ip1] - 2.0f * temp_new[idx] + temp_new[idx_im1]) +
|
||||
ry * (temp_new[idx_jp1] - 2.0f * temp_new[idx] + temp_new[idx_jm1])
|
||||
);
|
||||
|
||||
temp_new[idx] = temp[idx] + explicit_part + implicit_part;
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
/**
|
||||
* CUDA kernels for matrix operations
|
||||
*
|
||||
* Implements common linear algebra operations needed for CFD:
|
||||
* - Sparse matrix-vector multiplication
|
||||
* - Tridiagonal matrix solver (Thomas algorithm)
|
||||
* - Vector operations (dot product, norms, etc.)
|
||||
* - Reduction operations
|
||||
*/
|
||||
|
||||
extern "C" __global__ void tridiagonal_matvec(
|
||||
const float* __restrict__ diagonal, // Main diagonal (size n)
|
||||
const float* __restrict__ off_diagonal, // Super/sub diagonal (size n-1)
|
||||
const float* __restrict__ x, // Input vector
|
||||
float* __restrict__ y, // Output vector
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
float result = diagonal[i] * x[i];
|
||||
|
||||
// Add contribution from super-diagonal
|
||||
if (i > 0) {
|
||||
result += off_diagonal[i - 1] * x[i - 1];
|
||||
}
|
||||
|
||||
// Add contribution from sub-diagonal
|
||||
if (i < n - 1) {
|
||||
result += off_diagonal[i] * x[i + 1];
|
||||
}
|
||||
|
||||
y[i] = result;
|
||||
}
|
||||
|
||||
extern "C" __global__ void pentadiagonal_matvec(
|
||||
const float* __restrict__ diag_mm, // -2 diagonal
|
||||
const float* __restrict__ diag_m, // -1 diagonal
|
||||
const float* __restrict__ diag_0, // Main diagonal
|
||||
const float* __restrict__ diag_p, // +1 diagonal
|
||||
const float* __restrict__ diag_pp, // +2 diagonal
|
||||
const float* __restrict__ x,
|
||||
float* __restrict__ y,
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
float result = diag_0[i] * x[i];
|
||||
|
||||
if (i >= 2) result += diag_mm[i - 2] * x[i - 2];
|
||||
if (i >= 1) result += diag_m[i - 1] * x[i - 1];
|
||||
if (i < n - 1) result += diag_p[i] * x[i + 1];
|
||||
if (i < n - 2) result += diag_pp[i] * x[i + 2];
|
||||
|
||||
y[i] = result;
|
||||
}
|
||||
|
||||
// Sparse matrix-vector multiplication for CFD matrices (5-point stencil in 2D)
|
||||
extern "C" __global__ void sparse_matvec_2d_5point(
|
||||
const float* __restrict__ center, // Center coefficients
|
||||
const float* __restrict__ east, // East coefficients
|
||||
const float* __restrict__ west, // West coefficients
|
||||
const float* __restrict__ north, // North coefficients
|
||||
const float* __restrict__ south, // South coefficients
|
||||
const float* __restrict__ x,
|
||||
float* __restrict__ y,
|
||||
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 result = center[idx] * x[idx];
|
||||
|
||||
// East neighbor
|
||||
if (i < nx - 1) {
|
||||
int idx_e = j * nx + (i + 1);
|
||||
result += east[idx] * x[idx_e];
|
||||
}
|
||||
|
||||
// West neighbor
|
||||
if (i > 0) {
|
||||
int idx_w = j * nx + (i - 1);
|
||||
result += west[idx] * x[idx_w];
|
||||
}
|
||||
|
||||
// North neighbor
|
||||
if (j < ny - 1) {
|
||||
int idx_n = (j + 1) * nx + i;
|
||||
result += north[idx] * x[idx_n];
|
||||
}
|
||||
|
||||
// South neighbor
|
||||
if (j > 0) {
|
||||
int idx_s = (j - 1) * nx + i;
|
||||
result += south[idx] * x[idx_s];
|
||||
}
|
||||
|
||||
y[idx] = result;
|
||||
}
|
||||
|
||||
// Vector operations
|
||||
extern "C" __global__ void vector_add(
|
||||
const float* __restrict__ x,
|
||||
const float* __restrict__ y,
|
||||
float* __restrict__ result,
|
||||
float alpha, // result = x + alpha * y
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
result[i] = x[i] + alpha * y[i];
|
||||
}
|
||||
|
||||
extern "C" __global__ void vector_scale(
|
||||
float* __restrict__ x,
|
||||
float alpha,
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
x[i] *= alpha;
|
||||
}
|
||||
|
||||
// Dot product using shared memory reduction
|
||||
extern "C" __global__ void dot_product_partial(
|
||||
const float* __restrict__ x,
|
||||
const float* __restrict__ y,
|
||||
float* __restrict__ partial_sum,
|
||||
int n
|
||||
) {
|
||||
extern __shared__ float sdata[];
|
||||
|
||||
int tid = threadIdx.x;
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
// Load data into shared memory
|
||||
sdata[tid] = (i < n) ? x[i] * y[i] : 0.0f;
|
||||
__syncthreads();
|
||||
|
||||
// Perform reduction in shared memory
|
||||
for (int s = blockDim.x / 2; s > 0; s >>= 1) {
|
||||
if (tid < s) {
|
||||
sdata[tid] += sdata[tid + s];
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// Write result for this block to global memory
|
||||
if (tid == 0) {
|
||||
partial_sum[blockIdx.x] = sdata[0];
|
||||
}
|
||||
}
|
||||
|
||||
// L2 norm computation
|
||||
extern "C" __global__ void l2_norm_partial(
|
||||
const float* __restrict__ x,
|
||||
float* __restrict__ partial_sum,
|
||||
int n
|
||||
) {
|
||||
extern __shared__ float sdata[];
|
||||
|
||||
int tid = threadIdx.x;
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
// Load and square data
|
||||
float val = (i < n) ? x[i] : 0.0f;
|
||||
sdata[tid] = val * val;
|
||||
__syncthreads();
|
||||
|
||||
// Reduction
|
||||
for (int s = blockDim.x / 2; s > 0; s >>= 1) {
|
||||
if (tid < s) {
|
||||
sdata[tid] += sdata[tid + s];
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
if (tid == 0) {
|
||||
partial_sum[blockIdx.x] = sdata[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Thomas algorithm for tridiagonal systems (forward elimination)
|
||||
extern "C" __global__ void thomas_forward_sweep(
|
||||
float* __restrict__ c_prime, // Modified upper diagonal
|
||||
float* __restrict__ d_prime, // Modified RHS
|
||||
const float* __restrict__ a, // Lower diagonal
|
||||
const float* __restrict__ b, // Main diagonal
|
||||
const float* __restrict__ c, // Upper diagonal
|
||||
const float* __restrict__ d, // RHS
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
if (i == 0) {
|
||||
c_prime[0] = c[0] / b[0];
|
||||
d_prime[0] = d[0] / b[0];
|
||||
} else {
|
||||
float denominator = b[i] - a[i - 1] * c_prime[i - 1];
|
||||
|
||||
if (i < n - 1) {
|
||||
c_prime[i] = c[i] / denominator;
|
||||
}
|
||||
d_prime[i] = (d[i] - a[i - 1] * d_prime[i - 1]) / denominator;
|
||||
}
|
||||
}
|
||||
|
||||
// Thomas algorithm backward substitution
|
||||
extern "C" __global__ void thomas_backward_sweep(
|
||||
float* __restrict__ x, // Solution vector
|
||||
const float* __restrict__ c_prime,
|
||||
const float* __restrict__ d_prime,
|
||||
int n
|
||||
) {
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= n) return;
|
||||
|
||||
int idx = n - 1 - i; // Process in reverse order
|
||||
|
||||
if (idx == n - 1) {
|
||||
x[idx] = d_prime[idx];
|
||||
} else {
|
||||
x[idx] = d_prime[idx] - c_prime[idx] * x[idx + 1];
|
||||
}
|
||||
}
|
||||
|
||||
// Specialized operations for CFD
|
||||
extern "C" __global__ void compute_divergence_2d(
|
||||
float* __restrict__ divergence,
|
||||
const float* __restrict__ u, // x-velocity
|
||||
const float* __restrict__ v, // y-velocity
|
||||
float dx_inv,
|
||||
float dy_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;
|
||||
|
||||
float du_dx, dv_dy;
|
||||
|
||||
// Central differences for interior points
|
||||
if (i > 0 && i < nx - 1) {
|
||||
int idx_e = j * nx + (i + 1);
|
||||
int idx_w = j * nx + (i - 1);
|
||||
du_dx = 0.5f * dx_inv * (u[idx_e] - u[idx_w]);
|
||||
} else {
|
||||
// One-sided differences at boundaries
|
||||
if (i == 0) {
|
||||
int idx_e = j * nx + (i + 1);
|
||||
du_dx = dx_inv * (u[idx_e] - u[idx]);
|
||||
} else {
|
||||
int idx_w = j * nx + (i - 1);
|
||||
du_dx = dx_inv * (u[idx] - u[idx_w]);
|
||||
}
|
||||
}
|
||||
|
||||
if (j > 0 && j < ny - 1) {
|
||||
int idx_n = (j + 1) * nx + i;
|
||||
int idx_s = (j - 1) * nx + i;
|
||||
dv_dy = 0.5f * dy_inv * (v[idx_n] - v[idx_s]);
|
||||
} else {
|
||||
if (j == 0) {
|
||||
int idx_n = (j + 1) * nx + i;
|
||||
dv_dy = dy_inv * (v[idx_n] - v[idx]);
|
||||
} else {
|
||||
int idx_s = (j - 1) * nx + i;
|
||||
dv_dy = dy_inv * (v[idx] - v[idx_s]);
|
||||
}
|
||||
}
|
||||
|
||||
divergence[idx] = du_dx + dv_dy;
|
||||
}
|
||||
|
||||
extern "C" __global__ void compute_gradient_2d(
|
||||
float* __restrict__ grad_x,
|
||||
float* __restrict__ grad_y,
|
||||
const float* __restrict__ phi,
|
||||
float dx_inv,
|
||||
float dy_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;
|
||||
|
||||
// Compute ∂φ/∂x
|
||||
if (i > 0 && i < nx - 1) {
|
||||
int idx_e = j * nx + (i + 1);
|
||||
int idx_w = j * nx + (i - 1);
|
||||
grad_x[idx] = 0.5f * dx_inv * (phi[idx_e] - phi[idx_w]);
|
||||
} else {
|
||||
if (i == 0) {
|
||||
int idx_e = j * nx + (i + 1);
|
||||
grad_x[idx] = dx_inv * (phi[idx_e] - phi[idx]);
|
||||
} else {
|
||||
int idx_w = j * nx + (i - 1);
|
||||
grad_x[idx] = dx_inv * (phi[idx] - phi[idx_w]);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute ∂φ/∂y
|
||||
if (j > 0 && j < ny - 1) {
|
||||
int idx_n = (j + 1) * nx + i;
|
||||
int idx_s = (j - 1) * nx + i;
|
||||
grad_y[idx] = 0.5f * dy_inv * (phi[idx_n] - phi[idx_s]);
|
||||
} else {
|
||||
if (j == 0) {
|
||||
int idx_n = (j + 1) * nx + i;
|
||||
grad_y[idx] = dy_inv * (phi[idx_n] - phi[idx]);
|
||||
} else {
|
||||
int idx_s = (j - 1) * nx + i;
|
||||
grad_y[idx] = dy_inv * (phi[idx] - phi[idx_s]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AXPY operation: y = alpha * x + y
|
||||
*/
|
||||
extern "C" __global__ void vector_axpy(
|
||||
float alpha,
|
||||
const float* __restrict__ x,
|
||||
float* __restrict__ y,
|
||||
int n
|
||||
) {
|
||||
int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (idx >= n) return;
|
||||
|
||||
y[idx] = alpha * x[idx] + y[idx];
|
||||
}
|
||||
@@ -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])
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
/**
|
||||
* CUDA kernels for reduction operations
|
||||
*
|
||||
* Implements efficient parallel reduction operations for CFD:
|
||||
* - Max reduction (for residual checking)
|
||||
* - Sum reduction (for norms and dot products)
|
||||
* - Min reduction (for stability checks)
|
||||
*
|
||||
* Using warp shuffle operations for modern GPUs
|
||||
*/
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
#include <float.h>
|
||||
|
||||
// Warp size constant
|
||||
#define WARP_SIZE 32
|
||||
|
||||
/**
|
||||
* Warp-level reduction using shuffle operations
|
||||
*/
|
||||
__device__ float warp_reduce_sum(float val) {
|
||||
for (int offset = WARP_SIZE/2; offset > 0; offset /= 2) {
|
||||
val += __shfl_down_sync(0xffffffff, val, offset);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
__device__ float warp_reduce_max(float val) {
|
||||
for (int offset = WARP_SIZE/2; offset > 0; offset /= 2) {
|
||||
val = fmaxf(val, __shfl_down_sync(0xffffffff, val, offset));
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
__device__ float warp_reduce_min(float val) {
|
||||
for (int offset = WARP_SIZE/2; offset > 0; offset /= 2) {
|
||||
val = fminf(val, __shfl_down_sync(0xffffffff, val, offset));
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block-level reduction using shared memory and warp reduction
|
||||
*/
|
||||
__device__ float block_reduce_sum(float val) {
|
||||
__shared__ float shared[32]; // One value per warp
|
||||
int lane = threadIdx.x % WARP_SIZE;
|
||||
int wid = threadIdx.x / WARP_SIZE;
|
||||
|
||||
// First reduce within warps
|
||||
val = warp_reduce_sum(val);
|
||||
|
||||
// Write warp results to shared memory
|
||||
if (lane == 0) shared[wid] = val;
|
||||
__syncthreads();
|
||||
|
||||
// Final reduction of warp results
|
||||
val = (threadIdx.x < blockDim.x / WARP_SIZE) ? shared[lane] : 0;
|
||||
if (wid == 0) val = warp_reduce_sum(val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
__device__ float block_reduce_max(float val) {
|
||||
__shared__ float shared[32];
|
||||
int lane = threadIdx.x % WARP_SIZE;
|
||||
int wid = threadIdx.x / WARP_SIZE;
|
||||
|
||||
val = warp_reduce_max(val);
|
||||
|
||||
if (lane == 0) shared[wid] = val;
|
||||
__syncthreads();
|
||||
|
||||
val = (threadIdx.x < blockDim.x / WARP_SIZE) ? shared[lane] : -FLT_MAX;
|
||||
if (wid == 0) val = warp_reduce_max(val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum reduction kernel - reduces array to single value
|
||||
* Each block produces a partial sum, requires second pass for final result
|
||||
*/
|
||||
extern "C" __global__ void reduce_sum(
|
||||
const float* __restrict__ input,
|
||||
float* __restrict__ output,
|
||||
int n
|
||||
) {
|
||||
float sum = 0;
|
||||
|
||||
// Grid-stride loop for coalesced memory access
|
||||
for (int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
i < n;
|
||||
i += blockDim.x * gridDim.x) {
|
||||
sum += input[i];
|
||||
}
|
||||
|
||||
// Reduce within block
|
||||
sum = block_reduce_sum(sum);
|
||||
|
||||
// Write block result
|
||||
if (threadIdx.x == 0) {
|
||||
output[blockIdx.x] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Max reduction kernel - finds maximum value in array
|
||||
*/
|
||||
extern "C" __global__ void reduce_max(
|
||||
const float* __restrict__ input,
|
||||
float* __restrict__ output,
|
||||
int n
|
||||
) {
|
||||
float max_val = -FLT_MAX;
|
||||
|
||||
// Grid-stride loop
|
||||
for (int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
i < n;
|
||||
i += blockDim.x * gridDim.x) {
|
||||
max_val = fmaxf(max_val, input[i]);
|
||||
}
|
||||
|
||||
// Reduce within block
|
||||
max_val = block_reduce_max(max_val);
|
||||
|
||||
// Write block result
|
||||
if (threadIdx.x == 0) {
|
||||
output[blockIdx.x] = max_val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute max reduction kernel - finds maximum absolute value
|
||||
* Used for residual checking in iterative solvers
|
||||
*/
|
||||
extern "C" __global__ void reduce_abs_max(
|
||||
const float* __restrict__ input,
|
||||
float* __restrict__ output,
|
||||
int n
|
||||
) {
|
||||
float max_val = 0;
|
||||
|
||||
// Grid-stride loop with absolute value
|
||||
for (int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
i < n;
|
||||
i += blockDim.x * gridDim.x) {
|
||||
max_val = fmaxf(max_val, fabsf(input[i]));
|
||||
}
|
||||
|
||||
// Reduce within block
|
||||
max_val = block_reduce_max(max_val);
|
||||
|
||||
// Write block result
|
||||
if (threadIdx.x == 0) {
|
||||
output[blockIdx.x] = max_val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* L2 norm squared reduction - computes sum of squares
|
||||
*/
|
||||
extern "C" __global__ void reduce_norm2_squared(
|
||||
const float* __restrict__ input,
|
||||
float* __restrict__ output,
|
||||
int n
|
||||
) {
|
||||
float sum = 0;
|
||||
|
||||
// Grid-stride loop with squaring
|
||||
for (int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
i < n;
|
||||
i += blockDim.x * gridDim.x) {
|
||||
float val = input[i];
|
||||
sum += val * val;
|
||||
}
|
||||
|
||||
// Reduce within block
|
||||
sum = block_reduce_sum(sum);
|
||||
|
||||
// Write block result
|
||||
if (threadIdx.x == 0) {
|
||||
output[blockIdx.x] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Final reduction kernel - reduces partial results from blocks
|
||||
* Call with single block after first reduction pass
|
||||
*/
|
||||
extern "C" __global__ void reduce_final_sum(
|
||||
const float* __restrict__ input,
|
||||
float* __restrict__ output,
|
||||
int n
|
||||
) {
|
||||
float sum = 0;
|
||||
|
||||
// Single block reduces all partial results
|
||||
for (int i = threadIdx.x; i < n; i += blockDim.x) {
|
||||
sum += input[i];
|
||||
}
|
||||
|
||||
sum = block_reduce_sum(sum);
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
output[0] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void reduce_final_max(
|
||||
const float* __restrict__ input,
|
||||
float* __restrict__ output,
|
||||
int n
|
||||
) {
|
||||
float max_val = -FLT_MAX;
|
||||
|
||||
for (int i = threadIdx.x; i < n; i += blockDim.x) {
|
||||
max_val = fmaxf(max_val, input[i]);
|
||||
}
|
||||
|
||||
max_val = block_reduce_max(max_val);
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
output[0] = max_val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute residual and find max for Poisson solver
|
||||
* Combines residual computation with reduction for efficiency
|
||||
*/
|
||||
extern "C" __global__ void poisson_residual_max(
|
||||
const float* __restrict__ phi,
|
||||
const float* __restrict__ source,
|
||||
float* __restrict__ residual_max,
|
||||
float dx2_inv,
|
||||
float dy2_inv,
|
||||
int nx,
|
||||
int ny
|
||||
) {
|
||||
float max_residual = 0;
|
||||
|
||||
// Grid-stride loop over interior points
|
||||
for (int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
idx < nx * ny;
|
||||
idx += blockDim.x * gridDim.x) {
|
||||
|
||||
int i = idx % nx;
|
||||
int j = idx / nx;
|
||||
|
||||
// Skip boundary points
|
||||
if (i > 0 && i < nx-1 && j > 0 && j < ny-1) {
|
||||
// Compute Laplacian
|
||||
float laplacian =
|
||||
dx2_inv * (phi[idx-1] - 2.0f*phi[idx] + phi[idx+1]) +
|
||||
dy2_inv * (phi[idx-nx] - 2.0f*phi[idx] + phi[idx+nx]);
|
||||
|
||||
// Residual = source - laplacian
|
||||
float res = fabsf(source[idx] - laplacian);
|
||||
max_residual = fmaxf(max_residual, res);
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce within block
|
||||
max_residual = block_reduce_max(max_residual);
|
||||
|
||||
// Write block result
|
||||
if (threadIdx.x == 0) {
|
||||
residual_max[blockIdx.x] = max_residual;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user