diff --git a/game/src/info/intersection.rs b/game/src/info/intersection.rs index 4d23960d9c..fd670a6d4f 100644 --- a/game/src/info/intersection.rs +++ b/game/src/info/intersection.rs @@ -323,7 +323,7 @@ fn delay_plot( app.primary.sim.time() }; if let Some(list) = data.intersection_delays.get(&i) { - for (t, dt, agent_type) in list { + for (_, t, dt, agent_type) in list { if *t > limit { break; } diff --git a/map_model/src/lib.rs b/map_model/src/lib.rs index a658dd6638..fcc4a84ce9 100644 --- a/map_model/src/lib.rs +++ b/map_model/src/lib.rs @@ -27,7 +27,9 @@ pub use crate::objects::parking_lot::{ParkingLot, ParkingLotID}; pub use crate::objects::road::{DirectedRoadID, Road, RoadID}; pub use crate::objects::stop_signs::{ControlStopSign, RoadWithStopSign}; pub use crate::objects::traffic_signals::{ControlTrafficSignal, Phase, PhaseType}; -pub use crate::objects::turn::{Turn, TurnGroup, TurnGroupID, TurnID, TurnPriority, TurnType}; +pub use crate::objects::turn::{ + CompressedTurnGroupID, Turn, TurnGroup, TurnGroupID, TurnID, TurnPriority, TurnType, +}; pub use crate::objects::zone::{AccessRestrictions, Zone}; pub use crate::pathfind::uber_turns::{IntersectionCluster, UberTurn, UberTurnGroup}; use crate::pathfind::Pathfinder; diff --git a/map_model/src/objects/traffic_signals.rs b/map_model/src/objects/traffic_signals.rs index 543b7251a8..dc2a403873 100644 --- a/map_model/src/objects/traffic_signals.rs +++ b/map_model/src/objects/traffic_signals.rs @@ -1,13 +1,14 @@ use crate::make::traffic_signals::{brute_force, get_possible_policies}; use crate::raw::OriginalRoad; use crate::{ - osm, DirectedRoadID, IntersectionID, Map, TurnGroup, TurnGroupID, TurnID, TurnPriority, - TurnType, + osm, CompressedTurnGroupID, DirectedRoadID, IntersectionID, Map, TurnGroup, TurnGroupID, + TurnID, TurnPriority, TurnType, }; use abstutil::{deserialize_btreemap, retain_btreeset, serialize_btreemap, Timer}; use geom::Duration; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, BTreeSet}; +use std::convert::TryFrom; #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct ControlTrafficSignal { @@ -167,7 +168,7 @@ impl ControlTrafficSignal { { tg.id } else { - panic!("{} doesn't belong to any turn groups", turn) + panic!("{} doesn't belong to any turn groups in {}", turn, self.id) } } @@ -183,6 +184,18 @@ impl ControlTrafficSignal { } missing } + + pub fn compressed_id(&self, turn: TurnID) -> CompressedTurnGroupID { + for (idx, tg) in self.turn_groups.values().enumerate() { + if tg.members.contains(&turn) { + return CompressedTurnGroupID { + i: self.id, + idx: u8::try_from(idx).unwrap(), + }; + } + } + panic!("{} doesn't belong to any turn groups in {}", turn, self.id) + } } impl Phase { diff --git a/map_model/src/objects/turn.rs b/map_model/src/objects/turn.rs index ddcd8795cc..6e4c1d1050 100644 --- a/map_model/src/objects/turn.rs +++ b/map_model/src/objects/turn.rs @@ -183,6 +183,14 @@ pub struct TurnGroupID { pub crosswalk: bool, } +// This is cheaper to store than a TurnGroupID. It simply indexes into the list of turn_groups. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct CompressedTurnGroupID { + pub i: IntersectionID, + // There better not be any intersection with more than 256 turn groups... + pub idx: u8, +} + // TODO Unclear how this plays with different lane types // This is only useful for traffic signals currently. #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] diff --git a/sim/src/analytics.rs b/sim/src/analytics.rs index a430bdc2b3..76578ef980 100644 --- a/sim/src/analytics.rs +++ b/sim/src/analytics.rs @@ -30,7 +30,8 @@ pub struct Analytics { pub trip_log: Vec<(Time, TripID, Option, TripPhaseType)>, // TODO Transit riders aren't represented here yet, just the vehicle they're riding. - pub intersection_delays: BTreeMap>, + // Only for traffic signals. The u8 is the turn group index from a CompressedTurnGroupID. + pub intersection_delays: BTreeMap>, // Per parking lane or lot, when does a spot become filled (true) or free (false) pub parking_lane_changes: BTreeMap>, @@ -157,9 +158,9 @@ impl Analytics { // Intersection delays if let Event::IntersectionDelayMeasured(id, delay, agent) = ev { self.intersection_delays - .entry(id) + .entry(id.i) .or_insert_with(Vec::new) - .push((time, delay, agent.to_type())); + .push((id.idx, time, delay, agent.to_type())); } // Parking spot changes @@ -276,7 +277,7 @@ impl Analytics { for (i, list1) in &self.intersection_delays { if let Some(list2) = before.intersection_delays.get(i) { let mut sum1 = Duration::ZERO; - for (t, dt, _) in list1 { + for (_, t, dt, _) in list1 { if *t > now { break; } @@ -284,7 +285,7 @@ impl Analytics { } let mut sum2 = Duration::ZERO; - for (t, dt, _) in list2 { + for (_, t, dt, _) in list2 { if *t > now { break; } diff --git a/sim/src/events.rs b/sim/src/events.rs index 3f9b9f308e..e7cd304311 100644 --- a/sim/src/events.rs +++ b/sim/src/events.rs @@ -3,7 +3,8 @@ use crate::{ }; use geom::Duration; use map_model::{ - BuildingID, BusRouteID, BusStopID, IntersectionID, LaneID, Map, Path, PathRequest, Traversable, + BuildingID, BusRouteID, BusStopID, CompressedTurnGroupID, IntersectionID, LaneID, Map, Path, + PathRequest, Traversable, }; use serde::{Deserialize, Serialize}; @@ -41,7 +42,7 @@ pub enum Event { // If the agent is a transit vehicle, then include a count of how many passengers are on // board. AgentEntersTraversable(AgentID, Traversable, Option), - IntersectionDelayMeasured(IntersectionID, Duration, AgentID), + IntersectionDelayMeasured(CompressedTurnGroupID, Duration, AgentID), TripFinished { trip: TripID, diff --git a/sim/src/mechanics/intersection.rs b/sim/src/mechanics/intersection.rs index dbbb295d7a..8b15d406a0 100644 --- a/sim/src/mechanics/intersection.rs +++ b/sim/src/mechanics/intersection.rs @@ -303,8 +303,11 @@ impl IntersectionSimState { .entry(req.clone()) .or_insert(now); + let shared_sidewalk_corner = + map.get_t(req.turn).turn_type == TurnType::SharedSidewalkCorner; + let readonly_pair = maybe_cars_and_queues.as_ref().map(|(_, c, q)| (*c, &**q)); - let allowed = if map.get_t(req.turn).turn_type == TurnType::SharedSidewalkCorner { + let allowed = if shared_sidewalk_corner { // SharedSidewalkCorner doesn't conflict with anything -- fastpath! true } else if !self.handle_accepted_conflicts(&req, map, readonly_pair) { @@ -413,9 +416,15 @@ impl IntersectionSimState { // for stop signs too. let state = self.state.get_mut(&turn.parent).unwrap(); let delay = now - state.waiting.remove(&req).unwrap(); - if map.maybe_get_traffic_signal(state.id).is_some() { - self.events - .push(Event::IntersectionDelayMeasured(turn.parent, delay, agent)); + // SharedSidewalkCorner are always no-conflict, immediate turns; they're not interesting. + if !shared_sidewalk_corner { + if let Some(ts) = map.maybe_get_traffic_signal(state.id) { + self.events.push(Event::IntersectionDelayMeasured( + ts.compressed_id(turn), + delay, + agent, + )); + } } state.accepted.insert(req); if self.break_turn_conflict_cycles {