use crate::analytics::Window;
use crate::{
AgentID, AgentType, AlertLocation, Analytics, CapSimState, CarID, Command, CreateCar,
DrawCarInput, DrawPedCrowdInput, DrawPedestrianInput, DrivingSimState, Event, GetDrawAgents,
IntersectionSimState, OrigPersonID, PandemicModel, ParkedCar, ParkingSimState, ParkingSpot,
PedestrianID, Person, PersonID, PersonState, Router, Scenario, Scheduler, SidewalkPOI,
SidewalkSpot, TransitSimState, TripID, TripInfo, TripManager, TripPhaseType, TripResult,
TripSpawner, UnzoomedAgent, Vehicle, VehicleSpec, VehicleType, WalkingSimState, BUS_LENGTH,
LIGHT_RAIL_LENGTH, MIN_CAR_LENGTH, SPAWN_DIST,
};
use abstutil::{prettyprint_usize, serialized_size_bytes, Counter, Parallelism, Timer};
use derivative::Derivative;
use geom::{Distance, Duration, PolyLine, Pt2D, Speed, Time};
use instant::Instant;
use map_model::{
BuildingID, BusRoute, BusRouteID, BusStopID, IntersectionID, Lane, LaneID, Map, ParkingLotID,
Path, PathConstraints, PathRequest, Position, RoadID, Traversable,
};
use rand_xorshift::XorShiftRng;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashSet};
use std::panic;
const BLIND_RETRY_TO_SPAWN: Duration = Duration::const_seconds(5.0);
#[derive(Serialize, Deserialize, Clone, Derivative)]
#[derivative(PartialEq)]
pub struct Sim {
driving: DrivingSimState,
parking: ParkingSimState,
walking: WalkingSimState,
intersections: IntersectionSimState,
transit: TransitSimState,
cap: CapSimState,
trips: TripManager,
#[derivative(PartialEq = "ignore")]
#[serde(skip_serializing, skip_deserializing)]
pandemic: Option<PandemicModel>,
scheduler: Scheduler,
time: Time,
pub(crate) map_name: String,
pub(crate) edits_name: String,
#[derivative(PartialEq = "ignore")]
run_name: String,
#[derivative(PartialEq = "ignore")]
step_count: usize,
#[derivative(PartialEq = "ignore")]
#[serde(skip_serializing, skip_deserializing)]
analytics: Analytics,
#[derivative(PartialEq = "ignore")]
#[serde(skip_serializing, skip_deserializing)]
alerts: AlertHandler,
}
#[derive(Clone)]
pub struct SimOptions {
pub run_name: String,
pub use_freeform_policy_everywhere: bool,
pub dont_block_the_box: bool,
pub recalc_lanechanging: bool,
pub break_turn_conflict_cycles: bool,
pub handle_uber_turns: bool,
pub enable_pandemic_model: Option<XorShiftRng>,
pub alerts: AlertHandler,
pub pathfinding_upfront: bool,
}
#[derive(Clone)]
pub enum AlertHandler {
Print,
Block,
Silence,
}
impl std::default::Default for AlertHandler {
fn default() -> AlertHandler {
AlertHandler::Print
}
}
impl SimOptions {
pub fn new(run_name: &str) -> SimOptions {
SimOptions {
run_name: run_name.to_string(),
use_freeform_policy_everywhere: false,
dont_block_the_box: true,
recalc_lanechanging: true,
break_turn_conflict_cycles: true,
handle_uber_turns: true,
enable_pandemic_model: None,
alerts: AlertHandler::Print,
pathfinding_upfront: false,
}
}
}
impl Sim {
pub fn new(map: &Map, opts: SimOptions, timer: &mut Timer) -> Sim {
let mut scheduler = Scheduler::new();
Sim {
driving: DrivingSimState::new(map, opts.recalc_lanechanging, opts.handle_uber_turns),
parking: ParkingSimState::new(map, timer),
walking: WalkingSimState::new(),
intersections: IntersectionSimState::new(
map,
&mut scheduler,
opts.use_freeform_policy_everywhere,
opts.dont_block_the_box,
opts.break_turn_conflict_cycles,
opts.handle_uber_turns,
),
transit: TransitSimState::new(map),
cap: CapSimState::new(map),
trips: TripManager::new(opts.pathfinding_upfront),
pandemic: if let Some(rng) = opts.enable_pandemic_model {
Some(PandemicModel::new(rng))
} else {
None
},
scheduler,
time: Time::START_OF_DAY,
map_name: map.get_name().to_string(),
edits_name: "untitled edits".to_string(),
run_name: opts.run_name,
step_count: 0,
alerts: opts.alerts,
analytics: Analytics::new(),
}
}
pub fn make_spawner(&self) -> TripSpawner {
TripSpawner::new()
}
pub fn flush_spawner(&mut self, spawner: TripSpawner, map: &Map, timer: &mut Timer) {
spawner.finalize(map, &mut self.trips, &mut self.scheduler, timer);
if let Some(ref mut m) = self.pandemic {
m.initialize(self.trips.get_all_people(), &mut self.scheduler);
}
self.dispatch_events(Vec::new(), map);
}
pub fn get_free_onstreet_spots(&self, l: LaneID) -> Vec<ParkingSpot> {
self.parking.get_free_onstreet_spots(l)
}
pub fn get_free_offstreet_spots(&self, b: BuildingID) -> Vec<ParkingSpot> {
self.parking.get_free_offstreet_spots(b)
}
pub fn get_free_lot_spots(&self, pl: ParkingLotID) -> Vec<ParkingSpot> {
self.parking.get_free_lot_spots(pl)
}
pub fn get_all_parking_spots(&self) -> (Vec<ParkingSpot>, Vec<ParkingSpot>) {
self.parking.get_all_parking_spots()
}
pub fn walking_path_to_nearest_parking_spot(
&self,
map: &Map,
b: BuildingID,
) -> Option<(Path, Distance)> {
let vehicle = Vehicle {
id: CarID(0, VehicleType::Car),
owner: None,
vehicle_type: VehicleType::Car,
length: MIN_CAR_LENGTH,
max_speed: None,
};
let driving_lane = map.find_driving_lane_near_building(b);
let spot = if let Some((spot, _)) = self
.parking
.get_all_free_spots(Position::start(driving_lane), &vehicle, b, map)
.get(0)
{
spot.clone()
} else {
let (_, spot, _) =
self.parking
.path_to_free_parking_spot(driving_lane, &vehicle, b, map)?;
spot
};
let start = SidewalkSpot::building(b, map).sidewalk_pos;
let end = SidewalkSpot::parking_spot(spot, map, &self.parking).sidewalk_pos;
let path = map.pathfind(PathRequest {
start,
end,
constraints: PathConstraints::Pedestrian,
})?;
Some((path, start.dist_along()))
}
pub(crate) fn new_person(
&mut self,
p: PersonID,
orig_id: Option<OrigPersonID>,
ped_speed: Speed,
vehicle_specs: Vec<VehicleSpec>,
) {
self.trips.new_person(p, orig_id, ped_speed, vehicle_specs);
}
pub fn random_person(&mut self, ped_speed: Speed, vehicle_specs: Vec<VehicleSpec>) -> &Person {
self.trips.random_person(ped_speed, vehicle_specs)
}
pub(crate) fn seed_parked_car(&mut self, vehicle: Vehicle, spot: ParkingSpot) {
self.parking.reserve_spot(spot);
self.parking.add_parked_car(ParkedCar { vehicle, spot });
}
pub(crate) fn seed_bus_route(&mut self, route: &BusRoute) {
for t in &route.spawn_times {
self.scheduler.push(*t, Command::StartBus(route.id, *t));
}
}
fn start_bus(&mut self, route: &BusRoute, map: &Map) {
let (req, path) = self.transit.create_empty_route(route, map);
let (vehicle_type, length) = match route.route_type {
PathConstraints::Bus => (VehicleType::Bus, BUS_LENGTH),
PathConstraints::Train => (VehicleType::Train, LIGHT_RAIL_LENGTH),
_ => unreachable!(),
};
let vehicle = VehicleSpec {
vehicle_type,
length,
max_speed: None,
}
.make(CarID(self.trips.new_car_id(), vehicle_type), None);
let start_lane = map.get_l(path.current_step().as_lane());
let start_dist = if map.get_i(start_lane.src_i).is_incoming_border() {
SPAWN_DIST
} else {
assert!(start_lane.length() > vehicle.length);
vehicle.length
};
self.scheduler.push(
self.time,
Command::SpawnCar(
CreateCar {
start_dist,
router: Router::follow_bus_route(
vehicle.id,
path.clone(),
req.end.dist_along(),
),
vehicle,
req,
maybe_parked_car: None,
trip_and_person: None,
maybe_route: Some(route.id),
},
true,
),
);
}
pub fn set_name(&mut self, name: String) {
self.run_name = name;
}
}
impl GetDrawAgents for Sim {
fn time(&self) -> Time {
self.time
}
fn step_count(&self) -> usize {
self.step_count
}
fn get_draw_car(&self, id: CarID, map: &Map) -> Option<DrawCarInput> {
self.parking.get_draw_car(id, map).or_else(|| {
self.driving
.get_single_draw_car(id, self.time, map, &self.transit)
})
}
fn get_draw_ped(&self, id: PedestrianID, map: &Map) -> Option<DrawPedestrianInput> {
self.walking.get_draw_ped(id, self.time, map)
}
fn get_draw_cars(&self, on: Traversable, map: &Map) -> Vec<DrawCarInput> {
let mut results = Vec::new();
if let Traversable::Lane(l) = on {
if map.get_l(l).is_parking() {
return self.parking.get_draw_cars(l, map);
}
results.extend(self.parking.get_draw_cars_in_lots(l, map));
}
results.extend(
self.driving
.get_draw_cars_on(self.time, on, map, &self.transit),
);
results
}
fn get_draw_peds(
&self,
on: Traversable,
map: &Map,
) -> (Vec<DrawPedestrianInput>, Vec<DrawPedCrowdInput>) {
self.walking.get_draw_peds_on(self.time, on, map)
}
fn get_all_draw_cars(&self, map: &Map) -> Vec<DrawCarInput> {
let mut result = self
.driving
.get_all_draw_cars(self.time, map, &self.transit);
result.extend(self.parking.get_all_draw_cars(map));
result
}
fn get_all_draw_peds(&self, map: &Map) -> Vec<DrawPedestrianInput> {
self.walking.get_all_draw_peds(self.time, map)
}
fn get_unzoomed_agents(&self, map: &Map) -> Vec<UnzoomedAgent> {
let mut result = self.driving.get_unzoomed_agents(self.time, map);
result.extend(self.walking.get_unzoomed_agents(self.time, map));
result
}
}
impl Sim {
fn minimal_step(
&mut self,
map: &Map,
max_dt: Duration,
maybe_cb: &mut Option<Box<dyn SimCallback>>,
) -> bool {
self.step_count += 1;
let max_time = if let Some(t) = self.scheduler.peek_next_time() {
if t > self.time + max_dt {
self.time += max_dt;
return false;
}
t
} else {
self.time += max_dt;
return false;
};
let mut halt = false;
while let Some(time) = self.scheduler.peek_next_time() {
if time > max_time {
return false;
}
if let Some(cmd) = self.scheduler.get_next() {
if self.do_step(map, time, cmd, maybe_cb) {
halt = true;
break;
}
}
}
halt
}
fn do_step(
&mut self,
map: &Map,
time: Time,
cmd: Command,
maybe_cb: &mut Option<Box<dyn SimCallback>>,
) -> bool {
self.time = time;
let mut events = Vec::new();
let mut halt = false;
match cmd {
Command::StartTrip(id, trip_spec, maybe_req, maybe_path) => {
self.trips.start_trip(
self.time,
id,
trip_spec,
maybe_req,
maybe_path,
&mut self.parking,
&self.cap,
&mut self.scheduler,
map,
);
}
Command::SpawnCar(create_car, retry_if_no_room) => {
if self.driving.start_car_on_lane(
self.time,
create_car.clone(),
map,
&self.intersections,
&self.parking,
&mut self.scheduler,
) {
if let Some((trip, person)) = create_car.trip_and_person {
self.trips
.agent_starting_trip_leg(AgentID::Car(create_car.vehicle.id), trip);
events.push(Event::TripPhaseStarting(
trip,
person,
Some(create_car.req.clone()),
if create_car.vehicle.id.1 == VehicleType::Car {
TripPhaseType::Driving
} else {
TripPhaseType::Biking
},
));
}
if let Some(parked_car) = create_car.maybe_parked_car {
if let ParkingSpot::Offstreet(b, _) = parked_car.spot {
events.push(Event::PersonLeavesBuilding(
create_car.trip_and_person.unwrap().1,
b,
));
}
self.parking.remove_parked_car(parked_car);
}
if let Some(route) = create_car.maybe_route {
self.transit.bus_created(create_car.vehicle.id, route);
}
self.analytics
.record_demand(create_car.router.get_path(), map);
} else if retry_if_no_room {
self.scheduler.push(
self.time + BLIND_RETRY_TO_SPAWN,
Command::SpawnCar(create_car, retry_if_no_room),
);
} else {
let (trip, person) = create_car.trip_and_person.unwrap();
println!(
"No room to spawn car for {} by {}. Not retrying!",
trip, person
);
self.trips.abort_trip(
self.time,
trip,
Some(create_car.vehicle),
&mut self.parking,
&self.cap,
&mut self.scheduler,
map,
);
}
}
Command::SpawnPed(create_ped) => {
self.trips
.agent_starting_trip_leg(AgentID::Pedestrian(create_ped.id), create_ped.trip);
events.push(Event::TripPhaseStarting(
create_ped.trip,
create_ped.person,
Some(create_ped.req.clone()),
TripPhaseType::Walking,
));
self.analytics.record_demand(&create_ped.path, map);
match (&create_ped.start.connection, &create_ped.goal.connection) {
(
SidewalkPOI::Building(b1),
SidewalkPOI::ParkingSpot(ParkingSpot::Offstreet(b2, idx)),
) if b1 == b2 => {
self.trips.ped_reached_parking_spot(
self.time,
create_ped.id,
ParkingSpot::Offstreet(*b2, *idx),
Duration::ZERO,
map,
&mut self.parking,
&self.cap,
&mut self.scheduler,
);
}
_ => {
if let SidewalkPOI::Building(b) = &create_ped.start.connection {
events.push(Event::PersonLeavesBuilding(create_ped.person, *b));
}
self.walking
.spawn_ped(self.time, create_ped, map, &mut self.scheduler);
}
}
}
Command::UpdateCar(car) => {
self.driving.update_car(
car,
self.time,
map,
&mut self.parking,
&mut self.intersections,
&mut self.trips,
&mut self.scheduler,
&mut self.transit,
&mut self.cap,
&mut self.walking,
);
}
Command::UpdateLaggyHead(car) => {
self.driving.update_laggy_head(
car,
self.time,
map,
&mut self.intersections,
&mut self.scheduler,
);
}
Command::UpdatePed(ped) => {
self.walking.update_ped(
ped,
self.time,
map,
&mut self.intersections,
&mut self.parking,
&mut self.scheduler,
&mut self.trips,
&mut self.transit,
&self.cap,
);
}
Command::UpdateIntersection(i) => {
self.intersections
.update_intersection(self.time, i, map, &mut self.scheduler);
}
Command::Callback(frequency) => {
self.scheduler
.push(self.time + frequency, Command::Callback(frequency));
if maybe_cb.as_mut().unwrap().run(self, map) {
halt = true;
}
}
Command::Pandemic(cmd) => {
self.pandemic
.as_mut()
.unwrap()
.handle_cmd(self.time, cmd, &mut self.scheduler);
}
Command::FinishRemoteTrip(trip) => {
self.trips.remote_trip_finished(
self.time,
trip,
map,
&mut self.parking,
&self.cap,
&mut self.scheduler,
);
}
Command::StartBus(r, _) => {
self.start_bus(map.get_br(r), map);
}
}
self.dispatch_events(events, map);
halt
}
fn dispatch_events(&mut self, mut events: Vec<Event>, map: &Map) {
events.extend(self.trips.collect_events());
events.extend(self.transit.collect_events());
events.extend(self.driving.collect_events());
events.extend(self.walking.collect_events());
events.extend(self.intersections.collect_events());
events.extend(self.parking.collect_events());
for ev in events {
if let Some(ref mut m) = self.pandemic {
m.handle_event(self.time, &ev, &mut self.scheduler);
}
self.analytics.event(ev, self.time, map);
}
}
pub fn timed_step(
&mut self,
map: &Map,
dt: Duration,
maybe_cb: &mut Option<Box<dyn SimCallback>>,
timer: &mut Timer,
) {
let end_time = self.time + dt;
let start = Instant::now();
let mut last_update = Instant::now();
timer.start(format!("Advance sim to {}", end_time));
while self.time < end_time {
if self.minimal_step(map, end_time - self.time, maybe_cb) {
break;
}
if !self.analytics.alerts.is_empty() {
match self.alerts {
AlertHandler::Print => {
for (t, loc, msg) in self.analytics.alerts.drain(..) {
println!("Alert at {} ({:?}): {}", t, loc, msg);
}
}
AlertHandler::Block => {
for (t, loc, msg) in &self.analytics.alerts {
println!("Alert at {} ({:?}): {}", t, loc, msg);
}
break;
}
AlertHandler::Silence => {
self.analytics.alerts.clear();
}
}
}
if Duration::realtime_elapsed(last_update) >= Duration::seconds(1.0) {
println!(
"- After {}, the sim is at {}. {} live agents",
Duration::realtime_elapsed(start),
self.time,
prettyprint_usize(self.trips.num_active_agents()),
);
last_update = Instant::now();
}
}
timer.stop(format!("Advance sim to {}", end_time));
}
pub fn tiny_step(&mut self, map: &Map, maybe_cb: &mut Option<Box<dyn SimCallback>>) {
self.timed_step(
map,
Duration::seconds(0.1),
maybe_cb,
&mut Timer::throwaway(),
);
}
pub fn time_limited_step(
&mut self,
map: &Map,
dt: Duration,
real_time_limit: Duration,
maybe_cb: &mut Option<Box<dyn SimCallback>>,
) {
let started_at = Instant::now();
let end_time = self.time + dt;
while self.time < end_time && Duration::realtime_elapsed(started_at) < real_time_limit {
if self.minimal_step(map, end_time - self.time, maybe_cb) {
break;
}
if !self.analytics.alerts.is_empty() {
match self.alerts {
AlertHandler::Print => {
for (t, loc, msg) in self.analytics.alerts.drain(..) {
println!("Alert at {} ({:?}): {}", t, loc, msg);
}
}
AlertHandler::Block => {
for (t, loc, msg) in &self.analytics.alerts {
println!("Alert at {} ({:?}): {}", t, loc, msg);
}
break;
}
AlertHandler::Silence => {
self.analytics.alerts.clear();
}
}
}
}
}
pub fn dump_before_abort(&self) {
println!("At {}", self.time);
if let Some(path) = self.find_previous_savestate(self.time) {
println!("Debug from {}", path);
}
}
}
impl Sim {
pub fn run_until_done<F: Fn(&mut Sim, &Map)>(
&mut self,
map: &Map,
callback: F,
time_limit: Option<Duration>,
) {
let mut last_print = Instant::now();
let mut last_sim_time = self.time();
loop {
let dt = time_limit.unwrap_or_else(|| Duration::seconds(30.0));
match panic::catch_unwind(panic::AssertUnwindSafe(|| {
self.timed_step(map, dt, &mut None, &mut Timer::throwaway());
})) {
Ok(()) => {}
Err(err) => {
println!(
"*************************************************************************\
*******"
);
println!("Sim broke:");
self.dump_before_abort();
panic::resume_unwind(err);
}
}
let dt_real = Duration::realtime_elapsed(last_print);
if dt_real >= Duration::seconds(1.0) {
let (finished, unfinished) = self.num_trips();
println!(
"{}: {} trips finished, {} unfinished, speed = {:.2}x, {}",
self.time(),
prettyprint_usize(finished),
prettyprint_usize(unfinished),
(self.time() - last_sim_time) / dt_real,
self.scheduler.describe_stats()
);
last_print = Instant::now();
last_sim_time = self.time();
}
callback(self, map);
if self.is_done() {
println!(
"{}: speed = {:.2}x, {}",
self.time(),
(self.time() - last_sim_time) / dt_real,
self.scheduler.describe_stats()
);
break;
}
if let Some(lim) = time_limit {
panic!("Time limit {} hit", lim);
}
}
}
}
impl Sim {
pub fn save_dir(&self) -> String {
abstutil::path_all_saves(&self.map_name, &self.edits_name, &self.run_name)
}
fn save_path(&self, base_time: Time) -> String {
abstutil::path_save(
&self.map_name,
&self.edits_name,
&self.run_name,
base_time.as_filename(),
)
}
pub fn save(&mut self) -> String {
let restore = self.scheduler.before_savestate();
if true {
println!("sim savestate breakdown:");
println!(
"- driving: {} bytes",
prettyprint_usize(serialized_size_bytes(&self.driving))
);
println!(
"- parking: {} bytes",
prettyprint_usize(serialized_size_bytes(&self.parking))
);
println!(
"- walking: {} bytes",
prettyprint_usize(serialized_size_bytes(&self.walking))
);
println!(
"- intersections: {} bytes",
prettyprint_usize(serialized_size_bytes(&self.intersections))
);
println!(
"- transit: {} bytes",
prettyprint_usize(serialized_size_bytes(&self.transit))
);
println!(
"- cap: {} bytes",
prettyprint_usize(serialized_size_bytes(&self.cap))
);
println!(
"- trips: {} bytes",
prettyprint_usize(serialized_size_bytes(&self.trips))
);
println!(
"- scheduler: {} bytes",
prettyprint_usize(serialized_size_bytes(&self.scheduler))
);
}
let path = self.save_path(self.time);
abstutil::write_binary(path.clone(), self);
self.scheduler.after_savestate(restore);
path
}
pub fn find_previous_savestate(&self, base_time: Time) -> Option<String> {
abstutil::find_prev_file(self.save_path(base_time))
}
pub fn find_next_savestate(&self, base_time: Time) -> Option<String> {
abstutil::find_next_file(self.save_path(base_time))
}
pub fn load_savestate(
path: String,
map: &Map,
timer: &mut Timer,
) -> Result<Sim, std::io::Error> {
let mut sim: Sim = abstutil::maybe_read_binary(path, timer)?;
sim.restore_paths(map, timer);
Ok(sim)
}
pub fn restore_paths(&mut self, map: &Map, timer: &mut Timer) {
let paths = timer.parallelize(
"calculate paths",
Parallelism::Fastest,
self.scheduler.get_requests_for_savestate(),
|req| map.pathfind(req).unwrap(),
);
self.scheduler.after_savestate(paths);
}
pub fn handle_live_edited_traffic_signals(&mut self, map: &Map) {
self.intersections.handle_live_edited_traffic_signals(map)
}
}
impl Sim {
pub fn time(&self) -> Time {
self.time
}
pub fn is_done(&self) -> bool {
self.trips.is_done()
}
pub fn is_empty(&self) -> bool {
self.time == Time::START_OF_DAY && self.is_done()
}
pub fn num_trips(&self) -> (usize, usize) {
self.trips.num_trips()
}
pub fn num_agents(&self) -> Counter<AgentType> {
self.trips.num_agents(&self.transit)
}
pub fn num_ppl(&self) -> (usize, usize, usize) {
self.trips.num_ppl()
}
pub fn debug_ped(&self, id: PedestrianID) {
self.walking.debug_ped(id);
self.trips.debug_trip(AgentID::Pedestrian(id));
}
pub fn debug_car(&self, id: CarID) {
self.driving.debug_car(id);
self.trips.debug_trip(AgentID::Car(id));
}
pub fn debug_intersection(&self, id: IntersectionID, map: &Map) {
self.intersections.debug(id, map);
}
pub fn debug_lane(&self, id: LaneID) {
self.driving.debug_lane(id);
}
pub fn agent_properties(&self, id: AgentID) -> AgentProperties {
match id {
AgentID::Pedestrian(id) => self.walking.agent_properties(id, self.time),
AgentID::Car(id) => self.driving.agent_properties(id, self.time),
AgentID::BusPassenger(_, _) => AgentProperties {
total_time: Duration::ZERO,
waiting_here: Duration::ZERO,
total_waiting: Duration::ZERO,
dist_crossed: Distance::ZERO,
total_dist: Distance::meters(0.1),
lanes_crossed: 0,
total_lanes: 0,
},
}
}
pub fn num_transit_passengers(&self, car: CarID) -> usize {
self.transit.get_passengers(car).len()
}
pub fn bus_route_id(&self, maybe_bus: CarID) -> Option<BusRouteID> {
if maybe_bus.1 == VehicleType::Bus || maybe_bus.1 == VehicleType::Train {
Some(self.transit.bus_route(maybe_bus))
} else {
None
}
}
pub fn active_agents(&self) -> Vec<AgentID> {
self.trips.active_agents()
}
pub fn agent_to_trip(&self, id: AgentID) -> Option<TripID> {
self.trips.agent_to_trip(id)
}
pub fn trip_to_agent(&self, id: TripID) -> TripResult<AgentID> {
self.trips.trip_to_agent(id)
}
pub fn trip_info(&self, id: TripID) -> TripInfo {
self.trips.trip_info(id)
}
pub fn all_trip_info(&self) -> Vec<(TripID, TripInfo)> {
self.trips.all_trip_info()
}
pub fn finished_trip_time(&self, id: TripID) -> Option<(Duration, Duration)> {
self.trips.finished_trip_time(id)
}
pub fn trip_to_person(&self, id: TripID) -> PersonID {
self.trips.trip_to_person(id)
}
pub fn agent_to_person(&self, id: AgentID) -> Option<PersonID> {
self.agent_to_trip(id).map(|t| self.trip_to_person(t))
}
pub fn get_owner_of_car(&self, id: CarID) -> Option<PersonID> {
self.driving
.get_owner_of_car(id)
.or_else(|| self.parking.get_owner_of_car(id))
}
pub fn lookup_parked_car(&self, id: CarID) -> Option<&ParkedCar> {
self.parking.lookup_parked_car(id)
}
pub fn lookup_person(&self, id: PersonID) -> Option<&Person> {
self.trips.get_person(id)
}
pub fn get_person(&self, id: PersonID) -> &Person {
self.trips.get_person(id).unwrap()
}
pub fn find_person_by_orig_id(&self, id: OrigPersonID) -> Option<PersonID> {
for p in self.get_all_people() {
if p.orig_id == Some(id) {
return Some(p.id);
}
}
None
}
pub fn get_all_people(&self) -> &Vec<Person> {
self.trips.get_all_people()
}
pub fn lookup_car_id(&self, idx: usize) -> Option<CarID> {
for vt in &[
VehicleType::Car,
VehicleType::Bike,
VehicleType::Bus,
VehicleType::Train,
] {
let id = CarID(idx, *vt);
if self.driving.does_car_exist(id) {
return Some(id);
}
}
let id = CarID(idx, VehicleType::Car);
if self.parking.lookup_parked_car(id).is_some() {
return Some(id);
}
None
}
pub fn get_path(&self, id: AgentID) -> Option<&Path> {
match id {
AgentID::Car(car) => self.driving.get_path(car),
AgentID::Pedestrian(ped) => self.walking.get_path(ped),
AgentID::BusPassenger(_, _) => None,
}
}
pub fn get_all_driving_paths(&self) -> Vec<&Path> {
self.driving.get_all_driving_paths()
}
pub fn trace_route(
&self,
id: AgentID,
map: &Map,
dist_ahead: Option<Distance>,
) -> Option<PolyLine> {
match id {
AgentID::Car(car) => self.driving.trace_route(self.time, car, map, dist_ahead),
AgentID::Pedestrian(ped) => self.walking.trace_route(self.time, ped, map, dist_ahead),
AgentID::BusPassenger(_, _) => None,
}
}
pub fn get_canonical_pt_per_trip(&self, trip: TripID, map: &Map) -> TripResult<Pt2D> {
let agent = match self.trips.trip_to_agent(trip) {
TripResult::Ok(a) => a,
x => {
return x.propagate_error();
}
};
if let Some(pt) = self.canonical_pt_for_agent(agent, map) {
return TripResult::Ok(pt);
}
TripResult::ModeChange
}
pub fn get_canonical_pt_per_person(&self, p: PersonID, map: &Map) -> Option<Pt2D> {
match self.trips.get_person(p)?.state {
PersonState::Inside(b) => Some(map.get_b(b).polygon.center()),
PersonState::Trip(t) => self.get_canonical_pt_per_trip(t, map).ok(),
PersonState::OffMap => None,
}
}
pub fn canonical_pt_for_agent(&self, id: AgentID, map: &Map) -> Option<Pt2D> {
match id {
AgentID::Car(id) => self
.parking
.canonical_pt(id, map)
.or_else(|| Some(self.get_draw_car(id, map)?.body.last_pt())),
AgentID::Pedestrian(id) => Some(self.get_draw_ped(id, map)?.pos),
AgentID::BusPassenger(_, bus) => Some(self.get_draw_car(bus, map)?.body.last_pt()),
}
}
pub fn get_accepted_agents(&self, id: IntersectionID) -> HashSet<AgentID> {
self.intersections.get_accepted_agents(id)
}
pub fn get_blocked_by(&self, a: AgentID) -> HashSet<AgentID> {
self.intersections.get_blocked_by(a)
}
pub fn status_of_buses(
&self,
route: BusRouteID,
map: &Map,
) -> Vec<(CarID, Option<usize>, f64, Pt2D)> {
let mut results = Vec::new();
for (bus, stop_idx) in self.transit.buses_for_route(route) {
results.push((
bus,
stop_idx,
self.driving.percent_along_route(bus),
self.canonical_pt_for_agent(AgentID::Car(bus), map).unwrap(),
));
}
results
}
pub fn get_analytics(&self) -> &Analytics {
&self.analytics
}
pub fn find_blockage_front(&self, car: CarID, map: &Map) -> String {
self.driving
.find_blockage_front(car, map, &self.intersections)
}
pub fn delayed_intersections(&self, threshold: Duration) -> Vec<(IntersectionID, Time)> {
self.intersections
.delayed_intersections(self.time, threshold)
}
pub fn bldg_to_people(&self, b: BuildingID) -> Vec<PersonID> {
self.trips.bldg_to_people(b)
}
pub fn worst_delay(
&self,
map: &Map,
) -> (
BTreeMap<RoadID, Duration>,
BTreeMap<IntersectionID, Duration>,
) {
self.intersections.worst_delay(self.time, map)
}
pub fn get_pandemic_model(&self) -> Option<&PandemicModel> {
self.pandemic.as_ref()
}
pub fn get_end_of_day(&self) -> Time {
self.scheduler
.get_last_time()
.max(Time::START_OF_DAY + Duration::hours(24))
}
pub fn current_phase_and_remaining_time(&self, i: IntersectionID) -> (usize, Duration) {
self.intersections
.current_phase_and_remaining_time(self.time, i)
}
pub fn all_arrivals_at_border(
&self,
i: IntersectionID,
) -> Vec<(AgentType, Vec<(Time, usize)>)> {
let window_size = Duration::hours(1);
let mut pts_per_type: BTreeMap<AgentType, Vec<(Time, usize)>> = BTreeMap::new();
let mut windows_per_type: BTreeMap<AgentType, Window> = BTreeMap::new();
for agent_type in AgentType::all() {
pts_per_type.insert(agent_type, vec![(Time::START_OF_DAY, 0)]);
windows_per_type.insert(agent_type, Window::new(window_size));
}
for (t, agent_type) in self.trips.all_arrivals_at_border(i) {
let count = windows_per_type.get_mut(&agent_type).unwrap().add(t);
pts_per_type.get_mut(&agent_type).unwrap().push((t, count));
}
for (agent_type, pts) in pts_per_type.iter_mut() {
let mut window = windows_per_type.remove(agent_type).unwrap();
let end = self.get_end_of_day();
let t = (pts.last().unwrap().0 + window_size + Duration::seconds(0.1)).min(end);
if pts.last().unwrap().0 != t {
pts.push((t, window.count(t)));
}
if pts.last().unwrap().0 != end {
pts.push((end, window.count(end)));
}
}
pts_per_type.into_iter().collect()
}
pub fn target_lane_penalty(&self, lane: &Lane) -> (usize, usize) {
if lane.is_walkable() {
(0, 0)
} else {
self.driving.target_lane_penalty(lane.id)
}
}
pub fn get_people_waiting_at_stop(
&self,
at: BusStopID,
) -> &Vec<(PedestrianID, BusRouteID, Option<BusStopID>, Time)> {
self.transit.get_people_waiting_at_stop(at)
}
pub fn generate_scenario(&self, map: &Map, name: String) -> Scenario {
self.trips.generate_scenario(map, name)
}
}
impl Sim {
pub fn kill_stuck_car(&mut self, id: CarID, map: &Map) {
if let Some(trip) = self.agent_to_trip(AgentID::Car(id)) {
let vehicle = self.driving.kill_stuck_car(
id,
self.time,
map,
&mut self.scheduler,
&mut self.intersections,
);
self.trips.abort_trip(
self.time,
trip,
Some(vehicle),
&mut self.parking,
&self.cap,
&mut self.scheduler,
map,
);
println!("Forcibly killed {}", id);
} else {
println!("{} has no trip?!", id);
}
}
pub fn clear_alerts(&mut self) -> Vec<(Time, AlertLocation, String)> {
std::mem::replace(&mut self.analytics.alerts, Vec::new())
}
}
pub trait SimCallback: downcast_rs::Downcast {
fn run(&mut self, sim: &Sim, map: &Map) -> bool;
}
downcast_rs::impl_downcast!(SimCallback);
impl Sim {
pub fn set_periodic_callback(&mut self, frequency: Duration) {
self.scheduler
.push(self.time + frequency, Command::Callback(frequency));
}
pub fn unset_periodic_callback(&mut self) {
self.scheduler
.cancel(Command::Callback(Duration::seconds(1.0)));
}
}
pub struct AgentProperties {
pub total_time: Duration,
pub waiting_here: Duration,
pub total_waiting: Duration,
pub dist_crossed: Distance,
pub total_dist: Distance,
pub lanes_crossed: usize,
pub total_lanes: usize,
}