Files
rustytorch/crates/specialized/rtx-cfd/examples/gpu_simple_example.rs
T
2026-03-04 00:08:42 +00:00

276 lines
8.5 KiB
Rust

//! GPU-accelerated SIMPLE solver example
//!
//! This example demonstrates how to use the GPU-accelerated SIMPLE solver
//! for solving the lid-driven cavity problem, a classic CFD benchmark.
use rtx_cfd::solvers::incompressible::*;
use rtx_cfd::*;
#[tokio::main]
async fn main() -> CfdResult<()> {
println!("GPU-accelerated CFD Example");
println!("===========================");
// Create configuration
let config = CfdConfig::new()
.with_density(1.0)
.with_viscosity(0.01)
.with_reference_velocity(1.0)
.with_reference_length(1.0)
.with_device_id(0);
println!("Configuration:");
println!(" Grid: {}x{}", config.nx, config.ny);
println!(" Domain: {:.1}x{:.1}", config.lx, config.ly);
println!(
" Reynolds number: {:.0}",
estimate_reynolds_number(&config)
);
// Create SIMPLE solver parameters
let params = SimpleParameters::new()
.with_pressure_relaxation(0.3) // Under-relaxation for pressure
.with_velocity_relaxation(0.7) // Under-relaxation for velocity
.with_max_iterations(1000) // Maximum iterations
.with_tolerance(1e-6) // Convergence tolerance
.with_time_step(config.dt);
// Try to create GPU solver, fall back to CPU if GPU not available
match create_solver(config.clone(), params).await {
Ok(mut solver) => {
println!("Successfully created GPU solver");
run_lid_driven_cavity_simulation(solver, config).await?;
}
Err(e) => {
println!("GPU solver creation failed: {}", e);
println!("GPU features may not be available on this system");
run_cpu_fallback_demo(config).await?;
}
}
Ok(())
}
/// Create GPU solver or return error
#[cfg(feature = "cuda")]
async fn create_solver(
config: CfdConfig,
params: SimpleParameters,
) -> CfdResult<rtx_cfd::solvers::incompressible::SimpleGpuSolver> {
use rtx_cfd::solvers::incompressible::SimpleGpuSolver;
SimpleGpuSolver::new(config, params)
}
#[cfg(not(feature = "cuda"))]
async fn create_solver(
_config: CfdConfig,
_params: SimpleParameters,
) -> CfdResult<rtx_cfd::solvers::incompressible::SimpleSolver> {
Err(CfdError::not_implemented(
"CUDA not available - compile with --features cuda",
))
}
/// Run lid-driven cavity simulation with GPU solver
#[cfg(feature = "cuda")]
async fn run_lid_driven_cavity_simulation(
mut solver: rtx_cfd::solvers::incompressible::SimpleGpuSolver,
config: CfdConfig,
) -> CfdResult<()> {
println!("\nRunning lid-driven cavity simulation...");
// Create flow field
let dx = config.lx / (config.nx - 1) as f64;
let dy = config.ly / (config.ny - 1) as f64;
let mut flow_field = FlowField::new(config.nx, config.ny, dx, dy)?;
// Set up lid-driven cavity initial conditions
setup_lid_driven_cavity(&mut flow_field, 1.0)?;
// Create boundary conditions (simplified)
let boundary_conditions = BoundaryConditions::new();
println!("Initial conditions set up");
println!("Solving with GPU SIMPLE algorithm...");
// Solve
let start_time = std::time::Instant::now();
let result = solver.solve(&mut flow_field, &boundary_conditions).await?;
let total_time = start_time.elapsed();
// Display results
println!("\nSolution completed!");
println!(" Converged: {}", result.solver_result.converged);
println!(" Iterations: {}", result.solver_result.iterations);
println!(
" Final residual: {:.2e}",
result.solver_result.final_residual
);
println!(" Solve time: {:?}", result.solver_result.solve_time);
println!(" Total time: {:?}", total_time);
// Analyze results
analyze_flow_field(&flow_field)?;
// Optional: Save results
save_results(&flow_field, "gpu_lid_cavity_results.dat")?;
println!("\nResults saved to gpu_lid_cavity_results.dat");
Ok(())
}
#[cfg(not(feature = "cuda"))]
async fn run_lid_driven_cavity_simulation(
_solver: rtx_cfd::solvers::incompressible::SimpleSolver,
_config: CfdConfig,
) -> CfdResult<()> {
unreachable!("This function should not be called without CUDA")
}
/// Run CPU fallback demonstration
async fn run_cpu_fallback_demo(config: CfdConfig) -> CfdResult<()> {
println!("\nRunning CPU fallback demonstration...");
let params = SimpleParameters::new()
.with_max_iterations(100) // Fewer iterations for demo
.with_tolerance(1e-4);
let mut cpu_solver = SimpleSolver::new(config.clone(), params)?;
let dx = config.lx / (config.nx - 1) as f64;
let dy = config.ly / (config.ny - 1) as f64;
let mut flow_field = FlowField::new(config.nx, config.ny, dx, dy)?;
setup_lid_driven_cavity(&mut flow_field, 1.0)?;
let boundary_conditions = BoundaryConditions::new();
println!("Solving with CPU SIMPLE algorithm (limited iterations)...");
let start_time = std::time::Instant::now();
let result = cpu_solver
.solve(&mut flow_field, &boundary_conditions)
.await?;
let total_time = start_time.elapsed();
println!("\nCPU solution completed!");
println!(" Converged: {}", result.solver_result.converged);
println!(" Iterations: {}", result.solver_result.iterations);
println!(
" Final residual: {:.2e}",
result.solver_result.final_residual
);
println!(" Solve time: {:?}", result.solver_result.solve_time);
println!(" Total time: {:?}", total_time);
Ok(())
}
/// Set up lid-driven cavity initial and boundary conditions
fn setup_lid_driven_cavity(flow_field: &mut FlowField, lid_velocity: f64) -> CfdResult<()> {
let nx = flow_field.nx;
let ny = flow_field.ny;
// Initialize all velocities to zero except top wall
for j in 0..ny {
for i in 0..nx {
let u = if j == ny - 1 { lid_velocity } else { 0.0 };
flow_field.set_velocity(i, j, u, 0.0)?;
flow_field.set_pressure(i, j, 0.0)?;
}
}
Ok(())
}
/// Analyze flow field results
fn analyze_flow_field(flow_field: &FlowField) -> CfdResult<()> {
let nx = flow_field.nx;
let ny = flow_field.ny;
let mut max_u = 0.0_f64;
let mut max_v = 0.0_f64;
let mut max_p = 0.0_f64;
let mut min_p = 0.0_f64;
// Find extrema
for j in 0..ny {
for i in 0..nx {
let (u, v) = flow_field.get_velocity_at(i, j)?;
let p = flow_field.get_pressure_at(i, j)?;
max_u = max_u.max(u.abs());
max_v = max_v.max(v.abs());
max_p = max_p.max(p);
min_p = min_p.min(p);
}
}
println!("\nFlow field analysis:");
println!(" Max |u|: {:.4}", max_u);
println!(" Max |v|: {:.4}", max_v);
println!(" Pressure range: [{:.4}, {:.4}]", min_p, max_p);
// Check for reasonable values
if max_u > 10.0 || max_v > 10.0 {
println!(" Warning: Very large velocities detected");
}
if max_u < 0.01 {
println!(" Warning: Very small velocities - solution may not be converged");
}
// Compute velocity at geometric center
let center_i = nx / 2;
let center_j = ny / 2;
let (u_center, v_center) = flow_field.get_velocity_at(center_i, center_j)?;
println!(" Velocity at center: u={:.4}, v={:.4}", u_center, v_center);
Ok(())
}
/// Save results to file
fn save_results(flow_field: &FlowField, filename: &str) -> CfdResult<()> {
use std::fs::File;
use std::io::Write;
let mut file = File::create(filename)
.map_err(|e| CfdError::invalid_parameter(&format!("Failed to create file: {}", e)))?;
// Write header
writeln!(file, "# Lid-driven cavity results")?;
writeln!(file, "# nx={}, ny={}", flow_field.nx, flow_field.ny)?;
writeln!(file, "# Format: i j x y u v p")?;
// Write data
let nx = flow_field.nx;
let ny = flow_field.ny;
for j in 0..ny {
for i in 0..nx {
let x = i as f64 * flow_field.dx;
let y = j as f64 * flow_field.dy;
let (u, v) = flow_field.get_velocity_at(i, j)?;
let p = flow_field.get_pressure_at(i, j)?;
writeln!(
file,
"{} {} {:.6} {:.6} {:.6} {:.6} {:.6}",
i, j, x, y, u, v, p
)?;
}
}
Ok(())
}
/// Estimate Reynolds number based on configuration
fn estimate_reynolds_number(config: &CfdConfig) -> f64 {
let characteristic_length = config.lx; // Use domain length
let characteristic_velocity = 1.0; // Lid velocity
let kinematic_viscosity = config.viscosity / config.density;
characteristic_velocity * characteristic_length / kinematic_viscosity
}