Skip to content

Commit

Permalink
Merge pull request #103 from NREL/ndr/turn-access-cost
Browse files Browse the repository at this point in the history
Turn Access Cost
  • Loading branch information
nreinicke authored Jan 16, 2024
2 parents d7b309f + c052221 commit cd2b3e3
Show file tree
Hide file tree
Showing 16 changed files with 422 additions and 188 deletions.
267 changes: 135 additions & 132 deletions docs/notebooks/open_street_maps_example.ipynb

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions publish_crates.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# exit on failure
set -e

cd rust/

cargo publish -p routee-compass-core --dry-run
cargo publish -p routee-compass-core

cargo publish -p routee-compass-powertrain --dry-run
cargo publish -p routee-compass-powertrain

cargo publish -p routee-compass --dry-run
cargo publish -p routee-compass

cargo publish -p routee-compass-py --dry-run
cargo publish -p routee-compass-py
14 changes: 14 additions & 0 deletions python/nrel/routee/compass/io/generate_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def generate_compass_dataset(
log.info("processing graph topology and speeds")
g1 = ox.utils_graph.get_largest_component(g)
g1 = ox.add_edge_speeds(g1, hwy_speeds=hwy_speeds, fallback=fallback, agg=agg)
g1 = ox.add_edge_bearings(g1)

if add_grade:
log.info("adding grade information")
Expand Down Expand Up @@ -153,6 +154,19 @@ def replace_id(vertex_uuid):
header=False,
)

headings = e.bearing.fillna(0).apply(lambda x: int(round(x)))
headings_df = headings.to_frame(name="start_heading")

# We could get more sophisticated and compute the end heading
# for links that might have some significant curvature, but
# for now we'll just use the start heading.
headings_df["end_heading"] = None
headings_df.to_csv(
output_directory / "edges-headings-enumerated.csv.gz",
index=False,
compression="gzip",
)

