diff --git a/sim/src/make/load.rs b/sim/src/make/load.rs index 607598c287..7076da2633 100644 --- a/sim/src/make/load.rs +++ b/sim/src/make/load.rs @@ -114,7 +114,7 @@ impl SimFlags { opts.run_name = scenario.scenario_name.clone(); } let mut sim = Sim::new(&map, opts); - scenario.instantiate(&mut sim, &map, &mut rng, timer); + sim.instantiate(&scenario, &map, &mut rng, timer); (map, sim, rng) } else if self.load.contains("/raw_maps/") || self.load.contains("/maps/") { diff --git a/sim/src/make/mod.rs b/sim/src/make/mod.rs index 1af17e94a7..976352588b 100644 --- a/sim/src/make/mod.rs +++ b/sim/src/make/mod.rs @@ -11,7 +11,6 @@ pub(crate) use self::spawner::{StartTripArgs, TripSpec}; mod activity_model; mod generator; mod load; -mod scenario; mod spawner; /// Need to explain this trick -- basically keeps consistency between two different simulations when diff --git a/sim/src/sim/mod.rs b/sim/src/sim/mod.rs index 6527bf7072..7794dba0e7 100644 --- a/sim/src/sim/mod.rs +++ b/sim/src/sim/mod.rs @@ -28,6 +28,7 @@ use crate::{ }; mod queries; +mod scenario; // TODO Do something else. const BLIND_RETRY_TO_SPAWN: Duration = Duration::const_seconds(5.0); diff --git a/sim/src/make/scenario.rs b/sim/src/sim/scenario.rs similarity index 67% rename from sim/src/make/scenario.rs rename to sim/src/sim/scenario.rs index 14cec37afe..132daa6a13 100644 --- a/sim/src/make/scenario.rs +++ b/sim/src/sim/scenario.rs @@ -1,6 +1,7 @@ use std::collections::{BTreeMap, HashSet, VecDeque}; use anyhow::Result; +use rand::seq::SliceRandom; use rand::{Rng, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -9,7 +10,7 @@ use geom::{Distance, Pt2D, Speed}; use map_model::{ BuildingID, Map, OffstreetParking, PathConstraints, PathRequest, Position, RoadID, }; -use synthpop::{Scenario, TripEndpoint, TripMode}; +use synthpop::{PersonSpec, Scenario, TripEndpoint, TripMode}; use crate::make::fork_rng; use crate::{ @@ -17,43 +18,49 @@ use crate::{ VehicleType, BIKE_LENGTH, MAX_CAR_LENGTH, MIN_CAR_LENGTH, }; -impl Scenario { - pub fn instantiate(&self, sim: &mut Sim, map: &Map, rng: &mut XorShiftRng, timer: &mut Timer) { - self.instantiate_without_retries(sim, map, rng, true, timer); +impl Sim { + pub fn instantiate( + &mut self, + scenario: &Scenario, + map: &Map, + rng: &mut XorShiftRng, + timer: &mut Timer, + ) { + self.instantiate_without_retries(scenario, map, rng, true, timer); } /// If retry_if_no_room is false, any vehicles that fail to spawn because of something else in /// the way will just wind up as cancelled trips. pub fn instantiate_without_retries( - &self, - sim: &mut Sim, + &mut self, + scenario: &Scenario, map: &Map, rng: &mut XorShiftRng, retry_if_no_room: bool, timer: &mut Timer, ) { // Any case where map edits could change the calls to the RNG, we have to fork. - sim.set_run_name(self.scenario_name.clone()); + self.set_run_name(scenario.scenario_name.clone()); - timer.start(format!("Instantiating {}", self.scenario_name)); + timer.start(format!("Instantiating {}", scenario.scenario_name)); - if let Some(ref routes) = self.only_seed_buses { + if let Some(ref routes) = scenario.only_seed_buses { for route in map.all_transit_routes() { if routes.contains(&route.long_name) { - sim.seed_bus_route(route); + self.seed_bus_route(route); } } } else { // All of them for route in map.all_transit_routes() { - sim.seed_bus_route(route); + self.seed_bus_route(route); } } - timer.start_iter("trips for People", self.people.len()); + timer.start_iter("trips for People", scenario.people.len()); let mut parked_cars: Vec<(Vehicle, BuildingID)> = Vec::new(); let mut schedule_trips = Vec::new(); - for p in &self.people { + for p in &scenario.people { timer.next(); if let Err(err) = p.check_schedule() { @@ -61,8 +68,8 @@ impl Scenario { } let (vehicle_specs, cars_initially_parked_at, vehicle_foreach_trip) = - p.get_vehicles(rng); - let person = sim.new_person(p.orig_id, Scenario::rand_ped_speed(rng), vehicle_specs); + get_vehicles(p, rng); + let person = self.new_person(p.orig_id, rand_ped_speed(rng), vehicle_specs); for (idx, b) in cars_initially_parked_at { parked_cars.push((person.vehicles[idx].clone(), b)); } @@ -92,151 +99,138 @@ impl Scenario { // parked_cars is stable over map edits, so don't fork. parked_cars.shuffle(rng); - seed_parked_cars(parked_cars, sim, map, rng, timer); + seed_parked_cars(parked_cars, self, map, rng, timer); - sim.spawn_trips(schedule_trips, map, timer); - timer.stop(format!("Instantiating {}", self.scenario_name)); + self.spawn_trips(schedule_trips, map, timer); + timer.stop(format!("Instantiating {}", scenario.scenario_name)); } +} - fn get_vehicles( - &self, - rng: &mut XorShiftRng, - ) -> ( - Vec, - Vec<(usize, BuildingID)>, - Vec>, - ) { - let mut vehicle_specs = Vec::new(); - let mut cars_initially_parked_at = Vec::new(); - let mut vehicle_foreach_trip = Vec::new(); +fn get_vehicles( + person: &PersonSpec, + rng: &mut XorShiftRng, +) -> ( + Vec, + Vec<(usize, BuildingID)>, + Vec>, +) { + let mut vehicle_specs = Vec::new(); + let mut cars_initially_parked_at = Vec::new(); + let mut vehicle_foreach_trip = Vec::new(); - let mut bike_idx = None; - // For each indexed car, is it parked somewhere, or off-map? - let mut car_locations: Vec<(usize, Option)> = Vec::new(); + let mut bike_idx = None; + // For each indexed car, is it parked somewhere, or off-map? + let mut car_locations: Vec<(usize, Option)> = Vec::new(); - // TODO If the trip is cancelled, this should be affected... - for trip in &self.trips { - let use_for_trip = match trip.mode { - TripMode::Walk | TripMode::Transit => None, - TripMode::Bike => { - if bike_idx.is_none() { - bike_idx = Some(vehicle_specs.len()); - vehicle_specs.push(Scenario::rand_bike(rng)); - } - bike_idx + // TODO If the trip is cancelled, this should be affected... + for trip in &person.trips { + let use_for_trip = match trip.mode { + TripMode::Walk | TripMode::Transit => None, + TripMode::Bike => { + if bike_idx.is_none() { + bike_idx = Some(vehicle_specs.len()); + vehicle_specs.push(rand_bike(rng)); } - TripMode::Drive => { - let need_parked_at = match trip.origin { - TripEndpoint::Bldg(b) => Some(b), - _ => None, - }; + bike_idx + } + TripMode::Drive => { + let need_parked_at = match trip.origin { + TripEndpoint::Bldg(b) => Some(b), + _ => None, + }; - // Any available cars in the right spot? - let idx = if let Some(idx) = car_locations - .iter() - .find(|(_, parked_at)| *parked_at == need_parked_at) - .map(|(idx, _)| *idx) - { - idx - } else { - // Need a new car, starting in the right spot - let idx = vehicle_specs.len(); - vehicle_specs.push(Scenario::rand_car(rng)); - if let Some(b) = need_parked_at { - cars_initially_parked_at.push((idx, b)); - } - idx - }; - - // Where does this car wind up? - car_locations.retain(|(i, _)| idx != *i); - match trip.destination { - TripEndpoint::Bldg(b) => { - car_locations.push((idx, Some(b))); - } - TripEndpoint::Border(_) | TripEndpoint::SuddenlyAppear(_) => { - car_locations.push((idx, None)); - } + // Any available cars in the right spot? + let idx = if let Some(idx) = car_locations + .iter() + .find(|(_, parked_at)| *parked_at == need_parked_at) + .map(|(idx, _)| *idx) + { + idx + } else { + // Need a new car, starting in the right spot + let idx = vehicle_specs.len(); + vehicle_specs.push(rand_car(rng)); + if let Some(b) = need_parked_at { + cars_initially_parked_at.push((idx, b)); } + idx + }; - Some(idx) + // Where does this car wind up? + car_locations.retain(|(i, _)| idx != *i); + match trip.destination { + TripEndpoint::Bldg(b) => { + car_locations.push((idx, Some(b))); + } + TripEndpoint::Border(_) | TripEndpoint::SuddenlyAppear(_) => { + car_locations.push((idx, None)); + } } - }; - vehicle_foreach_trip.push(use_for_trip); - } - // For debugging - if false { - let mut n = vehicle_specs.len(); - if bike_idx.is_some() { - n -= 1; + Some(idx) } - if n > 1 { - println!("Someone needs {} cars", n); - } - } - - ( - vehicle_specs, - cars_initially_parked_at, - vehicle_foreach_trip, - ) + }; + vehicle_foreach_trip.push(use_for_trip); } - fn rand_car(rng: &mut XorShiftRng) -> VehicleSpec { - let length = Scenario::rand_dist(rng, MIN_CAR_LENGTH, MAX_CAR_LENGTH); - VehicleSpec { - vehicle_type: VehicleType::Car, - length, - max_speed: None, + // For debugging + if false { + let mut n = vehicle_specs.len(); + if bike_idx.is_some() { + n -= 1; + } + if n > 1 { + println!("Someone needs {} cars", n); } } - fn rand_bike(rng: &mut XorShiftRng) -> VehicleSpec { - let max_speed = Some(Scenario::rand_speed( - rng, - Speed::miles_per_hour(8.0), - map_model::MAX_BIKE_SPEED, - )); - VehicleSpec { - vehicle_type: VehicleType::Bike, - length: BIKE_LENGTH, - max_speed, - } - } + ( + vehicle_specs, + cars_initially_parked_at, + vehicle_foreach_trip, + ) +} - pub fn rand_dist(rng: &mut XorShiftRng, low: Distance, high: Distance) -> Distance { - assert!(high > low); - Distance::meters(rng.gen_range(low.inner_meters()..high.inner_meters())) +fn rand_car(rng: &mut XorShiftRng) -> VehicleSpec { + let length = rand_dist(rng, MIN_CAR_LENGTH, MAX_CAR_LENGTH); + VehicleSpec { + vehicle_type: VehicleType::Car, + length, + max_speed: None, } +} - fn rand_speed(rng: &mut XorShiftRng, low: Speed, high: Speed) -> Speed { - assert!(high > low); - Speed::meters_per_second( - rng.gen_range(low.inner_meters_per_second()..high.inner_meters_per_second()), - ) +fn rand_bike(rng: &mut XorShiftRng) -> VehicleSpec { + let max_speed = Some(rand_speed( + rng, + Speed::miles_per_hour(8.0), + map_model::MAX_BIKE_SPEED, + )); + VehicleSpec { + vehicle_type: VehicleType::Bike, + length: BIKE_LENGTH, + max_speed, } +} - pub fn rand_ped_speed(rng: &mut XorShiftRng) -> Speed { - Scenario::rand_speed( - rng, - Speed::miles_per_hour(2.0), - map_model::MAX_WALKING_SPEED, - ) - } +pub fn rand_dist(rng: &mut XorShiftRng, low: Distance, high: Distance) -> Distance { + assert!(high > low); + Distance::meters(rng.gen_range(low.inner_meters()..high.inner_meters())) +} - pub fn count_parked_cars_per_bldg(&self) -> Counter { - let mut per_bldg = Counter::new(); - // Pass in a dummy RNG - let mut rng = XorShiftRng::seed_from_u64(0); - for p in &self.people { - let (_, cars_initially_parked_at, _) = p.get_vehicles(&mut rng); - for (_, b) in cars_initially_parked_at { - per_bldg.inc(b); - } - } - per_bldg - } +fn rand_speed(rng: &mut XorShiftRng, low: Speed, high: Speed) -> Speed { + assert!(high > low); + Speed::meters_per_second( + rng.gen_range(low.inner_meters_per_second()..high.inner_meters_per_second()), + ) +} + +pub fn rand_ped_speed(rng: &mut XorShiftRng) -> Speed { + rand_speed( + rng, + Speed::miles_per_hour(2.0), + map_model::MAX_WALKING_SPEED, + ) } fn seed_parked_cars( @@ -360,7 +354,20 @@ fn find_spot_near_building( } } -impl TripEndpoint { +pub fn count_parked_cars_per_bldg(scenario: &Scenario) -> Counter { + let mut per_bldg = Counter::new(); + // Pass in a dummy RNG + let mut rng = XorShiftRng::seed_from_u64(0); + for p in &scenario.people { + let (_, cars_initially_parked_at, _) = get_vehicles(p, &mut rng); + for (_, b) in cars_initially_parked_at { + per_bldg.inc(b); + } + } + per_bldg +} + +/*impl TripEndpoint { /// Figure out a single PathRequest that goes between two TripEndpoints. Assume a single mode /// the entire time -- no walking to a car before driving, for instance. The result probably /// won't be exactly what would happen on a real trip between the endpoints because of this @@ -466,4 +473,4 @@ impl TripEndpoint { TripEndpoint::SuddenlyAppear(pos) => pos.pt(map), } } -} +}*/ diff --git a/synthpop/src/scenario.rs b/synthpop/src/scenario.rs index bd224d0cd4..0c0db2ba5b 100644 --- a/synthpop/src/scenario.rs +++ b/synthpop/src/scenario.rs @@ -161,7 +161,7 @@ impl Scenario { impl PersonSpec { /// Verify that a person's trips make sense - fn check_schedule(&self) -> Result<()> { + pub fn check_schedule(&self) -> Result<()> { if self.trips.is_empty() { bail!("Person ({:?}) has no trips at all", self.orig_id); }