forked from 1b-agent/fieldcraft-upstream
A new L7 crate: the CPU scheme's collision (TRT with Guo forcing), the per-link streaming rules (bounce-back with Ladd's term, slip reflection, open placeholders), the Zou–He boundaries and the momentum exchange per force group as wgpu compute shaders, stepping the flow fieldcraft_lbm's Flow prepared, through its Backend trait: populations stay on the device, a chunk of steps is one submission, and only frames, forces and the stability check come back. Populations are stored as deviations from their weights, which keeps single precision honest: the verification cases (Poiseuille with Λ = 3/16, Couette with wall forces, Taylor–Green, a sphere's drag, a Zou–He channel) agree with the CPU to 2–5e-6 in velocity, 1e-6 in density and 5e-5 in force, with those tolerances stated in the tests. The rules are applied one thread per link rather than in a per-cell loop, which lavapipe's JIT miscompiles (and which diverges on real GPUs anyway). Native only for now; the browser registers nothing. Part of #529.
57 lines
2.1 KiB
Rust
57 lines
2.1 KiB
Rust
//! Throughput of the GPU backend (ignored; run with
|
|
//! `cargo test --release -p fieldcraft-lbm-gpu --test throughput -- --ignored --nocapture`),
|
|
//! on the cases of `fieldcraft-lbm`'s throughput test.
|
|
|
|
#[path = "../../lbm/tests/common/mod.rs"]
|
|
mod common;
|
|
|
|
use common::*;
|
|
use fieldcraft_gpu::select::Policy;
|
|
use fieldcraft_lbm::backend::Backend;
|
|
use fieldcraft_lbm_gpu::GpuFlow;
|
|
use fieldcraft_model::{BoxFace, FaceCondition};
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn cell_updates_per_second() {
|
|
for (name, l, sphere_radius) in [("periodic box", 64usize, 0.0), ("periodic box", 128, 0.0), ("box with a sphere", 128, 32.0)] {
|
|
let side = l as f64;
|
|
let mut d = domain([0.0; 3], [side; 3], 1.0);
|
|
for f in BoxFace::ALL {
|
|
*d.faces.get_mut(f) = FaceCondition::Periodic {};
|
|
}
|
|
d.acceleration = [1e-6, 0.0, 0.0];
|
|
let mut obstacles = Vec::new();
|
|
if sphere_radius > 0.0 {
|
|
d.obstacles = vec!["Ball".into()];
|
|
obstacles.push(obstacle("Ball", sphere([side / 2.0; 3], sphere_radius, 4), &["skin"]));
|
|
}
|
|
let steps = 200;
|
|
let m = model(
|
|
1.0,
|
|
1.0 / 6.0,
|
|
d,
|
|
fieldcraft_model::Procedure::Fluid {
|
|
domain: "Flow".into(),
|
|
end_time: None,
|
|
steps: Some(steps),
|
|
time_step: Some(1.0),
|
|
frames: 1,
|
|
magic: None,
|
|
},
|
|
);
|
|
let (_, flow) = prepare(&m, &obstacles);
|
|
let Ok(mut gpu) = GpuFlow::new(&flow, Policy::from_env().expect("FIELDCRAFT_GPU names a policy")) else {
|
|
println!("skipping: no GPU");
|
|
return;
|
|
};
|
|
// One warm-up chunk, then the timed run: the chunk's steps are one submission.
|
|
gpu.advance(1).unwrap();
|
|
let start = std::time::Instant::now();
|
|
gpu.advance(steps).unwrap();
|
|
let _ = gpu.mass().unwrap();
|
|
let s = start.elapsed().as_secs_f64();
|
|
println!("{name}, {l}³ on {}: {:.1} M cell updates/s", gpu.adapter_name(), (gpu.fluid_cells() as f64 * steps as f64) / s / 1e6);
|
|
}
|
|
}
|