Initial commit

This commit is contained in:
redclawsystems
2026-03-04 00:08:42 +00:00
commit 4d88dc0584
4449 changed files with 1556714 additions and 0 deletions
@@ -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;
}
}