sim/
lib.rs

1//! The sim crate runs a traffic simulation on top of the map_model. See also
2//! https://a-b-street.github.io/docs/tech/trafficsim/index.html.
3//!
4//! The simulation is very roughly layered into two pieces: the low-level "mechanics" of simulating
5//! individual agents over time, and higher-level systems like TripManager and TransitSimState that
6//! glue together individual goals executed by the agents.
7//!
8//! Helpful terminology:
9//! - sov = single occupancy vehicle, a car with just a driver and no passengers. (Car passengers
10//!   are not currently modelled)
11
12// Disable some noisy clippy warnings
13#![allow(clippy::type_complexity, clippy::too_many_arguments)]
14
15#[macro_use]
16extern crate anyhow;
17#[macro_use]
18extern crate log;
19
20use std::fmt;
21
22use serde::{Deserialize, Serialize};
23
24use abstutil::{deserialize_usize, serialize_usize};
25use geom::{Distance, Speed, Time};
26use map_model::{
27    BuildingID, IntersectionID, LaneID, Map, ParkingLotID, Path, PathConstraints, Position,
28    TransitRouteID, TransitStopID,
29};
30use synthpop::TripEndpoint;
31
32pub use crate::render::{
33    CarStatus, DrawCarInput, DrawPedCrowdInput, DrawPedestrianInput, Intent, PedCrowdLocation,
34    UnzoomedAgent,
35};
36
37pub use self::analytics::{Analytics, Problem, ProblemType, SlidingWindow, TripPhase};
38pub(crate) use self::events::Event;
39pub use self::events::{AlertLocation, TripPhaseType};
40pub use self::make::SimFlags;
41pub(crate) use self::make::{StartTripArgs, TripSpec};
42pub(crate) use self::mechanics::{
43    DrivingSimState, IntersectionSimState, ParkingSim, ParkingSimState, WalkingSimState,
44};
45pub(crate) use self::pandemic::PandemicModel;
46pub use self::prebake::PrebakeSummary;
47pub(crate) use self::recorder::TrafficRecorder;
48pub(crate) use self::router::{ActionAtEnd, Router};
49pub(crate) use self::scheduler::{Command, Scheduler};
50pub use self::sim::{
51    count_parked_cars_per_bldg, rand_dist, AgentProperties, AlertHandler, DelayCause, Sim,
52    SimCallback, SimOptions,
53};
54pub(crate) use self::transit::TransitSimState;
55pub use self::trips::{CommutersVehiclesCounts, Person, PersonState, TripInfo, TripResult};
56pub(crate) use self::trips::{TripLeg, TripManager};
57pub use synthpop::make::{fork_rng, BorderSpawnOverTime, ScenarioGenerator, SpawnOverTime};
58
59mod analytics;
60mod events;
61mod make;
62mod mechanics;
63mod pandemic;
64pub mod prebake;
65mod recorder;
66mod render;
67mod router;
68mod scheduler;
69mod sim;
70mod transit;
71mod trips;
72
73// http://pccsc.net/bicycle-parking-info/ says 68 inches, which is 1.73m
74pub(crate) const BIKE_LENGTH: Distance = Distance::const_meters(1.8);
75pub(crate) const MIN_CAR_LENGTH: Distance = Distance::const_meters(4.5);
76pub(crate) const MAX_CAR_LENGTH: Distance = Distance::const_meters(6.5);
77// Note this is more than MAX_CAR_LENGTH
78pub(crate) const BUS_LENGTH: Distance = Distance::const_meters(12.5);
79pub(crate) const LIGHT_RAIL_LENGTH: Distance = Distance::const_meters(60.0);
80
81/// At all speeds (including at rest), cars must be at least this far apart, measured from front of
82/// one car to the back of the other.
83pub(crate) const FOLLOWING_DISTANCE: Distance = Distance::const_meters(1.0);
84
85/// When spawning at borders, start the front of the vehicle this far along and gradually appear.
86/// Getting too close to EPSILON_DIST can lead to get_draw_car having no geometry at all.
87pub(crate) const SPAWN_DIST: Distance = Distance::const_meters(0.05);
88
89// TODO Implement Eq, Hash, Ord manually to guarantee this.
90#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
91pub struct CarID {
92    /// The numeric ID must be globally unique, without considering VehicleType.
93    #[serde(
94        serialize_with = "serialize_usize",
95        deserialize_with = "deserialize_usize"
96    )]
97    pub id: usize,
98    /// VehicleType is bundled for convenience; many places need to know this without a lookup.
99    pub vehicle_type: VehicleType,
100}
101
102impl fmt::Display for CarID {
103    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104        match self.vehicle_type {
105            VehicleType::Car => write!(f, "Car #{}", self.id),
106            VehicleType::Bus => write!(f, "Bus #{}", self.id),
107            VehicleType::Train => write!(f, "Train #{}", self.id),
108            VehicleType::Bike => write!(f, "Bike #{}", self.id),
109        }
110    }
111}
112
113#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
114pub struct PedestrianID(
115    #[serde(
116        serialize_with = "serialize_usize",
117        deserialize_with = "deserialize_usize"
118    )]
119    pub usize,
120);
121
122impl fmt::Display for PedestrianID {
123    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124        write!(f, "Pedestrian #{}", self.0)
125    }
126}
127
128#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Hash)]
129pub enum AgentID {
130    Car(CarID),
131    Pedestrian(PedestrianID),
132    // TODO Rename...
133    BusPassenger(PersonID, CarID),
134}
135
136impl AgentID {
137    pub(crate) fn as_car(self) -> CarID {
138        match self {
139            AgentID::Car(id) => id,
140            _ => panic!("Not a CarID: {:?}", self),
141        }
142    }
143
144    pub fn to_type(self) -> AgentType {
145        match self {
146            AgentID::Car(c) => match c.vehicle_type {
147                VehicleType::Car => AgentType::Car,
148                VehicleType::Bike => AgentType::Bike,
149                VehicleType::Bus => AgentType::Bus,
150                VehicleType::Train => AgentType::Train,
151            },
152            AgentID::Pedestrian(_) => AgentType::Pedestrian,
153            AgentID::BusPassenger(_, _) => AgentType::TransitRider,
154        }
155    }
156
157    pub fn to_vehicle_type(self) -> Option<VehicleType> {
158        match self {
159            AgentID::Car(c) => Some(c.vehicle_type),
160            AgentID::Pedestrian(_) => None,
161            AgentID::BusPassenger(_, _) => None,
162        }
163    }
164
165    // Convenient debugging
166    #[allow(unused)]
167    pub(crate) fn is_car(&self, id: usize) -> bool {
168        match self {
169            AgentID::Car(c) => c.id == id,
170            _ => false,
171        }
172    }
173
174    pub(crate) fn is_pedestrian(&self) -> bool {
175        matches!(self, AgentID::Pedestrian(_))
176    }
177}
178
179impl fmt::Display for AgentID {
180    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181        match self {
182            AgentID::Car(id) => write!(f, "AgentID({})", id),
183            AgentID::Pedestrian(id) => write!(f, "AgentID({})", id),
184            AgentID::BusPassenger(person, bus) => write!(f, "AgentID({} on {})", person, bus),
185        }
186    }
187}
188
189#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Hash)]
190pub enum AgentType {
191    Car,
192    Bike,
193    Bus,
194    Train,
195    Pedestrian,
196    TransitRider,
197}
198
199impl AgentType {
200    pub fn all() -> Vec<AgentType> {
201        vec![
202            AgentType::Car,
203            AgentType::Bike,
204            AgentType::Bus,
205            AgentType::Train,
206            AgentType::Pedestrian,
207            AgentType::TransitRider,
208        ]
209    }
210
211    pub fn noun(self) -> &'static str {
212        match self {
213            AgentType::Car => "Car",
214            AgentType::Bike => "Bike",
215            AgentType::Bus => "Bus",
216            AgentType::Train => "Train",
217            AgentType::Pedestrian => "Pedestrian",
218            AgentType::TransitRider => "Transit rider",
219        }
220    }
221
222    pub fn plural_noun(self) -> &'static str {
223        match self {
224            AgentType::Car => "cars",
225            AgentType::Bike => "bikes",
226            AgentType::Bus => "buses",
227            AgentType::Train => "trains",
228            AgentType::Pedestrian => "pedestrians",
229            AgentType::TransitRider => "transit riders",
230        }
231    }
232
233    pub fn ongoing_verb(self) -> &'static str {
234        match self {
235            AgentType::Car => "driving",
236            AgentType::Bike => "biking",
237            AgentType::Bus | AgentType::Train => unreachable!(),
238            AgentType::Pedestrian => "walking",
239            AgentType::TransitRider => "riding transit",
240        }
241    }
242}
243
244#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
245pub struct TripID(
246    #[serde(
247        serialize_with = "serialize_usize",
248        deserialize_with = "deserialize_usize"
249    )]
250    pub usize,
251);
252
253impl abstutil::CloneableAny for TripID {}
254
255impl fmt::Display for TripID {
256    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
257        write!(f, "Trip #{}", self.0)
258    }
259}
260
261#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
262pub struct PersonID(
263    #[serde(
264        serialize_with = "serialize_usize",
265        deserialize_with = "deserialize_usize"
266    )]
267    pub usize,
268);
269
270impl fmt::Display for PersonID {
271    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
272        write!(f, "Person {}", self.0)
273    }
274}
275
276#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
277pub enum VehicleType {
278    Car,
279    Bus,
280    Train,
281    Bike,
282}
283
284impl fmt::Display for VehicleType {
285    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
286        match self {
287            VehicleType::Car => write!(f, "car"),
288            VehicleType::Bus => write!(f, "bus"),
289            VehicleType::Train => write!(f, "train"),
290            VehicleType::Bike => write!(f, "bike"),
291        }
292    }
293}
294
295impl VehicleType {
296    pub fn to_constraints(self) -> PathConstraints {
297        match self {
298            VehicleType::Car => PathConstraints::Car,
299            VehicleType::Bus => PathConstraints::Bus,
300            VehicleType::Train => PathConstraints::Train,
301            VehicleType::Bike => PathConstraints::Bike,
302        }
303    }
304
305    pub(crate) fn is_transit(self) -> bool {
306        match self {
307            VehicleType::Car => false,
308            VehicleType::Bus => true,
309            VehicleType::Train => true,
310            VehicleType::Bike => false,
311        }
312    }
313}
314
315#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
316pub struct Vehicle {
317    pub id: CarID,
318    pub owner: Option<PersonID>,
319    pub vehicle_type: VehicleType,
320    pub length: Distance,
321    pub max_speed: Option<Speed>,
322}
323
324#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
325pub struct VehicleSpec {
326    pub vehicle_type: VehicleType,
327    pub length: Distance,
328    pub max_speed: Option<Speed>,
329}
330
331impl VehicleSpec {
332    pub(crate) fn make(self, id: CarID, owner: Option<PersonID>) -> Vehicle {
333        assert_eq!(id.vehicle_type, self.vehicle_type);
334        Vehicle {
335            id,
336            owner,
337            vehicle_type: self.vehicle_type,
338            length: self.length,
339            max_speed: self.max_speed,
340        }
341    }
342}
343
344#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
345pub enum ParkingSpot {
346    /// Lane and idx
347    Onstreet(LaneID, usize),
348    /// Building and idx (pretty meaningless)
349    Offstreet(BuildingID, usize),
350    Lot(ParkingLotID, usize),
351}
352
353#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
354pub struct ParkedCar {
355    pub vehicle: Vehicle,
356    pub spot: ParkingSpot,
357    pub parked_since: Time,
358}
359
360#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
361pub(crate) enum DrivingGoal {
362    ParkNear(BuildingID),
363    Border(IntersectionID, LaneID),
364}
365
366impl DrivingGoal {
367    pub fn goal_pos(&self, constraints: PathConstraints, map: &Map) -> Option<Position> {
368        match self {
369            DrivingGoal::ParkNear(b) => match constraints {
370                PathConstraints::Car => {
371                    let driving_lane = map.find_driving_lane_near_building(*b);
372                    let sidewalk_pos = map.get_b(*b).sidewalk_pos;
373                    if driving_lane.road == sidewalk_pos.lane().road {
374                        Some(sidewalk_pos.equiv_pos(driving_lane, map))
375                    } else {
376                        Some(Position::start(driving_lane))
377                    }
378                }
379                PathConstraints::Bike => Some(map.get_b(*b).biking_connection(map)?.0),
380                PathConstraints::Bus | PathConstraints::Train | PathConstraints::Pedestrian => {
381                    unreachable!()
382                }
383            },
384            DrivingGoal::Border(_, l) => Some(Position::end(*l, map)),
385        }
386    }
387
388    pub fn make_router(&self, owner: CarID, path: Path, map: &Map) -> Router {
389        match self {
390            DrivingGoal::ParkNear(b) => {
391                if owner.vehicle_type == VehicleType::Bike {
392                    Router::bike_then_stop(owner, path, SidewalkSpot::bike_rack(*b, map).unwrap())
393                } else {
394                    Router::park_near(owner, path, *b)
395                }
396            }
397            DrivingGoal::Border(i, last_lane) => {
398                Router::end_at_border(owner, path, map.get_l(*last_lane).length(), *i)
399            }
400        }
401    }
402}
403
404#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
405pub(crate) struct SidewalkSpot {
406    pub connection: SidewalkPOI,
407    pub sidewalk_pos: Position,
408}
409
410/// Point of interest, that is
411#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
412pub(crate) enum SidewalkPOI {
413    /// Note that for offstreet parking, the path will be the same as the building's front path.
414    ParkingSpot(ParkingSpot),
415    /// Don't actually know where this goes yet!
416    DeferredParkingSpot,
417    Building(BuildingID),
418    TransitStop(TransitStopID),
419    Border(IntersectionID),
420    /// The bikeable position
421    BikeRack(Position),
422    SuddenlyAppear,
423}
424
425impl SidewalkSpot {
426    /// Pretty hacky case
427    pub fn deferred_parking_spot() -> SidewalkSpot {
428        SidewalkSpot {
429            connection: SidewalkPOI::DeferredParkingSpot,
430            sidewalk_pos: Position::start(LaneID::dummy()),
431        }
432    }
433
434    pub fn parking_spot(
435        spot: ParkingSpot,
436        map: &Map,
437        parking_sim: &ParkingSimState,
438    ) -> SidewalkSpot {
439        SidewalkSpot {
440            connection: SidewalkPOI::ParkingSpot(spot),
441            sidewalk_pos: parking_sim.spot_to_sidewalk_pos(spot, map),
442        }
443    }
444
445    pub fn building(b: BuildingID, map: &Map) -> SidewalkSpot {
446        SidewalkSpot {
447            connection: SidewalkPOI::Building(b),
448            sidewalk_pos: map.get_b(b).sidewalk_pos,
449        }
450    }
451
452    // TODO For the case when we have to start/stop biking somewhere else, this won't match up with
453    // a building though!
454    pub fn bike_rack(b: BuildingID, map: &Map) -> Option<SidewalkSpot> {
455        let (bike_pos, sidewalk_pos) = map.get_b(b).biking_connection(map)?;
456        Some(SidewalkSpot {
457            connection: SidewalkPOI::BikeRack(bike_pos),
458            sidewalk_pos,
459        })
460    }
461
462    pub fn bus_stop(stop: TransitStopID, map: &Map) -> SidewalkSpot {
463        SidewalkSpot {
464            sidewalk_pos: map.get_ts(stop).sidewalk_pos,
465            connection: SidewalkPOI::TransitStop(stop),
466        }
467    }
468
469    // Recall sidewalks are bidirectional.
470    pub fn start_at_border(i: IntersectionID, map: &Map) -> Option<SidewalkSpot> {
471        Some(SidewalkSpot {
472            sidewalk_pos: TripEndpoint::start_walking_at_border(i, map)?,
473            connection: SidewalkPOI::Border(i),
474        })
475    }
476
477    pub fn end_at_border(i: IntersectionID, map: &Map) -> Option<SidewalkSpot> {
478        Some(SidewalkSpot {
479            sidewalk_pos: TripEndpoint::end_walking_at_border(i, map)?,
480            connection: SidewalkPOI::Border(i),
481        })
482    }
483
484    pub fn suddenly_appear(pos: Position, map: &Map) -> SidewalkSpot {
485        let lane = map.get_l(pos.lane());
486        assert!(lane.is_walkable());
487        assert!(pos.dist_along() <= lane.length());
488        SidewalkSpot {
489            sidewalk_pos: pos,
490            connection: SidewalkPOI::SuddenlyAppear,
491        }
492    }
493}
494
495#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
496pub(crate) struct TimeInterval {
497    // TODO Private fields
498    pub start: Time,
499    pub end: Time,
500}
501
502impl TimeInterval {
503    pub fn new(start: Time, end: Time) -> TimeInterval {
504        if end < start {
505            panic!("Bad TimeInterval {} .. {}", start, end);
506        }
507        TimeInterval { start, end }
508    }
509
510    pub fn percent(&self, t: Time) -> f64 {
511        if self.start == self.end {
512            return 1.0;
513        }
514
515        let x = (t - self.start) / (self.end - self.start);
516        assert!((0.0..=1.0).contains(&x));
517        x
518    }
519
520    pub fn percent_clamp_end(&self, t: Time) -> f64 {
521        if t > self.end {
522            return 1.0;
523        }
524        self.percent(t)
525    }
526}
527
528#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
529pub(crate) struct DistanceInterval {
530    // TODO Private fields
531    pub start: Distance,
532    pub end: Distance,
533}
534
535impl DistanceInterval {
536    pub fn new_driving(start: Distance, end: Distance) -> DistanceInterval {
537        if end < start {
538            panic!("Bad DistanceInterval {} .. {}", start, end);
539        }
540        DistanceInterval { start, end }
541    }
542
543    pub fn new_walking(start: Distance, end: Distance) -> DistanceInterval {
544        // start > end is fine, might be contraflow.
545        DistanceInterval { start, end }
546    }
547
548    pub fn lerp(&self, x: f64) -> Distance {
549        assert!((0.0..=1.0).contains(&x));
550        self.start + x * (self.end - self.start)
551    }
552
553    pub fn length(&self) -> Distance {
554        (self.end - self.start).abs()
555    }
556}
557
558#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
559pub(crate) struct CreatePedestrian {
560    pub id: PedestrianID,
561    pub start: SidewalkSpot,
562    pub speed: Speed,
563    pub goal: SidewalkSpot,
564    pub path: Path,
565    pub trip: TripID,
566    pub person: PersonID,
567}
568
569#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
570pub(crate) struct CreateCar {
571    pub vehicle: Vehicle,
572    pub router: Router,
573    pub maybe_parked_car: Option<ParkedCar>,
574    /// None for buses
575    pub trip_and_person: Option<(TripID, PersonID)>,
576    pub maybe_route: Option<TransitRouteID>,
577}
578
579impl CreateCar {
580    pub fn for_appearing(
581        vehicle: Vehicle,
582        router: Router,
583        trip: TripID,
584        person: PersonID,
585    ) -> CreateCar {
586        CreateCar {
587            vehicle,
588            router,
589            maybe_parked_car: None,
590            trip_and_person: Some((trip, person)),
591            maybe_route: None,
592        }
593    }
594
595    // TODO Maybe inline in trips, the only caller.
596    pub fn for_parked_car(
597        parked_car: ParkedCar,
598        router: Router,
599        trip: TripID,
600        person: PersonID,
601    ) -> CreateCar {
602        CreateCar {
603            vehicle: parked_car.vehicle.clone(),
604            router,
605            maybe_parked_car: Some(parked_car),
606            trip_and_person: Some((trip, person)),
607            maybe_route: None,
608        }
609    }
610}
611
612pub fn pedestrian_body_radius() -> Distance {
613    map_model::SIDEWALK_THICKNESS / 4.0
614}