At last, collapse the OD -> SpawnTrip -> TripSpec function into one thing. #258

This commit is contained in:
Dustin Carlino 2020-11-17 17:55:29 -08:00
parent 7741ea79b5
commit 82c7cd1e8f
4 changed files with 123 additions and 171 deletions

View File

@ -469,7 +469,7 @@
"size_bytes": 58767209
},
"data/system/seattle/prebaked_results/lakeslice/weekday.bin": {
"checksum": "1da2f64c88888a6183849bd1e6a4b7f2",
"checksum": "bfb09ece72ba926b9d7d2583736689fd",
"size_bytes": 67024414
},
"data/system/seattle/prebaked_results/montlake/car vs bike contention.bin": {
@ -477,7 +477,7 @@
"size_bytes": 5277
},
"data/system/seattle/prebaked_results/montlake/weekday.bin": {
"checksum": "72b8f5dbd00a8027eb6583346fd8ae4f",
"checksum": "5817e0c4af88a8070577930effa409c5",
"size_bytes": 9004723
},
"data/system/seattle/scenarios/ballard/weekday.bin": {

View File

@ -8,16 +8,12 @@ use serde::{Deserialize, Serialize};
use abstutil::{prettyprint_usize, Counter, MapName, Parallelism, Timer};
use geom::{Distance, Speed, Time};
use map_model::{
BuildingID, BusRouteID, BusStopID, DirectedRoadID, Map, OffstreetParking, PathConstraints,
Position, RoadID,
};
use map_model::{BuildingID, Map, OffstreetParking, RoadID};
use crate::make::fork_rng;
use crate::{
CarID, DrivingGoal, OrigPersonID, ParkingSpot, PersonID, SidewalkSpot, Sim, TripEndpoint,
TripInfo, TripMode, TripSpawner, TripSpec, Vehicle, VehicleSpec, VehicleType, BIKE_LENGTH,
MAX_CAR_LENGTH, MIN_CAR_LENGTH, SPAWN_DIST,
OrigPersonID, ParkingSpot, PersonID, Sim, TripEndpoint, TripInfo, TripMode, TripSpawner,
TripSpec, Vehicle, VehicleSpec, VehicleType, BIKE_LENGTH, MAX_CAR_LENGTH, MIN_CAR_LENGTH,
};
/// A Scenario describes all the input to a simulation. Usually a scenario covers one day.
@ -71,32 +67,6 @@ impl IndividTrip {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
enum SpawnTrip {
/// Only for interactive / debug trips
VehicleAppearing {
start: Position,
goal: DrivingGoal,
is_bike: bool,
},
FromBorder {
dr: DirectedRoadID,
goal: DrivingGoal,
/// For bikes starting at a border, use FromBorder. UsingBike implies a walk->bike trip.
is_bike: bool,
},
UsingParkedCar(BuildingID, DrivingGoal),
UsingBike(BuildingID, DrivingGoal),
JustWalking(SidewalkSpot, SidewalkSpot),
UsingTransit(
SidewalkSpot,
SidewalkSpot,
BusRouteID,
BusStopID,
Option<BusStopID>,
),
}
/// Lifted from Seattle's Soundcast model, but seems general enough to use anyhere.
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum TripPurpose {
@ -195,17 +165,19 @@ impl Scenario {
// The RNG call might change over edits for picking the spawning lane from a border
// with multiple choices for a vehicle type.
let mut tmp_rng = fork_rng(rng);
let spec = match SpawnTrip::new(t.from.clone(), t.to.clone(), t.mode, map) {
Some(trip) => trip.to_trip_spec(
maybe_idx.map(|idx| person.vehicles[idx].id),
retry_if_no_room,
&mut tmp_rng,
map,
),
None => TripSpec::SpawningFailure {
let spec = match TripSpec::maybe_new(
t.from.clone(),
t.to.clone(),
t.mode,
maybe_idx.map(|idx| person.vehicles[idx].id),
retry_if_no_room,
&mut tmp_rng,
map,
) {
Ok(spec) => spec,
Err(error) => TripSpec::SpawningFailure {
use_vehicle: maybe_idx.map(|idx| person.vehicles[idx].id),
// TODO Collapse SpawnTrip::new and to_trip_spec and plumb better errors
error: format!("unknown spawning error"),
error,
},
};
schedule_trips.push((
@ -461,116 +433,6 @@ fn find_spot_near_building(
}
}
impl SpawnTrip {
fn to_trip_spec(
self,
use_vehicle: Option<CarID>,
retry_if_no_room: bool,
rng: &mut XorShiftRng,
map: &Map,
) -> TripSpec {
match self {
SpawnTrip::VehicleAppearing { start, goal, .. } => TripSpec::VehicleAppearing {
start_pos: start,
goal,
use_vehicle: use_vehicle.unwrap(),
retry_if_no_room,
},
SpawnTrip::FromBorder { dr, goal, is_bike } => {
let constraints = if is_bike {
PathConstraints::Bike
} else {
PathConstraints::Car
};
if let Some(l) = dr.lanes(constraints, map).choose(rng) {
TripSpec::VehicleAppearing {
start_pos: Position::new(*l, SPAWN_DIST),
goal,
use_vehicle: use_vehicle.unwrap(),
retry_if_no_room,
}
} else {
TripSpec::SpawningFailure {
use_vehicle,
error: format!("{} has no lanes to spawn a {:?}", dr.id, constraints),
}
}
}
SpawnTrip::UsingParkedCar(start_bldg, goal) => TripSpec::UsingParkedCar {
start_bldg,
goal,
car: use_vehicle.unwrap(),
},
SpawnTrip::UsingBike(start, goal) => TripSpec::UsingBike {
bike: use_vehicle.unwrap(),
start,
goal,
},
SpawnTrip::JustWalking(start, goal) => TripSpec::JustWalking { start, goal },
SpawnTrip::UsingTransit(start, goal, route, stop1, maybe_stop2) => {
TripSpec::UsingTransit {
start,
goal,
route,
stop1,
maybe_stop2,
}
}
}
}
fn new(from: TripEndpoint, to: TripEndpoint, mode: TripMode, map: &Map) -> Option<SpawnTrip> {
Some(match mode {
TripMode::Drive => match from {
TripEndpoint::Bldg(b) => {
SpawnTrip::UsingParkedCar(b, to.driving_goal(PathConstraints::Car, map)?)
}
TripEndpoint::Border(i) => SpawnTrip::FromBorder {
dr: map.get_i(i).some_outgoing_road(map)?,
goal: to.driving_goal(PathConstraints::Car, map)?,
is_bike: false,
},
TripEndpoint::SuddenlyAppear(start) => SpawnTrip::VehicleAppearing {
start,
goal: to.driving_goal(PathConstraints::Bike, map)?,
is_bike: false,
},
},
TripMode::Bike => match from {
TripEndpoint::Bldg(b) => {
SpawnTrip::UsingBike(b, to.driving_goal(PathConstraints::Bike, map)?)
}
TripEndpoint::Border(i) => SpawnTrip::FromBorder {
dr: map.get_i(i).some_outgoing_road(map)?,
goal: to.driving_goal(PathConstraints::Bike, map)?,
is_bike: true,
},
TripEndpoint::SuddenlyAppear(start) => SpawnTrip::VehicleAppearing {
start,
goal: to.driving_goal(PathConstraints::Bike, map)?,
is_bike: true,
},
},
TripMode::Walk => {
SpawnTrip::JustWalking(from.start_sidewalk_spot(map)?, to.end_sidewalk_spot(map)?)
}
TripMode::Transit => {
let start = from.start_sidewalk_spot(map)?;
let goal = to.end_sidewalk_spot(map)?;
if let Some((stop1, maybe_stop2, route)) =
map.should_use_transit(start.sidewalk_pos, goal.sidewalk_pos)
{
SpawnTrip::UsingTransit(start, goal, route, stop1, maybe_stop2)
} else {
//timer.warn(format!("{:?} not actually using transit, because pathfinding
// didn't find any useful route", trip));
SpawnTrip::JustWalking(start, goal)
}
}
})
}
}
impl PersonSpec {
// Verify that the trip start/endpoints of the person match up
fn check_schedule(&self) -> Result<(), String> {

View File

@ -1,14 +1,16 @@
//! Intermediate structures used to instantiate a Scenario. Badly needs simplification:
//! https://github.com/dabreegster/abstreet/issues/258
use rand::seq::SliceRandom;
use rand_xorshift::XorShiftRng;
use serde::{Deserialize, Serialize};
use abstutil::Timer;
use map_model::{BuildingID, BusRouteID, BusStopID, Map, PathConstraints, PathRequest, Position};
use crate::{
CarID, Command, DrivingGoal, PersonID, Scheduler, SidewalkSpot, TripInfo, TripLeg, TripManager,
VehicleType,
CarID, Command, DrivingGoal, PersonID, Scheduler, SidewalkSpot, TripEndpoint, TripInfo,
TripLeg, TripManager, TripMode, VehicleType, SPAWN_DIST,
};
// TODO Some of these fields are unused now that we separately pass TripEndpoint
@ -203,7 +205,6 @@ impl TripSpawner {
}
TripSpec::SpawningFailure { .. } => {
// TODO Is it OK to have empty trip legs?
// TODO Do we have to cancel the trip or move the vehicle here?
let legs = Vec::new();
trips.new_trip(person.id, info, legs)
}
@ -271,7 +272,7 @@ impl TripSpawner {
}
impl TripSpec {
pub(crate) fn get_pathfinding_request(&self, map: &Map) -> Option<PathRequest> {
pub fn get_pathfinding_request(&self, map: &Map) -> Option<PathRequest> {
match self {
TripSpec::VehicleAppearing {
start_pos,
@ -310,4 +311,88 @@ impl TripSpec {
}),
}
}
/// Turn an origin/destination pair and mode into a specific plan for instantiating a trip.
/// Decisions like how to use public transit happen here.
pub fn maybe_new(
from: TripEndpoint,
to: TripEndpoint,
mode: TripMode,
use_vehicle: Option<CarID>,
retry_if_no_room: bool,
rng: &mut XorShiftRng,
map: &Map,
) -> Result<TripSpec, String> {
Ok(match mode {
TripMode::Drive | TripMode::Bike => {
let constraints = if mode == TripMode::Drive {
PathConstraints::Car
} else {
PathConstraints::Bike
};
let goal = to.driving_goal(constraints, map)?;
match from {
TripEndpoint::Bldg(start_bldg) => {
if mode == TripMode::Drive {
TripSpec::UsingParkedCar {
start_bldg,
goal,
car: use_vehicle.unwrap(),
}
} else {
TripSpec::UsingBike {
start: start_bldg,
goal,
bike: use_vehicle.unwrap(),
}
}
}
TripEndpoint::Border(i) => {
let start_lane = map
.get_i(i)
.some_outgoing_road(map)
.and_then(|dr| dr.lanes(constraints, map).choose(rng).cloned())
.ok_or_else(|| {
format!("can't start a {} trip from {}", mode.ongoing_verb(), i)
})?;
TripSpec::VehicleAppearing {
start_pos: Position::new(start_lane, SPAWN_DIST),
goal,
use_vehicle: use_vehicle.unwrap(),
retry_if_no_room,
}
}
TripEndpoint::SuddenlyAppear(start_pos) => TripSpec::VehicleAppearing {
start_pos,
goal,
use_vehicle: use_vehicle.unwrap(),
retry_if_no_room,
},
}
}
TripMode::Walk => TripSpec::JustWalking {
start: from.start_sidewalk_spot(map)?,
goal: to.end_sidewalk_spot(map)?,
},
TripMode::Transit => {
let start = from.start_sidewalk_spot(map)?;
let goal = to.end_sidewalk_spot(map)?;
if let Some((stop1, maybe_stop2, route)) =
map.should_use_transit(start.sidewalk_pos, goal.sidewalk_pos)
{
TripSpec::UsingTransit {
start,
goal,
route,
stop1,
maybe_stop2,
}
} else {
//timer.warn(format!("{:?} not actually using transit, because pathfinding
// didn't find any useful route", trip));
TripSpec::JustWalking { start, goal }
}
}
})
}
}

View File

@ -1598,19 +1598,22 @@ pub enum PersonState {
OffMap,
}
// TODO Move these to make/spawner?
impl TripEndpoint {
pub(crate) fn start_sidewalk_spot(&self, map: &Map) -> Option<SidewalkSpot> {
pub(crate) fn start_sidewalk_spot(&self, map: &Map) -> Result<SidewalkSpot, String> {
match self {
TripEndpoint::Bldg(b) => Some(SidewalkSpot::building(*b, map)),
TripEndpoint::Border(i) => SidewalkSpot::start_at_border(*i, map),
TripEndpoint::SuddenlyAppear(pos) => Some(SidewalkSpot::suddenly_appear(*pos, map)),
TripEndpoint::Bldg(b) => Ok(SidewalkSpot::building(*b, map)),
TripEndpoint::Border(i) => SidewalkSpot::start_at_border(*i, map)
.ok_or_else(|| format!("can't start walking from {}", i)),
TripEndpoint::SuddenlyAppear(pos) => Ok(SidewalkSpot::suddenly_appear(*pos, map)),
}
}
pub(crate) fn end_sidewalk_spot(&self, map: &Map) -> Option<SidewalkSpot> {
pub(crate) fn end_sidewalk_spot(&self, map: &Map) -> Result<SidewalkSpot, String> {
match self {
TripEndpoint::Bldg(b) => Some(SidewalkSpot::building(*b, map)),
TripEndpoint::Border(i) => SidewalkSpot::end_at_border(*i, map),
TripEndpoint::Bldg(b) => Ok(SidewalkSpot::building(*b, map)),
TripEndpoint::Border(i) => SidewalkSpot::end_at_border(*i, map)
.ok_or_else(|| format!("can't end walking at {}", i)),
TripEndpoint::SuddenlyAppear(_) => unreachable!(),
}
}
@ -1619,12 +1622,14 @@ impl TripEndpoint {
&self,
constraints: PathConstraints,
map: &Map,
) -> Option<DrivingGoal> {
) -> Result<DrivingGoal, String> {
match self {
TripEndpoint::Bldg(b) => Some(DrivingGoal::ParkNear(*b)),
TripEndpoint::Border(i) => {
DrivingGoal::end_at_border(map.get_i(*i).some_incoming_road(map)?, constraints, map)
}
TripEndpoint::Bldg(b) => Ok(DrivingGoal::ParkNear(*b)),
TripEndpoint::Border(i) => map
.get_i(*i)
.some_incoming_road(map)
.and_then(|dr| DrivingGoal::end_at_border(dr, constraints, map))
.ok_or_else(|| format!("can't end at {} for {:?}", i, constraints)),
TripEndpoint::SuddenlyAppear(_) => unreachable!(),
}
}