if add_grade:
e.grade.to_csv(
output_directory / "edges-grade-enumerated.txt.gz",
Expand Down
45 changes: 19 additions & 26 deletions python/nrel/routee/compass/resources/osm_default_energy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,6 @@ output_plugins = [
{ type = "traversal", route = "geo_json", geometry_input_file = "edges-geometries-enumerated.txt.gz" },
{ type = "uuid", uuid_input_file = "vertices-uuid-enumerated.txt.gz" },
]

[traversal]
type = "energy_model"
speed_table_input_file = "edges-posted-speed-enumerated.txt.gz"
speed_table_speed_unit = "kilometers_per_hour"
output_time_unit = "minutes"
output_distance_unit = "miles"

# based on 65.5 cents per mile 2023 IRS mileage rate, $/mile
[cost.vehicle_state_variable_rates.distance]
type = "factor"
Expand All @@ -45,6 +37,15 @@ factor = 3.120
type = "factor"
factor = 0.50

[traversal]
type = "energy_model"
speed_table_input_file = "edges-posted-speed-enumerated.txt.gz"
speed_table_speed_unit = "kilometers_per_hour"
output_time_unit = "minutes"
output_distance_unit = "miles"
headings_table_input_file = "edges-headings-enumerated.csv.gz"


[[traversal.vehicles]]
name = "2012_Ford_Focus"
type = "ice"
Expand Down Expand Up @@ -342,14 +343,6 @@ speed_bins = 101
grade_lower_bound = -0.2
grade_upper_bound = 0.2
grade_bins = 41
[traversal.vehicles.model_type.interpolate]
underlying_model_type = "smartcore"
speed_lower_bound = 0
speed_upper_bound = 100
speed_bins = 101
grade_lower_bound = -0.2
grade_upper_bound = 0.2
grade_bins = 41

[[traversal.vehicles]]
name = "2016_TOYOTA_Corolla_4cyl_2WD"
Expand Down Expand Up @@ -700,7 +693,7 @@ grade_unit = "decimal"
energy_rate_unit = "kilowatt_hours_per_mile"
ideal_energy_rate = 0.2
real_world_energy_adjustment = 1.3958
[traversal.vehicles.model_type.interpolate]
[traversal.vehicles.charge_depleting.model_type.interpolate]
underlying_model_type = "smartcore"
speed_lower_bound = 0
speed_upper_bound = 100
Expand All @@ -716,7 +709,7 @@ grade_unit = "decimal"
energy_rate_unit = "gallons_gasoline_per_mile"
ideal_energy_rate = 0.02
real_world_energy_adjustment = 1.1252
[traversal.vehicles.model_type.interpolate]
[traversal.vehicles.charge_sustaining.model_type.interpolate]
underlying_model_type = "smartcore"
speed_lower_bound = 0
speed_upper_bound = 100
Expand All @@ -738,7 +731,7 @@ grade_unit = "decimal"
energy_rate_unit = "kilowatt_hours_per_mile"
ideal_energy_rate = 0.2
real_world_energy_adjustment = 1.3958
[traversal.vehicles.model_type.interpolate]
[traversal.vehicles.charge_depleting.model_type.interpolate]
underlying_model_type = "smartcore"
speed_lower_bound = 0
speed_upper_bound = 100
Expand All @@ -754,7 +747,7 @@ grade_unit = "decimal"
energy_rate_unit = "gallons_gasoline_per_mile"
ideal_energy_rate = 0.02
real_world_energy_adjustment = 1.1252
[traversal.vehicles.model_type.interpolate]
[traversal.vehicles.charge_sustaining.model_type.interpolate]
underlying_model_type = "smartcore"
speed_lower_bound = 0
speed_upper_bound = 100
Expand All @@ -776,7 +769,7 @@ grade_unit = "decimal"
energy_rate_unit = "kilowatt_hours_per_mile"
ideal_energy_rate = 0.2
real_world_energy_adjustment = 1.3958
[traversal.vehicles.model_type.interpolate]
[traversal.vehicles.charge_depleting.model_type.interpolate]
underlying_model_type = "smartcore"
speed_lower_bound = 0
speed_upper_bound = 100
Expand All @@ -792,7 +785,7 @@ grade_unit = "decimal"
energy_rate_unit = "gallons_gasoline_per_mile"
ideal_energy_rate = 0.02
real_world_energy_adjustment = 1.1252
[traversal.vehicles.model_type.interpolate]
[traversal.vehicles.charge_sustaining.model_type.interpolate]
underlying_model_type = "smartcore"
speed_lower_bound = 0
speed_upper_bound = 100
Expand All @@ -814,7 +807,7 @@ grade_unit = "decimal"
energy_rate_unit = "kilowatt_hours_per_mile"
ideal_energy_rate = 0.2
real_world_energy_adjustment = 1.3958
[traversal.vehicles.model_type.interpolate]
[traversal.vehicles.charge_depleting.model_type.interpolate]
underlying_model_type = "smartcore"
speed_lower_bound = 0
speed_upper_bound = 100
Expand All @@ -830,7 +823,7 @@ grade_unit = "decimal"
energy_rate_unit = "gallons_gasoline_per_mile"
ideal_energy_rate = 0.02
real_world_energy_adjustment = 1.1252
[traversal.vehicles.model_type.interpolate]
[traversal.vehicles.charge_sustaining.model_type.interpolate]
underlying_model_type = "smartcore"
speed_lower_bound = 0
speed_upper_bound = 100
Expand All @@ -852,7 +845,7 @@ grade_unit = "decimal"
energy_rate_unit = "kilowatt_hours_per_mile"
ideal_energy_rate = 0.2
real_world_energy_adjustment = 1.3958
[traversal.vehicles.model_type.interpolate]
[traversal.vehicles.charge_depleting.model_type.interpolate]
underlying_model_type = "smartcore"
speed_lower_bound = 0
speed_upper_bound = 100
Expand All @@ -868,7 +861,7 @@ grade_unit = "decimal"
energy_rate_unit = "gallons_gasoline_per_mile"
ideal_energy_rate = 0.02
real_world_energy_adjustment = 1.1252
[traversal.vehicles.model_type.interpolate]
[traversal.vehicles.charge_sustaining.model_type.interpolate]
underlying_model_type = "smartcore"
speed_lower_bound = 0
speed_upper_bound = 100
Expand Down
68 changes: 68 additions & 0 deletions rust/routee-compass-core/src/model/road_network/edge_heading.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use serde::Deserialize;

#[derive(Copy, Clone, Deserialize)]
pub struct EdgeHeading {
start_heading: i16,
end_heading: Option<i16>,
}

impl EdgeHeading {
pub fn with_start_and_end(start_heading: i16, end_heading: i16) -> Self {
Self {
start_heading,
end_heading: Some(end_heading),
}
}

pub fn with_start(start_heading: i16) -> Self {
Self {
start_heading,
end_heading: None,
}
}

pub fn start_heading(&self) -> i16 {
self.start_heading
}
/// If the end heading is not specified, it is assumed to be the same as the start heading
pub fn end_heading(&self) -> i16 {
match self.end_heading {
Some(end_heading) => end_heading,
None => self.start_heading,
}
}
/// Compute the angle between this edge and the next edge
pub fn next_edge_angle(&self, next_edge_heading: &EdgeHeading) -> i16 {
let angle = next_edge_heading.start_heading() - self.end_heading();
if angle > 180 {
angle - 360
} else if angle < -180 {
angle + 360
} else {
angle
}
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_next_edge_angle() {
let edge_heading = EdgeHeading::with_start_and_end(45, 90);
let next_edge_heading = EdgeHeading::with_start_and_end(90, 135);
assert_eq!(edge_heading.next_edge_angle(&next_edge_heading), 0);
}

#[test]
fn test_next_edge_angle_wrap() {
let edge_heading = EdgeHeading::with_start_and_end(10, 10);
let next_edge_heading = EdgeHeading::with_start_and_end(350, 350);
assert_eq!(edge_heading.next_edge_angle(&next_edge_heading), -20);

let edge_heading = EdgeHeading::with_start_and_end(350, 350);
let next_edge_heading = EdgeHeading::with_start_and_end(10, 10);
assert_eq!(edge_heading.next_edge_angle(&next_edge_heading), 20);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub enum GraphError {
VertexWithoutInEdges { vertex_id: VertexId },
#[error("error in test setup")]
TestError,
#[error("Error with graph attribute {0}: {1}")]
AttributeError(String, String),
#[error("{filename} file source was empty")]
EmptyFileSource { filename: PathBuf },
#[error("failure reading TomTom graph: {source}")]
Expand Down
2 changes: 2 additions & 0 deletions rust/routee-compass-core/src/model/road_network/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pub mod edge_heading;
pub mod edge_id;
pub mod edge_loader;
pub mod graph;
pub mod graph_error;
pub mod graph_loader;
pub mod turn;
pub mod vertex_id;
pub mod vertex_loader;
32 changes: 32 additions & 0 deletions rust/routee-compass-core/src/model/road_network/turn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use super::graph_error::GraphError;

pub enum Turn {
NoTurn,
SlightRight,
SlightLeft,
Right,
Left,
SharpRight,
SharpLeft,
UTurn,
}

impl Turn {
pub fn from_angle(angle: i16) -> Result<Self, GraphError> {
match angle {
-180..=-160 => Ok(Turn::UTurn),
-159..=-135 => Ok(Turn::SharpLeft),
-134..=-45 => Ok(Turn::Left),
-44..=-20 => Ok(Turn::SlightLeft),
-19..=19 => Ok(Turn::NoTurn),
20..=44 => Ok(Turn::SlightRight),
45..=134 => Ok(Turn::Right),
135..=159 => Ok(Turn::SharpRight),
160..=180 => Ok(Turn::UTurn),
_ => Err(GraphError::AttributeError(
"Turn".to_string(),
format!("Angle {} out of range of -180 to 180", angle),
)),
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::path::PathBuf;

use super::state::traversal_state::TraversalState;
use crate::model::road_network::graph_error::GraphError;
use crate::model::unit::UnitError;
use crate::util::cache_policy::cache_error::CacheError;

Expand All @@ -22,6 +23,8 @@ pub enum TraversalModelError {
TraversalUnitsError(#[from] UnitError),
#[error(transparent)]
CacheError(#[from] CacheError),
#[error(transparent)]
GraphError(#[from] GraphError),
#[error("prediction model failed with error {0}")]
PredictionModel(String),
}
12 changes: 6 additions & 6 deletions rust/routee-compass-core/src/model/unit/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub fn create_time(
))
} else {
let time = (d, s).into();
let result = BASE_TIME_UNIT.convert(time, time_unit);
let result = BASE_TIME_UNIT.convert(time, &time_unit);
Ok(result)
}
}
Expand All @@ -52,7 +52,7 @@ pub fn create_speed(
speed_unit: SpeedUnit,
) -> Result<Speed, UnitError> {
let d = distance_unit.convert(distance, BASE_DISTANCE_UNIT);
let t = time_unit.convert(time, BASE_TIME_UNIT);
let t = time_unit.convert(time, &BASE_TIME_UNIT);
if t <= Time::ZERO {
Err(UnitError::SpeedFromTimeAndDistanceError(time, distance))
} else {
Expand Down Expand Up @@ -253,7 +253,7 @@ mod test {
BASE_TIME_UNIT,
)
.unwrap();
let expected = TimeUnit::Hours.convert(Time::ONE, BASE_TIME_UNIT);
let expected = TimeUnit::Hours.convert(Time::ONE, &BASE_TIME_UNIT);
approx_eq_time(time, expected, 0.001);
}

Expand All @@ -267,7 +267,7 @@ mod test {
TimeUnit::Hours,
)
.unwrap();
let expected = BASE_TIME_UNIT.convert(Time::ONE, TimeUnit::Hours);
let expected = BASE_TIME_UNIT.convert(Time::ONE, &TimeUnit::Hours);
approx_eq_time(time, expected, 0.001);
}

Expand All @@ -281,7 +281,7 @@ mod test {
BASE_TIME_UNIT,
)
.unwrap();
let expected = TimeUnit::Hours.convert(Time::ONE, BASE_TIME_UNIT);
let expected = TimeUnit::Hours.convert(Time::ONE, &BASE_TIME_UNIT);
approx_eq_time(time, expected, 0.01);
}

Expand All @@ -295,7 +295,7 @@ mod test {
TimeUnit::Hours,
)
.unwrap();
let expected = BASE_TIME_UNIT.convert(Time::ONE, TimeUnit::Hours);
let expected = BASE_TIME_UNIT.convert(Time::ONE, &TimeUnit::Hours);
approx_eq_time(time, expected, 0.001);
}

Expand Down
Loading

0 comments on commit cd2b3e3

Please sign in to comment.