#![cfg(feature = "rayon")]
use rustsim_crowd::broadphase::Scratch;
use rustsim_crowd::common::{Pedestrian, WallSegment};
fn fixture(n: usize) -> (Vec<Pedestrian>, Vec<WallSegment>) {
let mut peds = Vec::with_capacity(n);
for k in 0..n as u64 {
let x = ((k.wrapping_mul(2_654_435_761)) % 6_000_000) as f64 / 1_000_000.0;
let y = ((k.wrapping_mul(40_503)) % 6_000_000) as f64 / 1_000_000.0;
peds.push(Pedestrian::new([x, y], [0.0, 0.0], 0.25, 1.2, [x + 5.0, y]));
}
let walls = vec![WallSegment {
a: [-1.0, -1.0],
b: [20.0, -1.0],
}];
(peds, walls)
}
fn assert_trajectories_equal(label: &str, a: &[Pedestrian], b: &[Pedestrian]) {
assert_eq!(a.len(), b.len(), "{label}: length mismatch");
for (i, (pa, pb)) in a.iter().zip(b.iter()).enumerate() {
assert_eq!(pa.pos, pb.pos, "{label}: pos diverged at agent {i}");
assert_eq!(pa.vel, pb.vel, "{label}: vel diverged at agent {i}");
}
}
#[test]
fn gcf_step_scratch_par_matches_step_scratch_bit_exact() {
use rustsim_crowd::generalized_centrifugal_force::{
neighbor_cutoff, step_scratch, step_scratch_par, Params,
};
let (mut a, walls) = fixture(64);
let mut b = a.clone();
let params = Params::default();
let cutoff = neighbor_cutoff(¶ms);
let mut scratch_a = Scratch::with_capacity(a.len(), cutoff);
let mut scratch_b = Scratch::with_capacity(b.len(), cutoff);
for _ in 0..40 {
step_scratch(&mut a, &walls, ¶ms, 0.05, &mut scratch_a);
step_scratch_par(&mut b, &walls, ¶ms, 0.05, &mut scratch_b);
}
assert_trajectories_equal("gcf", &a, &b);
}
#[test]
fn cfs_step_scratch_par_matches_step_scratch_bit_exact() {
use rustsim_crowd::collision_free_speed::{
neighbor_cutoff, step_scratch, step_scratch_par, Params,
};
let (mut a, walls) = fixture(64);
let mut b = a.clone();
let params = Params::default();
let cutoff = neighbor_cutoff(¶ms);
let mut scratch_a = Scratch::with_capacity(a.len(), cutoff);
let mut scratch_b = Scratch::with_capacity(b.len(), cutoff);
for _ in 0..40 {
step_scratch(&mut a, &walls, ¶ms, 0.05, &mut scratch_a);
step_scratch_par(&mut b, &walls, ¶ms, 0.05, &mut scratch_b);
}
assert_trajectories_equal("cfs", &a, &b);
}
#[test]
fn avm_step_scratch_par_matches_step_scratch_bit_exact() {
use rustsim_crowd::anticipation_velocity::{
neighbor_cutoff, step_scratch, step_scratch_par, Params,
};
let (mut a, walls) = fixture(64);
let mut b = a.clone();
let params = Params::default();
let cutoff = neighbor_cutoff(¶ms);
let mut scratch_a = Scratch::with_capacity(a.len(), cutoff);
let mut scratch_b = Scratch::with_capacity(b.len(), cutoff);
for _ in 0..40 {
step_scratch(&mut a, &walls, ¶ms, 0.05, &mut scratch_a);
step_scratch_par(&mut b, &walls, ¶ms, 0.05, &mut scratch_b);
}
assert_trajectories_equal("avm", &a, &b);
}
#[test]
fn osm_step_scratch_par_matches_step_scratch_bit_exact() {
use rustsim_crowd::optimal_steps::{neighbor_cutoff, step_scratch, step_scratch_par, Params};
let (mut a, walls) = fixture(64);
let mut b = a.clone();
let params = Params::default();
let cutoff = neighbor_cutoff(¶ms);
let mut scratch_a = Scratch::with_capacity(a.len(), cutoff);
let mut scratch_b = Scratch::with_capacity(b.len(), cutoff);
for _ in 0..40 {
step_scratch(&mut a, &walls, ¶ms, 0.05, &mut scratch_a);
step_scratch_par(&mut b, &walls, ¶ms, 0.05, &mut scratch_b);
}
assert_trajectories_equal("osm", &a, &b);
}
#[test]
fn step_scratch_store_par_matches_step_scratch_store_bit_exact() {
use rustsim_core::prelude::VecStore;
use rustsim_core::store::AgentStore;
use rustsim_crowd::common::Pedestrian;
use rustsim_crowd::prelude::{
recommended_cell_size, step_scratch_store, step_scratch_store_observed,
step_scratch_store_observed_par, step_scratch_store_par, CrowdAgent, Scratch,
SocialForceModel,
};
use rustsim_crowd::social_force::{neighbor_cutoff, Params};
let (peds, walls) = fixture(64);
let mut store_a: VecStore<CrowdAgent> = VecStore::new();
let mut store_b: VecStore<CrowdAgent> = VecStore::new();
for (i, p) in peds.iter().enumerate() {
store_a.insert(CrowdAgent {
id: i as u64 + 1,
ped: *p,
});
store_b.insert(CrowdAgent {
id: i as u64 + 1,
ped: *p,
});
}
let params = Params::default();
let cutoff = neighbor_cutoff(¶ms);
let cell = recommended_cell_size(cutoff);
let mut scratch_a = Scratch::with_capacity(peds.len(), cell);
let mut scratch_b = Scratch::with_capacity(peds.len(), cell);
let mut buf_a: Vec<Pedestrian> = Vec::with_capacity(peds.len());
let mut buf_b: Vec<Pedestrian> = Vec::with_capacity(peds.len());
let model = SocialForceModel;
for _ in 0..40 {
step_scratch_store(
&model,
&mut store_a,
&walls,
¶ms,
0.05,
&mut scratch_a,
&mut buf_a,
);
step_scratch_store_par(
&model,
&mut store_b,
&walls,
¶ms,
0.05,
&mut scratch_b,
&mut buf_b,
);
}
let ids_a = store_a.iter_ids();
let ids_b = store_b.iter_ids();
assert_eq!(ids_a, ids_b, "id sets diverged");
for &id in &ids_a {
let a = store_a.get(id).expect("a missing").ped;
let b = store_b.get(id).expect("b missing").ped;
assert_eq!(a.pos, b.pos, "store_par diverged in pos at id {id}");
assert_eq!(a.vel, b.vel, "store_par diverged in vel at id {id}");
}
let mut store_c: VecStore<CrowdAgent> = VecStore::new();
let mut store_d: VecStore<CrowdAgent> = VecStore::new();
for (i, p) in peds.iter().enumerate() {
store_c.insert(CrowdAgent {
id: i as u64 + 1,
ped: *p,
});
store_d.insert(CrowdAgent {
id: i as u64 + 1,
ped: *p,
});
}
let mut scratch_c = Scratch::with_capacity(peds.len(), cell);
let mut scratch_d = Scratch::with_capacity(peds.len(), cell);
let mut buf_c: Vec<Pedestrian> = Vec::with_capacity(peds.len());
let mut buf_d: Vec<Pedestrian> = Vec::with_capacity(peds.len());
let mut log_serial: Vec<(u64, [f64; 2])> = Vec::new();
let mut log_par: Vec<(u64, [f64; 2])> = Vec::new();
for _ in 0..5 {
step_scratch_store_observed(
&model,
&mut store_c,
&walls,
¶ms,
0.05,
&mut scratch_c,
&mut buf_c,
&mut |id, ped: &Pedestrian| log_serial.push((id, ped.pos)),
);
step_scratch_store_observed_par(
&model,
&mut store_d,
&walls,
¶ms,
0.05,
&mut scratch_d,
&mut buf_d,
&mut |id, ped: &Pedestrian| log_par.push((id, ped.pos)),
);
}
assert_eq!(
log_serial, log_par,
"observer streams diverged between serial and parallel integrated paths"
);
}