rtx-cfd legacy GPU tests: the current CudaKernelManager API (Arc-held manager, allocate-on-copy, copy_from_device returning the host vector), MatrixOpsKernel::tridiagonal_matvec added over the existing kernel, PoissonKernel::solve_jacobi_2d kept as the older name; CfdConfig literals take ..Default
CI / Build (macos-latest) (push) Waiting to run
CI / Test (macos-latest) (push) Blocked by required conditions
CI / Test (ubuntu-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (macos-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (ubuntu-latest) (push) Blocked by required conditions
CI / WASM Build + Size Check (push) Blocked by required conditions
CI / Distributed Training Tests (push) Blocked by required conditions
CI / CI Success (push) Blocked by required conditions
CI / Build CPU-Only (Explicit) (push) Failing after 6s
CI / Build (ubuntu-latest) (push) Failing after 6s
Documentation / Build API Documentation (push) Failing after 6s
Documentation / Build User Guide (push) Successful in 7s
CI / Format Check (push) Failing after 13s
CI / Clippy Check (push) Failing after 37s
Performance Benchmarks / Run Benchmarks (push) Successful in 3m23s

This commit is contained in:
Omar Sobh
2026-09-16 07:18:34 -05:00
parent af19860c04
commit 1381b5b633
6 changed files with 109 additions and 48 deletions
@@ -601,6 +601,23 @@ impl PoissonKernel {
})
}
/// [`Self::solve_2d`] under its older name.
#[allow(clippy::too_many_arguments)]
pub fn solve_jacobi_2d(
&self,
phi: &mut CudaSlice<f32>,
source: &CudaSlice<f32>,
nx: usize,
ny: usize,
dx: f32,
dy: f32,
max_iterations: usize,
tolerance: f32,
) -> CfdResult<usize> {
self.solve_2d(phi, source, nx, ny, dx, dy, max_iterations, tolerance)
}
#[allow(clippy::too_many_arguments)]
pub fn solve_2d(
&self,
phi: &mut CudaSlice<f32>,
@@ -757,6 +774,39 @@ impl MatrixOpsKernel {
Ok(result)
}
/// `y = T x` for the tridiagonal matrix with `diagonal` (n) and
/// `off_diagonal` (n 1) — the `tridiagonal_matvec` kernel.
pub fn tridiagonal_matvec(
&self,
diagonal: &CudaSlice<f32>,
off_diagonal: &CudaSlice<f32>,
x: &CudaSlice<f32>,
y: &mut CudaSlice<f32>,
) -> CfdResult<()> {
let n = x.len();
let func = self
.module
.load_function("tridiagonal_matvec")
.map_err(|e| CfdError::gpu_error(&format!("Failed to get kernel: {}", e)))?;
let config = LaunchConfig {
grid_dim: ((n as u32 + 255) / 256, 1, 1),
block_dim: (256, 1, 1),
shared_mem_bytes: 0,
};
unsafe {
self.stream
.launch_builder(&func)
.arg(diagonal)
.arg(off_diagonal)
.arg(x)
.arg(y)
.arg(&(n as i32))
.launch(config)
.map_err(|e| CfdError::gpu_error(&format!("Kernel launch failed: {}", e)))?;
}
Ok(())
}
/// Compute L2 norm of vector
pub fn vector_norm(&self, x: &CudaSlice<f32>) -> CfdResult<f32> {
let n = x.len();
@@ -20,6 +20,7 @@ mod cuda_kernel_manager_tests {
viscosity: 0.01,
density: 1.0,
device_id: 0,
..CfdConfig::default()
}
}
@@ -31,7 +32,7 @@ mod cuda_kernel_manager_tests {
}
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
// Verify manager is created successfully
// Context and stream should be valid (non-null references)
@@ -49,7 +50,7 @@ mod cuda_kernel_manager_tests {
}
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
// Test that required modules are loaded
// Getting a module should succeed and return a valid Arc reference
@@ -72,7 +73,7 @@ mod cuda_kernel_manager_tests {
}
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
// Test that non-existent module returns error
let result = manager.get_module("nonexistent_module");
@@ -92,7 +93,7 @@ mod cuda_kernel_manager_tests {
}
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
// Test allocating GPU memory
let size = 1024;
@@ -110,7 +111,7 @@ mod cuda_kernel_manager_tests {
}
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
// Test copying data to GPU
let host_data: Vec<f32> = (0..100).map(|i| i as f32).collect();
@@ -128,7 +129,7 @@ mod cuda_kernel_manager_tests {
}
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
// Test round-trip copy: host -> device -> host
let original_data: Vec<f32> = (0..100).map(|i| i as f32 * 0.1).collect();
@@ -159,7 +160,7 @@ mod cuda_kernel_manager_tests {
}
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
// Test that synchronization doesn't error
manager.synchronize()?;
@@ -37,9 +37,10 @@ mod gpu_kernel_tests {
lz: 0.0,
dt: 0.001,
..Default::default()
..CfdConfig::default()
};
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
// Test all advection schemes
let schemes = vec![
@@ -117,9 +118,10 @@ mod gpu_kernel_tests {
lz: 0.0,
dt: 0.001,
..Default::default()
..CfdConfig::default()
};
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let kernel = AdvectionKernel::new(&manager, AdvectionScheme::Upwind)?;
let nx = 64;
@@ -202,9 +204,10 @@ mod gpu_kernel_tests {
lz: 0.0,
dt: 0.0001,
..Default::default()
..CfdConfig::default()
};
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
// Test all diffusion schemes
let schemes = vec![
@@ -288,9 +291,10 @@ mod gpu_kernel_tests {
lz: 0.0,
dt: 0.0001,
..Default::default()
..CfdConfig::default()
};
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let kernel = DiffusionKernel::new(&manager, DiffusionScheme::Explicit)?;
let nx = 64;
@@ -366,9 +370,10 @@ mod gpu_kernel_tests {
lz: 0.0,
dt: 0.001,
..Default::default()
..CfdConfig::default()
};
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let kernel = PoissonKernel::new(&manager)?;
let nx = 32;
@@ -443,9 +448,10 @@ mod gpu_kernel_tests {
lz: 0.0,
dt: 0.001,
..Default::default()
..CfdConfig::default()
};
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
// Test allocation and deallocation of large arrays
let sizes = vec![1024, 65536, 262144];
@@ -491,9 +497,10 @@ mod gpu_kernel_tests {
lz: 0.0,
dt: 0.001,
..Default::default()
..CfdConfig::default()
};
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let kernel = AdvectionKernel::new(&manager, AdvectionScheme::Upwind)?;
let n = 512 * 512;
@@ -25,20 +25,21 @@ mod cuda_tests {
viscosity: 0.01,
density: 1.0,
device_id: 0,
..CfdConfig::default()
}
}
#[test]
fn test_cuda_kernel_manager_creation() -> CfdResult<()> {
let config = create_test_config();
let _manager = CudaKernelManager::new(&config)?;
let _manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
Ok(())
}
#[test]
fn test_memory_operations() -> CfdResult<()> {
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
// Test allocation
let size = 1000;
@@ -63,7 +64,7 @@ mod cuda_tests {
#[test]
fn test_advection_kernel_1d() -> CfdResult<()> {
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let kernel = AdvectionKernel::new(&manager, AdvectionScheme::Upwind)?;
let n = 100;
@@ -99,7 +100,7 @@ mod cuda_tests {
#[test]
fn test_advection_kernel_2d() -> CfdResult<()> {
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let kernel = AdvectionKernel::new(&manager, AdvectionScheme::Upwind)?;
let nx = 32;
@@ -147,7 +148,7 @@ mod cuda_tests {
#[test]
fn test_diffusion_kernel_explicit() -> CfdResult<()> {
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let kernel = DiffusionKernel::new(&manager, DiffusionScheme::Explicit)?;
let n = 100;
@@ -182,7 +183,7 @@ mod cuda_tests {
#[test]
fn test_diffusion_kernel_2d() -> CfdResult<()> {
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let kernel = DiffusionKernel::new(&manager, DiffusionScheme::Explicit)?;
let nx = 32;
@@ -225,7 +226,7 @@ mod cuda_tests {
#[test]
fn test_poisson_kernel_jacobi() -> CfdResult<()> {
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let kernel = PoissonKernel::new(&manager)?;
let nx = 32;
@@ -267,7 +268,7 @@ mod cuda_tests {
#[test]
fn test_matrix_ops_tridiagonal() -> CfdResult<()> {
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let kernel = MatrixOpsKernel::new(&manager)?;
let n = 100;
@@ -303,7 +304,7 @@ mod cuda_tests {
#[test]
fn test_matrix_ops_dot_product() -> CfdResult<()> {
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let kernel = MatrixOpsKernel::new(&manager)?;
let n = 1000;
@@ -325,7 +326,7 @@ mod cuda_tests {
#[test]
fn test_matrix_ops_vector_norm() -> CfdResult<()> {
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let kernel = MatrixOpsKernel::new(&manager)?;
let n = 100;
@@ -345,7 +346,7 @@ mod cuda_tests {
#[test]
fn test_matrix_ops_axpy() -> CfdResult<()> {
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let kernel = MatrixOpsKernel::new(&manager)?;
let n = 100;
@@ -372,7 +373,7 @@ mod cuda_tests {
#[test]
fn test_performance_comparison() -> CfdResult<()> {
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let n = 10000;
let host_data: Vec<f32> = (0..n).map(|i| i as f32 * 0.1).collect();
@@ -394,7 +395,7 @@ mod cuda_tests {
#[test]
fn test_kernel_error_handling() -> CfdResult<()> {
let config = create_test_config();
let manager = CudaKernelManager::new(&config)?;
let manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
// Test with mismatched array sizes (should not crash)
let small_array = manager.allocate_f32(10)?;
@@ -434,6 +435,7 @@ mod cpu_fallback_tests {
reference_length: 1.0,
reference_velocity: 1.0,
use_gpu: true,
..CfdConfig::default()
};
let result = CudaKernelManager::new(&config);
@@ -21,6 +21,7 @@ mod gpu_reduction_tests {
viscosity: 0.01,
density: 1.0,
device_id: 0,
..CfdConfig::default()
}
}
@@ -20,7 +20,7 @@ mod cuda_tests {
#[tokio::test]
async fn test_advection_kernel_upwind() -> CfdResult<()> {
let config = CfdConfig::new().with_gpu(true);
let kernel_manager = CudaKernelManager::new(&config)?;
let kernel_manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let advection_kernel = AdvectionKernel::new(&kernel_manager, AdvectionScheme::Upwind)?;
// Test data: 1D advection with known analytical solution
@@ -43,14 +43,14 @@ mod cuda_tests {
let d_phi_new = kernel_manager.allocate_f32(nx)?;
// Copy to GPU
kernel_manager.copy_to_device(&phi, &mut d_phi)?;
d_phi = kernel_manager.copy_to_device(&phi)?;
// Run advection kernel
advection_kernel.apply(&d_phi, &d_phi_new, velocity as f32, dt as f32, dx as f32)?;
// Copy result back
let mut result = vec![0.0f32; nx];
kernel_manager.copy_from_device(&d_phi_new, &mut result)?;
result = kernel_manager.copy_from_device(&d_phi_new)?;
// Verify that the pulse has moved (mass conservation)
let initial_mass: f32 = phi.iter().sum();
@@ -82,7 +82,7 @@ mod cuda_tests {
#[tokio::test]
async fn test_advection_kernel_central() -> CfdResult<()> {
let config = CfdConfig::new().with_gpu(true);
let kernel_manager = CudaKernelManager::new(&config)?;
let kernel_manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let advection_kernel = AdvectionKernel::new(&kernel_manager, AdvectionScheme::Central)?;
// Test data: smooth sinusoidal wave
@@ -100,11 +100,11 @@ mod cuda_tests {
let mut d_phi = kernel_manager.allocate_f32(nx)?;
let d_phi_new = kernel_manager.allocate_f32(nx)?;
kernel_manager.copy_to_device(&phi, &mut d_phi)?;
d_phi = kernel_manager.copy_to_device(&phi)?;
advection_kernel.apply(&d_phi, &d_phi_new, velocity as f32, dt as f32, dx as f32)?;
let mut result = vec![0.0f32; nx];
kernel_manager.copy_from_device(&d_phi_new, &mut result)?;
result = kernel_manager.copy_from_device(&d_phi_new)?;
// For central scheme, verify mass conservation and smoothness
let initial_mass: f32 = phi.iter().sum();
@@ -117,7 +117,7 @@ mod cuda_tests {
#[tokio::test]
async fn test_diffusion_kernel_explicit() -> CfdResult<()> {
let config = CfdConfig::new().with_gpu(true);
let kernel_manager = CudaKernelManager::new(&config)?;
let kernel_manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let diffusion_kernel = DiffusionKernel::new(&kernel_manager, DiffusionScheme::Explicit)?;
// Test 1D heat equation with analytical solution
@@ -135,11 +135,11 @@ mod cuda_tests {
let mut d_temp = kernel_manager.allocate_f32(nx)?;
let d_temp_new = kernel_manager.allocate_f32(nx)?;
kernel_manager.copy_to_device(&temperature, &mut d_temp)?;
d_temp = kernel_manager.copy_to_device(&temperature)?;
diffusion_kernel.apply(&d_temp, &d_temp_new, alpha as f32, dt as f32, dx as f32)?;
let mut result = vec![0.0f32; nx];
kernel_manager.copy_from_device(&d_temp_new, &mut result)?;
result = kernel_manager.copy_from_device(&d_temp_new)?;
// Verify diffusion: edges should be smoother, total heat conserved
let initial_total: f32 = temperature.iter().sum();
@@ -160,7 +160,7 @@ mod cuda_tests {
#[tokio::test]
async fn test_poisson_kernel_2d() -> CfdResult<()> {
let config = CfdConfig::new().with_gpu(true);
let kernel_manager = CudaKernelManager::new(&config)?;
let kernel_manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let poisson_kernel = PoissonKernel::new(&kernel_manager)?;
// Test 2D Poisson equation: ∇²φ = f
@@ -190,8 +190,8 @@ mod cuda_tests {
let mut d_phi = kernel_manager.allocate_f32(nx * ny)?;
let mut d_source = kernel_manager.allocate_f32(nx * ny)?;
kernel_manager.copy_to_device(&phi, &mut d_phi)?;
kernel_manager.copy_to_device(&source, &mut d_source)?;
d_phi = kernel_manager.copy_to_device(&phi)?;
d_source = kernel_manager.copy_to_device(&source)?;
// Solve Poisson equation
let max_iterations = 1000;
@@ -208,7 +208,7 @@ mod cuda_tests {
)?;
let mut result = vec![0.0f32; nx * ny];
kernel_manager.copy_from_device(&d_phi, &mut result)?;
result = kernel_manager.copy_from_device(&d_phi)?;
// Verify against analytical solution
let mut max_error = 0.0f32;
@@ -233,7 +233,7 @@ mod cuda_tests {
#[tokio::test]
async fn test_matrix_ops_kernel() -> CfdResult<()> {
let config = CfdConfig::new().with_gpu(true);
let kernel_manager = CudaKernelManager::new(&config)?;
let kernel_manager = std::sync::Arc::new(CudaKernelManager::new(&config)?);
let matrix_ops = MatrixOpsKernel::new(&kernel_manager)?;
// Test sparse matrix-vector multiplication (typical in CFD)
@@ -246,17 +246,17 @@ mod cuda_tests {
let mut d_diag = kernel_manager.allocate_f32(n)?;
let mut d_off_diag = kernel_manager.allocate_f32(n - 1)?;
let mut d_x = kernel_manager.allocate_f32(n)?;
let d_y = kernel_manager.allocate_f32(n)?;
let mut d_y = kernel_manager.allocate_f32(n)?;
kernel_manager.copy_to_device(&diagonal, &mut d_diag)?;
kernel_manager.copy_to_device(&off_diagonal, &mut d_off_diag)?;
kernel_manager.copy_to_device(&x, &mut d_x)?;
d_diag = kernel_manager.copy_to_device(&diagonal)?;
d_off_diag = kernel_manager.copy_to_device(&off_diagonal)?;
d_x = kernel_manager.copy_to_device(&x)?;
// Perform A*x = y operation
matrix_ops.tridiagonal_matvec(&d_diag, &d_off_diag, &d_x, &d_y, n)?;
matrix_ops.tridiagonal_matvec(&d_diag, &d_off_diag, &d_x, &mut d_y)?;
let mut result = vec![0.0f32; n];
kernel_manager.copy_from_device(&d_y, &mut result)?;
result = kernel_manager.copy_from_device(&d_y)?;
// Verify result manually for first few elements
assert_relative_eq!(result[0], 2.0 * x[0] - x[1], epsilon = 1e-6);
@@ -287,7 +287,7 @@ async fn test_kernel_manager_initialization() -> CfdResult<()> {
#[cfg(feature = "cuda")]
{
// Should successfully create kernel manager
let result = rtx_cfd::kernels::CudaKernelManager::new(&config);
let mut result = rtx_cfd::kernels::CudaKernelManager::new(&config);
// May fail if no CUDA device available - that's expected in CI
match result {
Ok(_) => println!("CUDA kernel manager created successfully"),