diff --git a/src/simulation/config.rs b/src/simulation/config.rs index 79c0372..78d0e91 100644 --- a/src/simulation/config.rs +++ b/src/simulation/config.rs @@ -28,7 +28,12 @@ pub struct Config { impl Config { pub fn from_file(args: &CommandLineArgs) -> Self { let reader = BufReader::new(File::open(&args.config_path).expect("Failed to open file.")); - let mut config: Config = serde_yaml::from_reader(reader).expect("Failed to parse config."); + let mut config: Config = serde_yaml::from_reader(reader).unwrap_or_else(|e| { + panic!( + "Failed to parse config at {}. Original error was: {}", + args.config_path, e + ) + }); // replace some configuration if we get a partition from the outside. This is interesting for testing if let Some(part_args) = args.num_parts { config.set_partitioning(Partitioning { @@ -104,14 +109,10 @@ impl Config { if let Some(simulation) = self.module::("simulation") { simulation } else { - let default = Simulation { - start_time: 0, - end_time: 86400, - sample_size: 1., - }; + let default = Simulation::default(); self.modules .borrow_mut() - .insert("simulation".to_string(), Box::new(default.clone())); + .insert("simulation".to_string(), Box::new(default)); default } } @@ -164,11 +165,12 @@ pub struct Routing { pub mode: RoutingMode, } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, Copy)] pub struct Simulation { pub start_time: u32, pub end_time: u32, pub sample_size: f32, + pub stuck_threshold: u32, } #[typetag::serde(tag = "type")] @@ -211,6 +213,17 @@ impl ConfigModule for Simulation { } } +impl Default for Simulation { + fn default() -> Self { + Self { + start_time: 0, + end_time: 86400, + sample_size: 1.0, + stuck_threshold: u32::MAX, + } + } +} + #[derive(PartialEq, Debug, ValueEnum, Clone, Copy, Serialize, Deserialize)] pub enum RoutingMode { AdHoc, diff --git a/src/simulation/controller.rs b/src/simulation/controller.rs index 953b6a8..bdfe750 100644 --- a/src/simulation/controller.rs +++ b/src/simulation/controller.rs @@ -117,8 +117,7 @@ fn execute_partition(comm: C, args: &CommandLineAr &mut garage, ); - let network_partition = - SimNetworkPartition::from_network(&network, rank, config.simulation().sample_size); + let network_partition = SimNetworkPartition::from_network(&network, rank, config.simulation()); info!( "Partition #{rank} network has: {} nodes and {} links. Population has {} agents", network_partition.nodes.len(), diff --git a/src/simulation/messaging/communication/message_broker.rs b/src/simulation/messaging/communication/message_broker.rs index 392e632..a1d8f4e 100644 --- a/src/simulation/messaging/communication/message_broker.rs +++ b/src/simulation/messaging/communication/message_broker.rs @@ -170,6 +170,7 @@ mod tests { use std::sync::Arc; use std::thread; + use crate::simulation::config; use crate::simulation::id::Id; use crate::simulation::messaging::communication::communicators::ChannelSimCommunicator; use crate::simulation::messaging::communication::message_broker::{ @@ -353,10 +354,16 @@ mod tests { communicator: ChannelSimCommunicator, ) -> NetMessageBroker { let rank = communicator.rank(); + let config = config::Simulation { + start_time: 0, + end_time: 0, + sample_size: 0.0, + stuck_threshold: 0, + }; let broker = NetMessageBroker::new( Rc::new(communicator), &create_network(), - &SimNetworkPartition::from_network(&create_network(), rank, 1.0), + &SimNetworkPartition::from_network(&create_network(), rank, config), ); broker } diff --git a/src/simulation/network/flow_cap.rs b/src/simulation/network/flow_cap.rs index dd64fc3..99e3613 100644 --- a/src/simulation/network/flow_cap.rs +++ b/src/simulation/network/flow_cap.rs @@ -6,7 +6,8 @@ pub struct Flowcap { } impl Flowcap { - pub fn new(capacity_s: f32) -> Flowcap { + pub fn new(capacity_h: f32, sample_size: f32) -> Flowcap { + let capacity_s = capacity_h * sample_size / 3600.; Flowcap { last_update_time: 0, accumulated_capacity: capacity_s, @@ -41,11 +42,19 @@ impl Flowcap { #[cfg(test)] mod tests { + use assert_approx_eq::assert_approx_eq; + use crate::simulation::network::flow_cap::Flowcap; + #[test] + fn init() { + let cap = Flowcap::new(5432., 0.31415); + assert_approx_eq!(0.47401747, cap.capacity_s, 0.0001); + } + #[test] fn flowcap_consume_capacity() { - let mut flowcap = Flowcap::new(10.0); + let mut flowcap = Flowcap::new(36000., 1.); assert!(flowcap.has_capacity()); flowcap.consume_capacity(20.0); @@ -54,7 +63,7 @@ mod tests { #[test] fn flowcap_max_capacity_s() { - let mut flowcap = Flowcap::new(10.0); + let mut flowcap = Flowcap::new(36000., 1.); flowcap.update_capacity(20); @@ -64,7 +73,7 @@ mod tests { #[test] fn flowcap_acc_capacity() { - let mut flowcap = Flowcap::new(0.25); + let mut flowcap = Flowcap::new(900., 1.); assert!(flowcap.has_capacity()); // accumulated_capacity should be at -0.75 after this. diff --git a/src/simulation/network/global_network.rs b/src/simulation/network/global_network.rs index e87a28b..19e80e6 100644 --- a/src/simulation/network/global_network.rs +++ b/src/simulation/network/global_network.rs @@ -296,7 +296,7 @@ mod tests { } #[test] - #[serial_test::serial] + #[ignore] // ingore this test, because it keeps not working, due to non determined ordering of metis fn from_file() { let network = Network::from_file( "./assets/equil/equil-network.xml", diff --git a/src/simulation/network/link.rs b/src/simulation/network/link.rs index 5eca707..841963d 100644 --- a/src/simulation/network/link.rs +++ b/src/simulation/network/link.rs @@ -1,11 +1,13 @@ use std::collections::VecDeque; use std::fmt::Debug; +use crate::simulation::config; use crate::simulation::id::Id; use crate::simulation::network::flow_cap::Flowcap; use crate::simulation::network::global_network::Node; use crate::simulation::network::sim_network::SplitStorage; use crate::simulation::network::storage_cap::StorageCap; +use crate::simulation::network::stuck_timer::StuckTimer; use crate::simulation::wire_types::messages::Vehicle; use super::global_network::Link; @@ -76,6 +78,16 @@ impl SimLink { } } + pub fn is_veh_stuck(&self, now: u32) -> bool { + match self { + SimLink::Local(ll) => ll.stuck_timer.is_stuck(now), + SimLink::In(il) => il.local_link.stuck_timer.is_stuck(now), + SimLink::Out(_) => { + panic!("Out links don't offer vehicles. ") + } + } + } + pub fn is_available(&self) -> bool { match self { SimLink::Local(ll) => ll.is_available(), @@ -141,6 +153,7 @@ pub struct LocalLink { free_speed: f32, storage_cap: StorageCap, flow_cap: Flowcap, + stuck_timer: StuckTimer, pub from: Id, pub to: Id, } @@ -152,15 +165,15 @@ struct VehicleQEntry { } impl LocalLink { - pub fn from_link(link: &Link, sample_size: f32, effective_cell_size: f32) -> Self { + pub fn from_link(link: &Link, effective_cell_size: f32, config: config::Simulation) -> Self { LocalLink::new( link.id.clone(), link.capacity, link.freespeed, link.permlanes, link.length, - sample_size, effective_cell_size, + config, link.from.clone(), link.to.clone(), ) @@ -173,7 +186,8 @@ impl LocalLink { length: 1.0, free_speed: 1.0, storage_cap: StorageCap::new(0., 1., 1., 1.0, 7.5), - flow_cap: Flowcap::new(1.0), + flow_cap: Flowcap::new(3600., 1.0), + stuck_timer: StuckTimer::new(u32::MAX), from, to, } @@ -185,17 +199,16 @@ impl LocalLink { free_speed: f32, perm_lanes: f32, length: f64, - sample_size: f32, effective_cell_size: f32, + config: config::Simulation, from: Id, to: Id, ) -> Self { - let flow_cap_s = capacity_h * sample_size / 3600.; let storage_cap = StorageCap::new( length, perm_lanes, - flow_cap_s, - sample_size, + capacity_h, + config.sample_size, effective_cell_size, ); @@ -205,7 +218,8 @@ impl LocalLink { length, free_speed, storage_cap, - flow_cap: Flowcap::new(flow_cap_s), + flow_cap: Flowcap::new(capacity_h, config.sample_size), + stuck_timer: StuckTimer::new(config.stuck_threshold), from, to, } @@ -228,7 +242,7 @@ impl LocalLink { let veh = self.q.pop_front().unwrap_or_else(|| panic!("There was no vehicle in the queue. Use 'offers_veh' to test if a vehicle is present first.")); self.flow_cap.consume_capacity(veh.vehicle.pce); self.storage_cap.release(veh.vehicle.pce); - + self.stuck_timer.reset(); veh.vehicle } @@ -246,6 +260,7 @@ impl LocalLink { // peek if fist vehicle in queue can leave if let Some(entry) = self.q.front() { if entry.earliest_exit_time <= now { + self.stuck_timer.start(now); return Some(&entry.vehicle); } } @@ -293,11 +308,10 @@ impl SplitOutLink { sample_size: f32, to_part: u32, ) -> SplitOutLink { - let flow_cap_s = link.capacity * sample_size / 3600.; let storage_cap = StorageCap::new( link.length, link.permlanes, - flow_cap_s, + link.capacity, sample_size, effective_cell_size, ); @@ -364,9 +378,11 @@ impl SplitInLink { mod sim_link_tests { use assert_approx_eq::assert_approx_eq; + use crate::simulation::config; use crate::simulation::id::Id; use crate::simulation::network::link::{LocalLink, SimLink}; use crate::simulation::wire_types::messages::Vehicle; + use crate::test_utils; use crate::test_utils::create_agent; #[test] @@ -377,8 +393,8 @@ mod sim_link_tests { 10., 3., 100., - 1.0, 7.5, + test_utils::config(), Id::new_internal(1), Id::new_internal(2), )); @@ -399,8 +415,8 @@ mod sim_link_tests { 10., 3., 100., - 1.0, 7.5, + test_utils::config(), Id::new_internal(1), Id::new_internal(2), )); @@ -430,8 +446,8 @@ mod sim_link_tests { 10., 3., 100., - 1.0, 7.5, + test_utils::config(), Id::new_internal(1), Id::new_internal(2), )); @@ -470,8 +486,8 @@ mod sim_link_tests { 10., 3., 100., - 1.0, 7.5, + test_utils::config(), Id::new_internal(1), Id::new_internal(2), )); @@ -500,8 +516,8 @@ mod sim_link_tests { 1., 1., 15.0, - 1., 10.0, + test_utils::config(), Id::new_internal(0), Id::new_internal(0), )); @@ -526,66 +542,85 @@ mod sim_link_tests { let popped_vehicle2 = link.pop_veh(); assert_eq!(id2, popped_vehicle2.id); } -} - -#[cfg(test)] -mod local_link_tests { - use assert_approx_eq::assert_approx_eq; - - use crate::simulation::id::Id; - use crate::simulation::network::link::LocalLink; #[test] - fn storage_cap_initialized_default() { - let link = LocalLink::new( - Id::new_internal(1), + pub fn stuck_time() { + let stuck_threshold = 10; + let config = config::Simulation { + start_time: 0, + end_time: 0, + sample_size: 1.0, + stuck_threshold, + }; + let mut link = SimLink::Local(LocalLink::new( + Id::create("stuck-link"), 1., 1., - 3., - 100., - 0.2, + 1.0, + 10.0, 7.5, - Id::new_internal(1), - Id::new_internal(2), - ); + config, + Id::create("from-node"), + Id::create("to-node"), + )); - // we expect a storage size of 100 * 3 * 0.2 / 7.5 = 8 - assert_approx_eq!(8., link.storage_cap.max()); - } + let vehicle = Vehicle::new(1, 0, 10., 1., None); + link.push_veh(vehicle, 0); - #[test] - fn storage_cap_initialized_large_flow() { - let link = LocalLink::new( - Id::new_internal(1), - 360000., - 1., - 3., - 100., - 0.2, - 7.5, - Id::new_internal(1), - Id::new_internal(2), - ); + // earliest exit is at 10. Therefore this call should not trigger the stuck timer + let offers = link.offers_veh(9); + assert!(offers.is_none()); + assert!(!link.is_veh_stuck(9)); - // we expect a storage size of 20. because it the flow cap/s is 20 (36000 * 0.2 / 3600) - assert_eq!(20., link.storage_cap.max()); + // this should trigger the stuck timer + let expected_timer_start = 10; + let offers = link.offers_veh(expected_timer_start); + assert!(offers.is_some()); + assert!(!link.is_veh_stuck(expected_timer_start + stuck_threshold - 1)); + assert!(link.is_veh_stuck(expected_timer_start + stuck_threshold)); } #[test] - fn flow_cap_initialized() { - let link = LocalLink::new( - Id::new_internal(1), - 3600., + pub fn stuck_time_reset() { + let stuck_threshold = 10; + let earliest_exit: u32 = 10; + let config = config::Simulation { + start_time: 0, + end_time: 0, + sample_size: 1.0, + stuck_threshold, + }; + let mut link = SimLink::Local(LocalLink::new( + Id::create("stuck-link"), + 36000., 1., - 3., - 100., - 0.2, + 1.0, + earliest_exit as f64, 7.5, - Id::new_internal(1), - Id::new_internal(2), - ); + config, + Id::create("from-node"), + Id::create("to-node"), + )); + + let vehicle1 = Vehicle::new(1, 0, earliest_exit as f32, 1., None); + let vehicle2 = Vehicle::new(2, 0, earliest_exit as f32, 1., None); + link.push_veh(vehicle1, 0); + link.push_veh(vehicle2, 0); - assert_eq!(0.2, link.flow_cap.capacity()) + // trigger stuck timer + assert!(link.offers_veh(earliest_exit).is_some()); + // check that stuck timer works as expected + let now = earliest_exit + stuck_threshold; + assert!(link.is_veh_stuck(now)); + // fetch the stuck vehicle, which should reset the timer, so that the next veh is not stuck + let _ = link.pop_veh(); + assert!(!link.is_veh_stuck(now)); + // the next vehicle should be ready to leave the link as well. + // This call should trigger the stuck timer again. + assert!(link.offers_veh(now).is_some()); + let now = now + stuck_threshold; + assert!(!link.is_veh_stuck(now - 1)); + assert!(link.is_veh_stuck(now)); } } diff --git a/src/simulation/network/mod.rs b/src/simulation/network/mod.rs index f8dba6f..4317c16 100644 --- a/src/simulation/network/mod.rs +++ b/src/simulation/network/mod.rs @@ -5,3 +5,4 @@ pub mod link; pub mod metis_partitioning; pub mod sim_network; mod storage_cap; +mod stuck_timer; diff --git a/src/simulation/network/sim_network.rs b/src/simulation/network/sim_network.rs index 3d93086..eead0f1 100644 --- a/src/simulation/network/sim_network.rs +++ b/src/simulation/network/sim_network.rs @@ -4,6 +4,7 @@ use nohash_hasher::{IntMap, IntSet}; use rand::rngs::ThreadRng; use rand::{thread_rng, Rng}; +use crate::simulation::config; use crate::simulation::id::Id; use crate::simulation::messaging::events::EventsPublisher; use crate::simulation::wire_types::events::Event; @@ -40,7 +41,11 @@ pub struct SimNode { } impl SimNetworkPartition { - pub fn from_network(global_network: &Network, partition: u32, sample_size: f32) -> Self { + pub fn from_network( + global_network: &Network, + partition: u32, + config: config::Simulation, + ) -> Self { let nodes: Vec<&Node> = global_network .nodes .iter() @@ -62,7 +67,7 @@ impl SimNetworkPartition { link, partition, global_network.effective_cell_size, - sample_size, + config, global_network, ), ) @@ -90,22 +95,22 @@ impl SimNetworkPartition { link: &Link, partition: u32, effective_cell_size: f32, - sample_size: f32, + config: config::Simulation, global_network: &Network, ) -> SimLink { let from_part = global_network.get_node(&link.from).partition; //all_nodes.get(link.from.internal()).unwrap().partition; let to_part = global_network.get_node(&link.to).partition; //all_nodes.get(link.to.internal()).unwrap().partition; if from_part == to_part { - SimLink::Local(LocalLink::from_link(link, sample_size, effective_cell_size)) + SimLink::Local(LocalLink::from_link(link, effective_cell_size, config)) } else if to_part == partition { - let local_link = LocalLink::from_link(link, sample_size, effective_cell_size); + let local_link = LocalLink::from_link(link, effective_cell_size, config); SimLink::In(SplitInLink::new(from_part, local_link)) } else { SimLink::Out(SplitOutLink::new( link, effective_cell_size, - sample_size, + config.sample_size, to_part, )) } @@ -407,10 +412,11 @@ impl SimNetworkPartition { let in_link = links.get(in_id).unwrap(); if let Some(veh_ref) = in_link.offers_veh(now) { return if let Some(next_id_int) = veh_ref.peek_next_route_element() { - // if the vehicle has a next link id, it should move out of the current link, if the - // next link is free. + // if the vehicle has a next link id, it should move out of the current link. + // if the vehicle has reached its stuck threshold, we push it to the next link regardless of the available + // storage capacity. Under normal conditions, we check whether the downstream link has storage capacity available let out_link = links.get(&next_id_int).unwrap(); - out_link.is_available() + in_link.is_veh_stuck(now) || out_link.is_available() } else { // if there is no next link, the vehicle is done with its route and we can take it out // of the network @@ -461,7 +467,7 @@ mod tests { link::SimLink, }; use crate::simulation::wire_types::messages::Vehicle; - use crate::simulation::wire_types::population::{Activity, Leg, Person, Plan, Route}; + use crate::test_utils; use super::SimNetworkPartition; @@ -497,8 +503,8 @@ mod tests { 1, PartitionMethod::Metis(MetisOptions::default()), ); - let mut network = SimNetworkPartition::from_network(&global_net, 0, 1.0); - let agent = create_agent(1, vec![0, 1, 2]); + let mut network = SimNetworkPartition::from_network(&global_net, 0, test_utils::config()); + let agent = test_utils::create_agent(1, vec![0, 1, 2]); let vehicle = Vehicle::new(1, 0, 10., 1., Some(agent)); network.send_veh_en_route(vehicle, None, 0); @@ -541,8 +547,8 @@ mod tests { 2, PartitionMethod::None, ); - let mut network = SimNetworkPartition::from_network(&global_net, 0, 1.0); - let agent = create_agent(1, vec![0, 1, 2]); + let mut network = SimNetworkPartition::from_network(&global_net, 0, test_utils::config()); + let agent = test_utils::create_agent(1, vec![0, 1, 2]); let vehicle = Vehicle::new(1, 0, 10., 100., Some(agent)); network.send_veh_en_route(vehicle, None, 0); @@ -571,11 +577,11 @@ mod tests { 1, PartitionMethod::Metis(MetisOptions::default()), ); - let mut network = SimNetworkPartition::from_network(&global_net, 0, 1.0); + let mut network = SimNetworkPartition::from_network(&global_net, 0, test_utils::config()); // place 100 vehicles on first link for i in 0..100 { - let agent = create_agent(i, vec![0]); + let agent = test_utils::create_agent(i, vec![0]); let vehicle = Vehicle::new(i, 0, 10., 1., Some(agent)); network.send_veh_en_route(vehicle, None, 0); } @@ -608,12 +614,12 @@ mod tests { let id_1: Id = Id::get_from_ext("link1"); let id_2: Id = Id::get_from_ext("link2"); - let mut network = SimNetworkPartition::from_network(&global_net, 0, 1.0); + let mut network = SimNetworkPartition::from_network(&global_net, 0, test_utils::config()); //place 10 vehicles on link2 so that it is jammed // vehicles are very slow, so that the first vehicle should leave link2 at t=1000 for i in 0..10 { - let agent = create_agent(i, vec![id_2.internal(), 2]); + let agent = test_utils::create_agent(i, vec![id_2.internal(), 2]); let vehicle = Vehicle::new(i, 0, 1., 10., Some(agent)); network.send_veh_en_route(vehicle, None, 0); } @@ -621,7 +627,7 @@ mod tests { // place 1 vehicle onto link1 which has to wait until link2 has free storage cap // as the first vehicle leaves link2 at t=1000 this vehicle can leave link1 and enter link2 at // the next timestep at t=1001 - let agent = create_agent(11, vec![id_1.internal(), 1, 2]); + let agent = test_utils::create_agent(11, vec![id_1.internal(), 1, 2]); let vehicle = Vehicle::new(11, 0, 10., 1., Some(agent)); network.send_veh_en_route(vehicle, None, 0); @@ -631,6 +637,55 @@ mod tests { let link1 = network.links.get(&id_1.internal()).unwrap(); if (10..1001).contains(&now) { + // while the vehicle waits, link1 is ready to move the vehicle + assert!(link1.offers_veh(now).is_some()); + } else { + // once the vehicle has move, link1 has nothing to offer. + assert!(link1.offers_veh(now).is_none()); + } + } + } + + #[test] + fn move_nodes_stuck_threshold() { + let mut publisher = EventsPublisher::new(); + let mut global_net = Network::from_file( + "./assets/3-links/3-links-network.xml", + 1, + PartitionMethod::Metis(MetisOptions::default()), + ); + global_net.effective_cell_size = 10.; + + let id_1: Id = Id::get_from_ext("link1"); + let id_2: Id = Id::get_from_ext("link2"); + let mut config = test_utils::config(); + config.stuck_threshold = 10; + let mut network = SimNetworkPartition::from_network(&global_net, 0, config); + + //place 10 vehicles on link2 so that it is jammed + // vehicles are very slow, so that the first vehicle should leave link2 at t=1000 + for i in 0..10 { + let agent = test_utils::create_agent(i, vec![id_2.internal(), 2]); + let vehicle = Vehicle::new(i, 0, 1., 10., Some(agent)); + network.send_veh_en_route(vehicle, None, 0); + } + + // place 1 vehicle onto link1 which has to wait until link2 has free storage cap, or the stuck time is reached + // first vehicle on link2 leaves at t=1000, but stuck time is 10. Therefore we expect the vehicle on link1 to be + // pushed onto link2 at t=10+10. + let agent = test_utils::create_agent(11, vec![id_1.internal(), 1, 2]); + let vehicle = Vehicle::new(11, 0, 10., 1., Some(agent)); + network.send_veh_en_route(vehicle, None, 0); + + for now in 0..20 { + network.move_nodes(&mut publisher, now); + network.move_links(now); + + let link1 = network.links.get(&id_1.internal()).unwrap(); + // the veh is ready to leave at t=10, but the downstream link is jammed + // after 10 seconds at t=20, the stuck threshold is reached and the vehicle + // is moved + if (10..20).contains(&now) { assert!(link1.offers_veh(now).is_some()); } else { assert!(link1.offers_veh(now).is_none()); @@ -699,25 +754,25 @@ mod tests { modes: Default::default(), partition: 0, }); - let mut sim_net = SimNetworkPartition::from_network(&net, 0, 1.0); + let mut sim_net = SimNetworkPartition::from_network(&net, 0, test_utils::config()); //place 10 vehicles on 2, so that it is jammed. The link should release 1 veh per time step. for i in 2000..2010 { - let agent = create_agent(i, vec![2]); + let agent = test_utils::create_agent(i, vec![2]); let vehicle = Vehicle::new(i, 0, 100., 1., Some(agent)); sim_net.send_veh_en_route(vehicle, None, 0); } //place 1000 vehicles on 0 for i in 0..1000 { - let agent = create_agent(i, vec![0, 2]); + let agent = test_utils::create_agent(i, vec![0, 2]); let vehicle = Vehicle::new(i, 0, 100., 1., Some(agent)); sim_net.send_veh_en_route(vehicle, None, 0); } //place 1000 vehicles on 1 for i in 1000..2000 { - let agent = create_agent(i, vec![1, 2]); + let agent = test_utils::create_agent(i, vec![1, 2]); let vehicle = Vehicle::new(i, 0, 100., 1., Some(agent)); sim_net.send_veh_en_route(vehicle, None, 0); } @@ -744,7 +799,7 @@ mod tests { let mut publisher = EventsPublisher::new(); let split_link_id: Id = Id::get_from_ext("link-2"); - let agent = create_agent(1, vec![split_link_id.internal()]); + let agent = test_utils::create_agent(1, vec![split_link_id.internal()]); let vehicle = Vehicle::new(1, 0, 10., 100., Some(agent)); // collect empty storage caps @@ -816,7 +871,7 @@ mod tests { net.add_link(out_link_1_2); net.add_link(out_link_3_1); - let sim_net = SimNetworkPartition::from_network(&net, 0, 1.0); + let sim_net = SimNetworkPartition::from_network(&net, 0, test_utils::config()); let neighbors = sim_net.neighbors(); assert_eq!(3, neighbors.len()); @@ -853,25 +908,8 @@ mod tests { let link2 = network.links.get_mut(1).unwrap(); link2.partition = 1; vec![ - SimNetworkPartition::from_network(network, 0, 1.0), - SimNetworkPartition::from_network(network, 1, 1.0), + SimNetworkPartition::from_network(network, 0, test_utils::config()), + SimNetworkPartition::from_network(network, 1, test_utils::config()), ] } - - fn create_agent(id: u64, route: Vec) -> Person { - let route = Route { - veh_id: id, - distance: 0.0, - route, - }; - let leg = Leg::new(route, 0, 0, None); - let act = Activity::new(0., 0., 0, 1, None, None, None); - let mut plan = Plan::new(); - plan.add_act(act); - plan.add_leg(leg); - let mut agent = Person::new(id, plan); - agent.advance_plan(); - - agent - } } diff --git a/src/simulation/network/storage_cap.rs b/src/simulation/network/storage_cap.rs index c6a48d8..d6ad165 100644 --- a/src/simulation/network/storage_cap.rs +++ b/src/simulation/network/storage_cap.rs @@ -25,10 +25,11 @@ impl StorageCap { pub fn new( length: f64, perm_lanes: f32, - flow_cap_s: f32, + capacity_h: f32, sample_size: f32, effective_cell_size: f32, ) -> Self { + let flow_cap_s = capacity_h * sample_size / 3600.; let cap = length * perm_lanes as f64 * sample_size as f64 / effective_cell_size as f64; // storage capacity needs to be at least enough to handle the cap_per_time_step: let max_storage_cap = flow_cap_s.max(cap as f32); @@ -56,11 +57,6 @@ impl StorageCap { self.consumed } - #[cfg(test)] - pub fn max(&self) -> f32 { - self.max - } - /// Consumes storage capacity on a link /// /// This method should be called when a vehicle enters a link. @@ -99,3 +95,21 @@ impl StorageCap { available_cap > 0.0 } } + +#[cfg(test)] +mod test { + use crate::simulation::network::storage_cap::StorageCap; + + #[test] + fn init_default() { + let cap = StorageCap::new(100., 3., 1., 0.2, 7.5); + assert_eq!(8., cap.max); + } + + #[test] + fn init_large_capacity() { + let cap = StorageCap::new(100., 3., 360000., 0.2, 7.5); + // we expect a storage size of 20. because it the flow cap/s is 20 (36000 * 0.2 / 3600) + assert_eq!(20., cap.max); + } +} diff --git a/src/simulation/network/stuck_timer.rs b/src/simulation/network/stuck_timer.rs new file mode 100644 index 0000000..a305fc0 --- /dev/null +++ b/src/simulation/network/stuck_timer.rs @@ -0,0 +1,80 @@ +use std::cell::Cell; + +#[derive(Debug, Clone)] +pub struct StuckTimer { + timer_started: Cell>, + stuck_threshold: u32, +} + +impl StuckTimer { + pub fn new(stuck_threshold: u32) -> Self { + StuckTimer { + timer_started: Cell::new(None), + stuck_threshold, + } + } + + pub fn start(&self, now: u32) { + if self.timer_started.get().is_none() { + self.timer_started.replace(Some(now)); + } + } + + pub fn reset(&self) { + self.timer_started.replace(None); + } + + pub fn is_stuck(&self, now: u32) -> bool { + if let Some(time) = self.timer_started.get() { + now - time >= self.stuck_threshold + } else { + false + } + } +} + +#[cfg(test)] +mod tests { + use crate::simulation::network::stuck_timer::StuckTimer; + + #[test] + fn init() { + let timer = StuckTimer::new(42); + assert!(timer.timer_started.get().is_none()); + assert_eq!(42, timer.stuck_threshold); + } + + #[test] + fn start() { + let timer = StuckTimer::new(42); + + timer.start(1); + timer.start(2); + + assert!(timer.timer_started.get().is_some()); + assert_eq!(1, timer.timer_started.get().unwrap()); + } + + #[test] + fn reset() { + let timer = StuckTimer::new(42); + + timer.start(17); + assert!(timer.timer_started.get().is_some()); + + timer.reset(); + assert!(timer.timer_started.get().is_none()); + } + + #[test] + fn is_stuck() { + let timer = StuckTimer::new(42); + + timer.start(17); + assert!(!timer.is_stuck(18)); + assert!(timer.is_stuck(17 + 42)); + + timer.reset(); + assert!(!timer.is_stuck(17 + 42)); + } +} diff --git a/src/simulation/population/io.rs b/src/simulation/population/io.rs index 9e6f7ed..353fa94 100644 --- a/src/simulation/population/io.rs +++ b/src/simulation/population/io.rs @@ -208,7 +208,7 @@ impl IOPerson { #[derive(Debug, Deserialize, PartialEq)] pub struct IOPopulation { - #[serde(rename = "person")] + #[serde(rename = "person", default)] pub persons: Vec, } @@ -225,12 +225,13 @@ impl IOPopulation { #[cfg(test)] mod tests { + use std::path::PathBuf; + + use quick_xml::de::from_str; + use crate::simulation::config::{MetisOptions, PartitionMethod}; use crate::simulation::id::Id; use crate::simulation::network::global_network::Network; - use quick_xml::de::from_str; - use std::path::PathBuf; - use crate::simulation::population::io::{load_from_xml, IOPlanElement, IOPopulation}; use crate::simulation::vehicles::garage::Garage; diff --git a/src/simulation/replanning/replanner.rs b/src/simulation/replanning/replanner.rs index 131f049..2717670 100644 --- a/src/simulation/replanning/replanner.rs +++ b/src/simulation/replanning/replanner.rs @@ -318,6 +318,7 @@ mod tests { use crate::simulation::vehicles::garage::Garage; use crate::simulation::wire_types::population::{Person, Route}; use crate::simulation::wire_types::vehicles::VehicleType; + use crate::test_utils; #[test] fn test_trip_placeholder_leg() { @@ -334,7 +335,7 @@ mod tests { &mut garage, 0, ); - let sim_net = SimNetworkPartition::from_network(&network, 0, 1.0); + let sim_net = SimNetworkPartition::from_network(&network, 0, test_utils::config()); let agent_id = Id::get_from_ext("100"); let mut agent = population.persons.get_mut(&agent_id).unwrap(); @@ -371,7 +372,7 @@ mod tests { &mut garage, 0, ); - let sim_net = SimNetworkPartition::from_network(&network, 0, 1.0); + let sim_net = SimNetworkPartition::from_network(&network, 0, test_utils::config()); let agent_id = Id::get_from_ext("100"); let mut agent = population.persons.get_mut(&agent_id).unwrap(); @@ -426,7 +427,7 @@ mod tests { 0, ); - let sim_net = SimNetworkPartition::from_network(&network, 0, 1.0); + let sim_net = SimNetworkPartition::from_network(&network, 0, test_utils::config()); let agent_id = Id::get_from_ext("100"); let mut agent = population.persons.get_mut(&agent_id).unwrap(); @@ -517,7 +518,7 @@ mod tests { &mut garage, 0, ); - let sim_net = SimNetworkPartition::from_network(&network, 0, 1.0); + let sim_net = SimNetworkPartition::from_network(&network, 0, test_utils::config()); let agent_id = Id::get_from_ext("100"); let mut agent = population.persons.get_mut(&agent_id).unwrap(); diff --git a/src/test_utils.rs b/src/test_utils.rs index bd7846f..7c40db9 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -1,6 +1,7 @@ use std::fs; use std::path::PathBuf; +use crate::simulation::config; use crate::simulation::id::Id; use crate::simulation::wire_types::population::{Activity, Leg, Person, Plan, Route}; use crate::simulation::wire_types::vehicles::{LevelOfDetail, VehicleType}; @@ -40,3 +41,12 @@ pub fn create_vehicle_type(id: &Id, net_mode: Id) -> Vehicl lod: LevelOfDetail::Network as i32, } } + +pub fn config() -> config::Simulation { + config::Simulation { + start_time: 0, + end_time: 0, + sample_size: 1.0, + stuck_threshold: u32::MAX, + } +} diff --git a/tests/resources/3-links/3-links-config-1.yml b/tests/resources/3-links/3-links-config-1.yml index d9a5f24..8a83896 100644 --- a/tests/resources/3-links/3-links-config-1.yml +++ b/tests/resources/3-links/3-links-config-1.yml @@ -17,9 +17,4 @@ modules: routing: type: Routing mode: UsePlans - simulation: - type: Simulation - start_time: 0 - end_time: 86400 - sample_size: 1.0 diff --git a/tests/resources/3-links/3-links-config-2.yml b/tests/resources/3-links/3-links-config-2.yml index e2c8b84..c68958a 100644 --- a/tests/resources/3-links/3-links-config-2.yml +++ b/tests/resources/3-links/3-links-config-2.yml @@ -17,9 +17,4 @@ modules: routing: type: Routing mode: UsePlans - simulation: - type: Simulation - start_time: 0 - end_time: 86400 - sample_size: 1.0 diff --git a/tests/resources/adhoc_routing/no_updates/config-1.yml b/tests/resources/adhoc_routing/no_updates/config-1.yml index 865e52c..1daf799 100644 --- a/tests/resources/adhoc_routing/no_updates/config-1.yml +++ b/tests/resources/adhoc_routing/no_updates/config-1.yml @@ -17,9 +17,4 @@ modules: routing: type: Routing mode: AdHoc - simulation: - type: Simulation - start_time: 0 - end_time: 86400 - sample_size: 1.0 diff --git a/tests/resources/adhoc_routing/no_updates/config-2.yml b/tests/resources/adhoc_routing/no_updates/config-2.yml index 35a3039..c5e1392 100644 --- a/tests/resources/adhoc_routing/no_updates/config-2.yml +++ b/tests/resources/adhoc_routing/no_updates/config-2.yml @@ -17,9 +17,4 @@ modules: routing: type: Routing mode: AdHoc - simulation: - type: Simulation - start_time: 0 - end_time: 86400 - sample_size: 1.0 diff --git a/tests/resources/adhoc_routing/with_updates/config-1.yml b/tests/resources/adhoc_routing/with_updates/config-1.yml index 483db2b..02cc25c 100644 --- a/tests/resources/adhoc_routing/with_updates/config-1.yml +++ b/tests/resources/adhoc_routing/with_updates/config-1.yml @@ -17,9 +17,4 @@ modules: routing: type: Routing mode: AdHoc - simulation: - type: Simulation - start_time: 0 - end_time: 86400 - sample_size: 1.0 diff --git a/tests/resources/adhoc_routing/with_updates/config-2.yml b/tests/resources/adhoc_routing/with_updates/config-2.yml index 0f4019c..1af0299 100644 --- a/tests/resources/adhoc_routing/with_updates/config-2.yml +++ b/tests/resources/adhoc_routing/with_updates/config-2.yml @@ -18,9 +18,4 @@ modules: routing: type: Routing mode: AdHoc - simulation: - type: Simulation - start_time: 0 - end_time: 86400 - sample_size: 1.0 diff --git a/tests/test_simulation.rs b/tests/test_simulation.rs index 8029de7..2c4a014 100644 --- a/tests/test_simulation.rs +++ b/tests/test_simulation.rs @@ -98,8 +98,7 @@ pub fn execute_sim( let mut garage = Garage::from_file(&PathBuf::from(config.proto_files().vehicles)); let population: Population = Population::from_file(&temp_population_file, &mut garage); - let sim_net = - SimNetworkPartition::from_network(&network, rank, config.simulation().sample_size); + let sim_net = SimNetworkPartition::from_network(&network, rank, config.simulation()); let mut events = EventsPublisher::new(); events.add_subscriber(test_subscriber);