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
189 lines
5.2 KiB
Rust
189 lines
5.2 KiB
Rust
//! Tests for GPU reduction operations
|
|
//!
|
|
//! Following strict TDD methodology - verifying our GPU reduction
|
|
//! implementations work correctly with cudarc 0.17.3
|
|
|
|
#[cfg(feature = "cuda")]
|
|
mod gpu_reduction_tests {
|
|
use rtx_cfd::kernels::CudaKernelManager;
|
|
use rtx_cfd::{CfdConfig, CfdResult};
|
|
use std::sync::Arc;
|
|
|
|
fn create_test_config() -> CfdConfig {
|
|
CfdConfig {
|
|
nx: 64,
|
|
ny: 64,
|
|
nz: 1,
|
|
lx: 1.0,
|
|
ly: 1.0,
|
|
lz: 1.0,
|
|
dt: 0.001,
|
|
viscosity: 0.01,
|
|
density: 1.0,
|
|
device_id: 0,
|
|
..CfdConfig::default()
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_gpu_sum_reduction() -> CfdResult<()> {
|
|
// Skip test if no GPU available
|
|
if std::env::var("SKIP_GPU_TESTS").is_ok() {
|
|
return Ok(());
|
|
}
|
|
|
|
let config = create_test_config();
|
|
let manager = Arc::new(CudaKernelManager::new(&config)?);
|
|
|
|
// Create test data
|
|
let test_data: Vec<f32> = (1..=100).map(|i| i as f32).collect();
|
|
let expected_sum = test_data.iter().sum::<f32>();
|
|
|
|
// Copy to GPU
|
|
let gpu_data = manager.copy_to_device(&test_data)?;
|
|
|
|
// Perform GPU reduction
|
|
let gpu_sum = manager.reduce_sum(&gpu_data)?;
|
|
|
|
// Verify result
|
|
assert!(
|
|
(gpu_sum - expected_sum).abs() < 1e-4,
|
|
"GPU sum {} != expected {}",
|
|
gpu_sum,
|
|
expected_sum
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_gpu_abs_max_reduction() -> CfdResult<()> {
|
|
// Skip test if no GPU available
|
|
if std::env::var("SKIP_GPU_TESTS").is_ok() {
|
|
return Ok(());
|
|
}
|
|
|
|
let config = create_test_config();
|
|
let manager = Arc::new(CudaKernelManager::new(&config)?);
|
|
|
|
// Create test data with known max absolute value
|
|
let mut test_data: Vec<f32> = vec![1.0, -5.0, 3.0, -7.5, 2.0, 6.0];
|
|
let expected_max = 7.5;
|
|
|
|
// Copy to GPU
|
|
let gpu_data = manager.copy_to_device(&test_data)?;
|
|
|
|
// Perform GPU reduction
|
|
let gpu_max = manager.reduce_abs_max(&gpu_data)?;
|
|
|
|
// Verify result
|
|
assert!(
|
|
(gpu_max - expected_max).abs() < 1e-6,
|
|
"GPU max {} != expected {}",
|
|
gpu_max,
|
|
expected_max
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_gpu_reduction_large_array() -> CfdResult<()> {
|
|
// Skip test if no GPU available
|
|
if std::env::var("SKIP_GPU_TESTS").is_ok() {
|
|
return Ok(());
|
|
}
|
|
|
|
let config = create_test_config();
|
|
let manager = Arc::new(CudaKernelManager::new(&config)?);
|
|
|
|
// Create large test array
|
|
let n = 1_000_000;
|
|
let test_data: Vec<f32> = (0..n).map(|i| (i % 100) as f32).collect();
|
|
let expected_sum = test_data.iter().sum::<f32>();
|
|
|
|
// Copy to GPU
|
|
let gpu_data = manager.copy_to_device(&test_data)?;
|
|
|
|
// Perform GPU reduction
|
|
let gpu_sum = manager.reduce_sum(&gpu_data)?;
|
|
|
|
// Verify result (allow for floating point error accumulation)
|
|
let relative_error = (gpu_sum - expected_sum).abs() / expected_sum;
|
|
assert!(
|
|
relative_error < 1e-5,
|
|
"GPU sum {} != expected {}, relative error: {}",
|
|
gpu_sum,
|
|
expected_sum,
|
|
relative_error
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_poisson_solver_with_gpu_reduction() -> CfdResult<()> {
|
|
// Skip test if no GPU available
|
|
if std::env::var("SKIP_GPU_TESTS").is_ok() {
|
|
return Ok(());
|
|
}
|
|
|
|
let config = create_test_config();
|
|
let manager = Arc::new(CudaKernelManager::new(&config)?);
|
|
|
|
// Create Poisson solver that uses GPU reduction
|
|
use rtx_cfd::kernels::PoissonKernel;
|
|
let poisson = PoissonKernel::new(&manager)?;
|
|
|
|
// Create test problem: Laplace equation with boundary conditions
|
|
let nx = 32;
|
|
let ny = 32;
|
|
let n = nx * ny;
|
|
|
|
// Initialize fields
|
|
let mut phi_data = vec![0.0f32; n];
|
|
let source_data = vec![0.0f32; n]; // Laplace equation (no source)
|
|
|
|
// Set boundary conditions (phi = 1 on top, 0 elsewhere)
|
|
for i in 0..nx {
|
|
phi_data[i + (ny - 1) * nx] = 1.0;
|
|
}
|
|
|
|
// Copy to GPU
|
|
let mut phi = manager.copy_to_device(&phi_data)?;
|
|
let source = manager.copy_to_device(&source_data)?;
|
|
|
|
// Solve using GPU with GPU reduction for convergence check
|
|
let iterations = poisson.solve_2d(
|
|
&mut phi,
|
|
&source,
|
|
nx,
|
|
ny,
|
|
1.0 / nx as f32,
|
|
1.0 / ny as f32,
|
|
100, // max iterations
|
|
1e-4, // tolerance
|
|
)?;
|
|
|
|
// Verify convergence
|
|
assert!(
|
|
iterations > 0 && iterations <= 100,
|
|
"Solver took {} iterations",
|
|
iterations
|
|
);
|
|
|
|
// Copy result back
|
|
let result = manager.copy_from_device(&phi)?;
|
|
|
|
// Verify boundary conditions are preserved
|
|
for i in 0..nx {
|
|
assert!(
|
|
(result[i + (ny - 1) * nx] - 1.0).abs() < 1e-3,
|
|
"Top boundary not preserved"
|
|
);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|