524 lines
17 KiB
Rust
524 lines
17 KiB
Rust
//! Performance benchmarks for RTX CFD solvers
|
|
//!
|
|
//! Benchmarks key computational kernels and full solver performance
|
|
//! across different problem sizes and configurations.
|
|
|
|
use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
|
|
use nalgebra::DVector;
|
|
use rtx_cfd::{
|
|
discretization::{
|
|
DifferencingScheme, FiniteDifferenceMethod, FiniteVolumeMethod, FluxScheme, GridSpacing,
|
|
SpatialOrder,
|
|
},
|
|
solvers::incompressible::{FlowField, SimpleAlgorithm},
|
|
turbulence::{KEpsilonModel, KEpsilonVariant, SmagorinskyModel, TurbulenceState},
|
|
};
|
|
|
|
/// Benchmark finite volume method discretization
|
|
fn bench_fvm_discretization(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("FVM Discretization");
|
|
|
|
for size in [100, 500, 1000, 2000].iter() {
|
|
group.benchmark_with_input(BenchmarkId::new("scalar", size), size, |b, &size| {
|
|
let mut fvm = FiniteVolumeMethod::new(SpatialOrder::Second, FluxScheme::Central)
|
|
.with_diffusion_coefficient(1e-3);
|
|
|
|
// Create 1D mesh
|
|
for i in 0..size {
|
|
fvm.add_cell(1.0, [i as f64, 0.0, 0.0]);
|
|
}
|
|
|
|
for i in 0..size - 1 {
|
|
fvm.add_face([1.0, 0.0, 0.0], 1.0, i, Some(i + 1)).unwrap();
|
|
}
|
|
|
|
let phi = DVector::zeros(size);
|
|
let velocity = DVector::zeros(size - 1);
|
|
|
|
b.iter(|| {
|
|
let matrix = fvm.discretize_scalar(black_box(&phi), black_box(&velocity));
|
|
black_box(matrix)
|
|
});
|
|
});
|
|
|
|
group.benchmark_with_input(BenchmarkId::new("momentum", size), size, |b, &size| {
|
|
let mut fvm = FiniteVolumeMethod::new(SpatialOrder::Second, FluxScheme::Upwind)
|
|
.with_diffusion_coefficient(1e-3);
|
|
|
|
for i in 0..size {
|
|
fvm.add_cell(1.0, [i as f64, 0.0, 0.0]);
|
|
}
|
|
|
|
for i in 0..size - 1 {
|
|
fvm.add_face([1.0, 0.0, 0.0], 1.0, i, Some(i + 1)).unwrap();
|
|
}
|
|
|
|
let velocity = DVector::ones(size);
|
|
let pressure = DVector::zeros(size);
|
|
|
|
b.iter(|| {
|
|
let matrix = fvm.discretize_momentum(black_box(&velocity), black_box(&pressure));
|
|
black_box(matrix)
|
|
});
|
|
});
|
|
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("flux_calculation", size),
|
|
size,
|
|
|b, &size| {
|
|
let mut fvm = FiniteVolumeMethod::new(SpatialOrder::Second, FluxScheme::Central);
|
|
|
|
for i in 0..size {
|
|
fvm.add_cell(1.0, [i as f64, 0.0, 0.0]);
|
|
}
|
|
|
|
for i in 0..size - 1 {
|
|
fvm.add_face([1.0, 0.0, 0.0], 1.0, i, Some(i + 1)).unwrap();
|
|
}
|
|
|
|
let phi = DVector::from_fn(size, |i, _| (i as f64).sin());
|
|
let velocity = DVector::ones(size - 1);
|
|
|
|
b.iter(|| {
|
|
let fluxes = fvm.calculate_fluxes(black_box(&phi), black_box(&velocity));
|
|
black_box(fluxes)
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark finite difference method
|
|
fn bench_fdm_discretization(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("FDM Discretization");
|
|
|
|
for grid_size in [32, 64, 128, 256].iter() {
|
|
let total_size = grid_size * grid_size;
|
|
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("laplacian_2d", grid_size),
|
|
&total_size,
|
|
|b, &size| {
|
|
let spacing = GridSpacing::uniform(1.0 / *grid_size as f64);
|
|
let fdm = FiniteDifferenceMethod::new(
|
|
SpatialOrder::Second,
|
|
DifferencingScheme::Central,
|
|
spacing,
|
|
[*grid_size, *grid_size, 1],
|
|
)
|
|
.unwrap();
|
|
|
|
b.iter(|| {
|
|
let matrix = fdm.build_laplacian_2d();
|
|
black_box(matrix)
|
|
});
|
|
},
|
|
);
|
|
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("derivative_1d", grid_size),
|
|
grid_size,
|
|
|b, &size| {
|
|
let spacing = GridSpacing::uniform(1.0 / size as f64);
|
|
let fdm = FiniteDifferenceMethod::new(
|
|
SpatialOrder::Second,
|
|
DifferencingScheme::Central,
|
|
spacing,
|
|
[size, 1, 1],
|
|
)
|
|
.unwrap();
|
|
|
|
b.iter(|| {
|
|
let matrix = fdm.build_derivative_matrix_1d(size, 1.0 / size as f64, 1);
|
|
black_box(matrix)
|
|
});
|
|
},
|
|
);
|
|
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("scalar_discretization", grid_size),
|
|
&total_size,
|
|
|b, &size| {
|
|
let spacing = GridSpacing::uniform(1.0 / *grid_size as f64);
|
|
let fdm = FiniteDifferenceMethod::new(
|
|
SpatialOrder::Second,
|
|
DifferencingScheme::Central,
|
|
spacing,
|
|
[*grid_size, *grid_size, 1],
|
|
)
|
|
.unwrap();
|
|
|
|
let phi = DVector::zeros(size);
|
|
let velocity = DVector::zeros(size);
|
|
|
|
b.iter(|| {
|
|
let matrix = fdm.discretize_scalar(black_box(&phi), black_box(&velocity));
|
|
black_box(matrix)
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark turbulence models
|
|
fn bench_turbulence_models(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("Turbulence Models");
|
|
|
|
for size in [500, 1000, 2000, 5000].iter() {
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("k_epsilon_standard", size),
|
|
size,
|
|
|b, &size| {
|
|
let mut model = KEpsilonModel::new(KEpsilonVariant::Standard, size);
|
|
let mut state = TurbulenceState::new(size);
|
|
state.initialize_k_epsilon(1e-6, 1e-8);
|
|
|
|
model.initialize_from_state(&state).unwrap();
|
|
|
|
// Add some velocity gradients for realistic computation
|
|
for i in 0..size.min(state.velocity_gradients.len()) {
|
|
state.velocity_gradients[i][0][1] = 1.0; // du/dy
|
|
}
|
|
|
|
b.iter(|| {
|
|
model.update(black_box(&state), black_box(1e-3)).unwrap();
|
|
});
|
|
},
|
|
);
|
|
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("k_epsilon_realizable", size),
|
|
size,
|
|
|b, &size| {
|
|
let mut model = KEpsilonModel::new(KEpsilonVariant::Realizable, size);
|
|
let mut state = TurbulenceState::new(size);
|
|
state.initialize_k_epsilon(1e-6, 1e-8);
|
|
|
|
model.initialize_from_state(&state).unwrap();
|
|
|
|
for i in 0..size.min(state.velocity_gradients.len()) {
|
|
state.velocity_gradients[i][0][1] = 1.0;
|
|
}
|
|
|
|
b.iter(|| {
|
|
model.update(black_box(&state), black_box(1e-3)).unwrap();
|
|
});
|
|
},
|
|
);
|
|
|
|
group.benchmark_with_input(BenchmarkId::new("smagorinsky", size), size, |b, &size| {
|
|
let mut model = SmagorinskyModel::new(size);
|
|
let filter_width = DVector::from_element(size, 0.1);
|
|
model.set_filter_width(filter_width).unwrap();
|
|
|
|
let mut state = TurbulenceState::new(size);
|
|
for i in 0..size.min(state.velocity_gradients.len()) {
|
|
state.velocity_gradients[i][0][1] = 1.0;
|
|
}
|
|
|
|
b.iter(|| {
|
|
model.update(black_box(&state), black_box(1e-3)).unwrap();
|
|
});
|
|
});
|
|
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("production_terms", size),
|
|
size,
|
|
|b, &size| {
|
|
let model = KEpsilonModel::new(KEpsilonVariant::Standard, size);
|
|
let mut state = TurbulenceState::new(size);
|
|
state.initialize_k_epsilon(1e-6, 1e-8);
|
|
|
|
for i in 0..size.min(state.velocity_gradients.len()) {
|
|
state.velocity_gradients[i][0][1] = 1.0;
|
|
}
|
|
|
|
b.iter(|| {
|
|
let production = model.production_terms(black_box(&state));
|
|
black_box(production)
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark flow field operations
|
|
fn bench_flow_field_operations(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("Flow Field Operations");
|
|
|
|
for size in [1000, 5000, 10000, 20000].iter() {
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("velocity_operations", size),
|
|
size,
|
|
|b, &size| {
|
|
let mut flow_field = FlowField::new(size);
|
|
|
|
// Initialize with some values
|
|
for i in 0..size {
|
|
flow_field
|
|
.set_velocity(i, [i as f64, (i * 2) as f64, 0.0])
|
|
.unwrap();
|
|
}
|
|
|
|
b.iter(|| {
|
|
for i in 0..size {
|
|
let vel = flow_field.get_velocity(black_box(i)).unwrap();
|
|
black_box(vel);
|
|
}
|
|
});
|
|
},
|
|
);
|
|
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("pressure_operations", size),
|
|
size,
|
|
|b, &size| {
|
|
let mut flow_field = FlowField::new(size);
|
|
|
|
for i in 0..size {
|
|
flow_field.set_pressure(i, i as f64 * 0.1).unwrap();
|
|
}
|
|
|
|
b.iter(|| {
|
|
for i in 0..size {
|
|
let pressure = flow_field.get_pressure(black_box(i)).unwrap();
|
|
black_box(pressure);
|
|
}
|
|
});
|
|
},
|
|
);
|
|
|
|
group.benchmark_with_input(BenchmarkId::new("statistics", size), size, |b, &size| {
|
|
let mut flow_field = FlowField::new(size);
|
|
|
|
// Initialize with realistic velocity field
|
|
for i in 0..size {
|
|
let u = (i as f64 / size as f64).sin();
|
|
let v = (i as f64 / size as f64 * 2.0).cos();
|
|
flow_field.set_velocity(i, [u, v, 0.0]).unwrap();
|
|
}
|
|
|
|
b.iter(|| {
|
|
let max_vel = flow_field.calculate_max_velocity_magnitude();
|
|
let ke = flow_field.calculate_kinetic_energy();
|
|
black_box((max_vel, ke));
|
|
});
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark SIMPLE algorithm
|
|
fn bench_simple_algorithm(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("SIMPLE Algorithm");
|
|
|
|
for grid_size in [32, 64, 128].iter() {
|
|
let n_cells = grid_size * grid_size;
|
|
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("solve_step", grid_size),
|
|
&n_cells,
|
|
|b, &size| {
|
|
let mut flow_field = FlowField::new(size);
|
|
|
|
// Initialize with some non-trivial field
|
|
for i in 0..size {
|
|
let u = 1.0 + 0.1 * (i as f64).sin();
|
|
let v = 0.1 * (i as f64 * 2.0).cos();
|
|
flow_field.set_velocity(i, [u, v, 0.0]).unwrap();
|
|
flow_field.set_pressure(i, 0.0).unwrap();
|
|
}
|
|
|
|
let discretization =
|
|
FiniteVolumeMethod::new(SpatialOrder::Second, FluxScheme::Upwind)
|
|
.with_diffusion_coefficient(1e-3);
|
|
|
|
let mut solver = SimpleAlgorithm::new()
|
|
.with_max_iterations(10) // Limit iterations for benchmarking
|
|
.with_tolerance(1e-6)
|
|
.with_under_relaxation(0.7, 0.3);
|
|
|
|
b.iter(|| {
|
|
let residual =
|
|
solver.solve_step(black_box(&mut flow_field), black_box(&discretization));
|
|
black_box(residual)
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark different flux schemes
|
|
fn bench_flux_schemes(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("Flux Schemes");
|
|
|
|
let size = 1000;
|
|
let schemes = [
|
|
("central", FluxScheme::Central),
|
|
("upwind", FluxScheme::Upwind),
|
|
("quick", FluxScheme::Quick),
|
|
("power_law", FluxScheme::PowerLaw),
|
|
];
|
|
|
|
for (name, scheme) in schemes.iter() {
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("flux_calculation", name),
|
|
scheme,
|
|
|b, &scheme| {
|
|
let mut fvm = FiniteVolumeMethod::new(SpatialOrder::Second, scheme)
|
|
.with_diffusion_coefficient(1e-3);
|
|
|
|
for i in 0..size {
|
|
fvm.add_cell(1.0, [i as f64, 0.0, 0.0]);
|
|
}
|
|
|
|
for i in 0..size - 1 {
|
|
fvm.add_face([1.0, 0.0, 0.0], 1.0, i, Some(i + 1)).unwrap();
|
|
}
|
|
|
|
let phi = DVector::from_fn(size, |i, _| (i as f64 * 0.1).sin());
|
|
let velocity =
|
|
DVector::from_fn(size - 1, |i, _| 1.0 + 0.5 * (i as f64 * 0.2).cos());
|
|
|
|
b.iter(|| {
|
|
let fluxes = fvm.calculate_fluxes(black_box(&phi), black_box(&velocity));
|
|
black_box(fluxes)
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark memory allocation patterns
|
|
fn bench_memory_allocation(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("Memory Allocation");
|
|
|
|
for size in [1000, 5000, 10000].iter() {
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("flow_field_creation", size),
|
|
size,
|
|
|b, &size| {
|
|
b.iter(|| {
|
|
let flow_field = FlowField::new(black_box(size));
|
|
black_box(flow_field);
|
|
});
|
|
},
|
|
);
|
|
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("turbulence_state_creation", size),
|
|
size,
|
|
|b, &size| {
|
|
b.iter(|| {
|
|
let mut state = TurbulenceState::new(black_box(size));
|
|
state.initialize_k_epsilon(1e-6, 1e-8);
|
|
black_box(state);
|
|
});
|
|
},
|
|
);
|
|
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("k_epsilon_creation", size),
|
|
size,
|
|
|b, &size| {
|
|
b.iter(|| {
|
|
let model = KEpsilonModel::new(KEpsilonVariant::Standard, black_box(size));
|
|
black_box(model);
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark scaling with problem size
|
|
fn bench_scaling(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("Scaling");
|
|
|
|
// Test how performance scales with problem size
|
|
let sizes = [100, 200, 500, 1000, 2000, 5000];
|
|
|
|
for size in sizes.iter() {
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("total_fvm_workflow", size),
|
|
size,
|
|
|b, &size| {
|
|
b.iter(|| {
|
|
// Complete FVM workflow
|
|
let mut fvm =
|
|
FiniteVolumeMethod::new(SpatialOrder::Second, FluxScheme::Central);
|
|
|
|
for i in 0..size {
|
|
fvm.add_cell(1.0, [i as f64, 0.0, 0.0]);
|
|
}
|
|
|
|
for i in 0..size - 1 {
|
|
fvm.add_face([1.0, 0.0, 0.0], 1.0, i, Some(i + 1)).unwrap();
|
|
}
|
|
|
|
let phi = DVector::from_fn(size, |i, _| (i as f64 * 0.1).sin());
|
|
let velocity = DVector::ones(size - 1);
|
|
|
|
let matrix = fvm.discretize_scalar(&phi, &velocity).unwrap();
|
|
let fluxes = fvm.calculate_fluxes(&phi, &velocity).unwrap();
|
|
|
|
black_box((matrix, fluxes));
|
|
});
|
|
},
|
|
);
|
|
|
|
group.benchmark_with_input(
|
|
BenchmarkId::new("total_turbulence_workflow", size),
|
|
size,
|
|
|b, &size| {
|
|
b.iter(|| {
|
|
// Complete turbulence modeling workflow
|
|
let mut model = KEpsilonModel::new(KEpsilonVariant::Standard, size);
|
|
let mut state = TurbulenceState::new(size);
|
|
state.initialize_k_epsilon(1e-6, 1e-8);
|
|
|
|
model.initialize_from_state(&state).unwrap();
|
|
|
|
for i in 0..size.min(state.velocity_gradients.len()) {
|
|
state.velocity_gradients[i][0][1] = 1.0;
|
|
}
|
|
|
|
let production = model.production_terms(&state).unwrap();
|
|
model.update(&state, 1e-3).unwrap();
|
|
let nu_t = model.turbulent_viscosity(&state).unwrap();
|
|
|
|
black_box((production, nu_t));
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
criterion_group!(
|
|
benches,
|
|
bench_fvm_discretization,
|
|
bench_fdm_discretization,
|
|
bench_turbulence_models,
|
|
bench_flow_field_operations,
|
|
bench_simple_algorithm,
|
|
bench_flux_schemes,
|
|
bench_memory_allocation,
|
|
bench_scaling
|
|
);
|
|
|
|
criterion_main!(benches);
|