/** * 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; }