#[test]
fn free_streaming() {
use crate::tooling::core::algos::uniform::UniformGrid6D;
use crate::tooling::core::init::domain::{Domain, SpatialBoundType, VelocityBoundType};
use crate::tooling::core::phasespace::PhaseSpaceRepr as _;
use crate::tooling::core::types::DisplacementField;
let domain = Domain::builder()
.spatial_extent(4.0)
.velocity_extent(2.0)
.spatial_resolution(16)
.velocity_resolution(4)
.t_final(2.0)
.spatial_bc(SpatialBoundType::Periodic)
.velocity_bc(VelocityBoundType::Open)
.build()
.unwrap();
let mut grid = UniformGrid6D::new(domain);
let dx = grid.domain.dx();
let dv = grid.domain.dv();
let nx1 = grid.domain.spatial_res.x1 as usize;
let nx2 = grid.domain.spatial_res.x2 as usize;
let nx3 = grid.domain.spatial_res.x3 as usize;
let lx = 4.0f64;
let lv = 2.0f64;
let iv0 = 2usize;
let vx = -lv + (iv0 as f64 + 0.5) * dv[0];
let sigma = 1.5f64;
for ix1 in 0..nx1 {
let x1 = -lx + (ix1 as f64 + 0.5) * dx[0];
let f = (-x1 * x1 / (2.0 * sigma * sigma)).exp();
for ix2 in 0..nx2 {
for ix3 in 0..nx3 {
let idx = grid.index([ix1, ix2, ix3], [iv0, 0, 0]);
grid.data[idx] = f;
}
}
}
let dt = 2.0f64;
let shift = vx * dt;
let dummy = DisplacementField {
dx: vec![],
dy: vec![],
dz: vec![],
shape: [0, 0, 0],
};
grid.advect_x(&dummy, dt);
let mut max_err = 0.0f64;
let domain_width = 2.0 * lx;
for ix1 in 0..nx1 {
let x1 = -lx + (ix1 as f64 + 0.5) * dx[0];
let x_dep = x1 - shift;
let x_dep_wrapped = ((x_dep + lx).rem_euclid(domain_width)) - lx;
let expected = (-x_dep_wrapped * x_dep_wrapped / (2.0 * sigma * sigma)).exp();
let idx = grid.index([ix1, 0, 0], [iv0, 0, 0]);
let actual = grid.data[idx];
max_err = max_err.max((actual - expected).abs());
}
assert!(
max_err < 0.05,
"Free streaming L∞ error = {:.4}, expected < 0.05 (shift = {:.2} physical units)",
max_err,
shift
);
}