#[test]
fn uniform_acceleration() {
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::AccelerationField;
let domain = Domain::builder()
.spatial_extent(2.0)
.velocity_extent(4.0)
.spatial_resolution(4)
.velocity_resolution(16)
.t_final(2.0)
.spatial_bc(SpatialBoundType::Periodic)
.velocity_bc(VelocityBoundType::Open)
.build()
.unwrap();
let mut grid = UniformGrid6D::new(domain);
let dv = grid.domain.dv();
let dx = grid.domain.dx();
let nv1 = grid.domain.velocity_res.v1 as usize;
let nv2 = grid.domain.velocity_res.v2 as usize;
let nv3 = grid.domain.velocity_res.v3 as usize;
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 lv = 4.0f64;
let iv2_0 = nv2 / 2;
let iv3_0 = nv3 / 2;
let sigma = 1.5f64;
for ix1 in 0..nx1 {
for ix2 in 0..nx2 {
for ix3 in 0..nx3 {
for iv1 in 0..nv1 {
let v1 = -lv + (iv1 as f64 + 0.5) * dv[0];
let f = (-v1 * v1 / (2.0 * sigma * sigma)).exp();
let idx = grid.index([ix1, ix2, ix3], [iv1, iv2_0, iv3_0]);
grid.data[idx] = f;
}
}
}
}
let ax = 0.5f64;
let dt = 2.0f64;
let n_spatial = nx1 * nx2 * nx3;
let accel = AccelerationField {
gx: vec![ax; n_spatial],
gy: vec![0.0; n_spatial],
gz: vec![0.0; n_spatial],
shape: [nx1, nx2, nx3],
};
grid.advect_v(&accel, dt);
let shift = ax * dt; let domain_width = 2.0 * lv;
let mut max_err = 0.0f64;
for iv1 in 0..nv1 {
let v1 = -lv + (iv1 as f64 + 0.5) * dv[0];
let v_dep = v1 - shift;
let expected = if v_dep < -lv || v_dep >= lv {
0.0
} else {
(-v_dep * v_dep / (2.0 * sigma * sigma)).exp()
};
let idx = grid.index([0, 0, 0], [iv1, iv2_0, iv3_0]);
let actual = grid.data[idx];
max_err = max_err.max((actual - expected).abs());
}
assert!(
max_err < 1e-10,
"Uniform acceleration L∞ error = {:.2e}, expected < 1e-10 (shift = {:.2})",
max_err,
shift
);
}