276 lines
8.5 KiB
Rust
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
|
|
}
|