Skip to content

Commit

Permalink
Merge pull request #128 from NREL/rjf/energy-model-loads-time-model
Browse files Browse the repository at this point in the history
forcing merge with nick on PTO
  • Loading branch information
robfitzgerald authored Feb 28, 2024
2 parents ead2af6 + f2710e7 commit afb288c
Show file tree
Hide file tree
Showing 22 changed files with 727 additions and 510 deletions.
528 changes: 306 additions & 222 deletions docs/notebooks/open_street_maps_example.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion python/nrel/routee/compass/io/generate_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def replace_id(vertex_uuid):
# 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",
output_directory / "edges-headings-enumerated.txt.gz",
index=False,
compression="gzip",
)
Expand Down
15 changes: 11 additions & 4 deletions python/nrel/routee/compass/resources/osm_default_energy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,19 @@ energy_electric = 1

[traversal]
type = "energy_model"
time_model_speed_unit = "kilometers_per_hour"
grade_table_input_file = "edges-grade-enumerated.txt.gz"
grade_table_grade_unit = "decimal"
time_unit = "minutes"
distance_unit = "miles"
headings_table_input_file = "edges-headings-enumerated.txt.gz"

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

output_time_unit = "minutes"

[[traversal.vehicles]]
name = "2012_Ford_Focus"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use crate::model::road_network::edge_id::EdgeId;
use crate::model::road_network::graph::Graph;
use crate::model::road_network::vertex_id::VertexId;
use crate::model::termination::termination_model::TerminationModel;
use crate::model::traversal::state::traversal_state::TraversalState;
use crate::model::traversal::state::state_variable::StateVar;

use crate::model::traversal::traversal_model::TraversalModel;
use crate::model::unit::cost::ReverseCost;
use crate::model::unit::Cost;
Expand Down Expand Up @@ -369,7 +370,7 @@ pub fn run_a_star_edge_oriented(
pub fn h_cost(
src: VertexId,
dst: VertexId,
state: &TraversalState,
state: &[StateVar],
g: &RwLockReadGuard<Graph>,
m: &Arc<dyn TraversalModel>,
u: &CostModel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use super::search_error::SearchError;
use crate::model::cost::cost_model::CostModel;
use crate::model::road_network::edge_id::EdgeId;
use crate::model::road_network::graph::Graph;
use crate::model::traversal::state::state_variable::StateVar;
use crate::model::traversal::state::traversal_state::TraversalState;
use crate::model::traversal::traversal_model::TraversalModel;
use crate::model::unit::Cost;
Expand Down Expand Up @@ -41,7 +42,7 @@ impl EdgeTraversal {
pub fn perform_traversal(
edge_id: EdgeId,
prev_edge_id: Option<EdgeId>,
prev_state: &TraversalState,
prev_state: &[StateVar],
g: &RwLockReadGuard<Graph>,
tm: &Arc<dyn TraversalModel>,
um: &CostModel,
Expand Down
6 changes: 4 additions & 2 deletions rust/routee-compass-core/src/model/cost/cost_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ pub enum CostError {
},
#[error("invalid cost model configuration: {0}")]
InvalidConfiguration(String),
#[error("expected state variable name {0} not found in {1} table")]
StateVariableNotFound(String, String),
#[error(
"expected state variable name {0} not found in {1} table. possible alternatives: {{2}}"
)]
StateVariableNotFound(String, String, String),
#[error("index {0} for state variable {1} out of bounds, not found in traversal state")]
StateIndexOutOfBounds(usize, String),
#[error("invalid cost variables, sum of state variable coefficients must be non-zero")]
Expand Down
17 changes: 13 additions & 4 deletions rust/routee-compass-core/src/model/cost/cost_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use super::vehicle::vehicle_cost_rate::VehicleCostRate;
use crate::model::cost::cost_error::CostError;
use crate::model::property::edge::Edge;
use crate::model::traversal::state::state_variable::StateVar;
use crate::model::traversal::state::traversal_state::TraversalState;

use crate::model::unit::Cost;
use std::collections::HashMap;
use std::sync::Arc;
Expand Down Expand Up @@ -207,18 +207,27 @@ impl CostModel {
/// A JSON serialized version of the state. This does not need to include
/// additional details such as the units (kph, hours, etc), which can be
/// summarized in the serialize_state_info method.
fn serialize_cost(&self, state: &TraversalState) -> Result<serde_json::Value, CostError> {
fn serialize_cost(&self, state: &[StateVar]) -> Result<serde_json::Value, CostError> {
let mut state_variable_costs = self
.state_variable_indices
.iter()
.map(move |(name, idx)| {
let state_var = state
.get(*idx)
.ok_or_else(|| CostError::StateIndexOutOfBounds(*idx, name.clone()))?;

let rate = self.vehicle_state_variable_rates.get(*idx).ok_or_else(|| {
let alternatives = self
.state_variable_indices
.iter()
.filter(|(_, idx)| *idx < self.vehicle_state_variable_rates.len())
.map(|(n, _)| n.to_string())
.collect::<Vec<_>>()
.join(",");
CostError::StateVariableNotFound(
name.clone(),
String::from("vehicle cost rates"),
String::from("vehicle cost rates while serializing cost"),
alternatives,
)
})?;
let cost = rate.map_value(*state_var);
Expand Down Expand Up @@ -269,7 +278,7 @@ impl CostModel {
/// and `serialize_cost_info`.
pub fn serialize_cost_with_info(
&self,
state: &TraversalState,
state: &[StateVar],
) -> Result<serde_json::Value, CostError> {
let mut output = serde_json::Map::new();
output.insert(String::from("cost"), self.serialize_cost(state)?);
Expand Down
12 changes: 11 additions & 1 deletion rust/routee-compass-core/src/model/cost/cost_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,17 @@ pub fn calculate_vehicle_costs(
.ok_or_else(|| CostError::StateIndexOutOfBounds(*state_idx, name.clone()))?;
let delta: StateVar = *next_state_var - *prev_state_var;
let mapping = rates.get(model_idx).ok_or_else(|| {
CostError::StateVariableNotFound(name.clone(), String::from("vehicle_cost_rates"))
let alternatives = state_variable_indices
.iter()
.filter(|(_, idx)| *idx < rates.len())
.map(|(n, _)| n.to_string())
.collect::<Vec<_>>()
.join(",");
CostError::StateVariableNotFound(
name.clone(),
String::from("vehicle cost rates while calculating costs"),
alternatives,
)
})?;
let coefficient = state_variable_coefficients.get(model_idx).unwrap_or(&1.0);
let delta_cost = mapping.map_value(delta);
Expand Down
5 changes: 2 additions & 3 deletions rust/routee-compass-core/src/model/frontier/frontier_model.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::model::{property::edge::Edge, traversal::state::traversal_state::TraversalState};

use super::frontier_model_error::FrontierModelError;
use crate::model::{property::edge::Edge, traversal::state::state_variable::StateVar};

/// Validates edge and traversal states. Provides an API for removing edges from
/// the frontier in a way that could be more efficient than modifying the [TraversalModel].
Expand All @@ -23,7 +22,7 @@ pub trait FrontierModel: Send + Sync {
fn valid_frontier(
&self,
_edge: &Edge,
_state: &TraversalState,
_state: &[StateVar],
_previous_edge: Option<&Edge>,
) -> Result<bool, FrontierModelError> {
Ok(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,34 @@ impl TraversalModel for DistanceTraversalModel {
vec![StateVar(0.0)]
}

fn serialize_state(&self, state: &TraversalState) -> serde_json::Value {
fn get_state_variable(
&self,
key: &str,
state: &[StateVar],
) -> Result<StateVar, TraversalModelError> {
let index = match key {
"distance" => Ok(0),
_ => Err(TraversalModelError::InternalError(format!(
"unknown state variable {}, i only know 'distance'",
key
))),
}?;
let value_f64 = state.get(index).ok_or_else(|| {
TraversalModelError::InternalError(String::from(
"state variable distance with index 0 not found in state",
))
})?;
Ok(*value_f64)
}

fn serialize_state(&self, state: &[StateVar]) -> serde_json::Value {
let total_distance = state[0].0;
serde_json::json!({
"distance": total_distance
})
}

fn serialize_state_info(&self, _state: &TraversalState) -> serde_json::Value {
fn serialize_state_info(&self, _state: &[StateVar]) -> serde_json::Value {
serde_json::json!({
"distance_unit": self.distance_unit
})
Expand All @@ -48,10 +68,10 @@ impl TraversalModel for DistanceTraversalModel {
_src: &Vertex,
edge: &Edge,
_dst: &Vertex,
state: &TraversalState,
state: &[StateVar],
) -> Result<TraversalState, TraversalModelError> {
let distance = BASE_DISTANCE_UNIT.convert(edge.distance, self.distance_unit);
let mut updated_state = state.clone();
let mut updated_state = state.to_vec();
updated_state[0] = state[0] + StateVar::from(distance);
Ok(updated_state)
}
Expand All @@ -63,7 +83,7 @@ impl TraversalModel for DistanceTraversalModel {
_v2: &Vertex,
_dst: &Edge,
_v3: &Vertex,
_state: &TraversalState,
_state: &[StateVar],
) -> Result<Option<TraversalState>, TraversalModelError> {
Ok(None)
}
Expand All @@ -72,12 +92,12 @@ impl TraversalModel for DistanceTraversalModel {
&self,
src: &Vertex,
dst: &Vertex,
state: &TraversalState,
state: &[StateVar],
) -> Result<TraversalState, TraversalModelError> {
let distance =
haversine::coord_distance(&src.coordinate, &dst.coordinate, self.distance_unit)
.map_err(TraversalModelError::NumericError)?;
let mut updated_state = state.clone();
let mut updated_state = state.to_vec();
updated_state[0] = state[0] + StateVar::from(distance);
Ok(updated_state)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod distance_traversal_model;
pub mod speed_traversal_model;
pub mod speed_traversal_service;
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,29 @@ impl TraversalModel for SpeedTraversalModel {
vec![StateVar(0.0), StateVar(0.0)]
}

fn serialize_state(&self, state: &TraversalState) -> serde_json::Value {
fn get_state_variable(
&self,
key: &str,
state: &[StateVar],
) -> Result<StateVar, TraversalModelError> {
let index = match key {
"distance" => Ok(0),
"time" => Ok(1),
_ => Err(TraversalModelError::InternalError(format!(
"unknown state variable {}, should be one of [distance, time]",
key
))),
}?;
let value_f64 = state.get(index).ok_or_else(|| {
TraversalModelError::InternalError(format!(
"state variable {} with index {} not found in state",
key, index
))
})?;
Ok(*value_f64)
}

fn serialize_state(&self, state: &[StateVar]) -> serde_json::Value {
let distance = get_distance_from_state(state);
let time = get_time_from_state(state);
serde_json::json!({
Expand All @@ -73,7 +95,7 @@ impl TraversalModel for SpeedTraversalModel {
})
}

fn serialize_state_info(&self, _state: &TraversalState) -> serde_json::Value {
fn serialize_state_info(&self, _state: &[StateVar]) -> serde_json::Value {
serde_json::json!({
"distance_unit": self.distance_unit,
"time_unit": self.time_unit,
Expand All @@ -85,7 +107,7 @@ impl TraversalModel for SpeedTraversalModel {
_src: &Vertex,
edge: &Edge,
_dst: &Vertex,
state: &TraversalState,
state: &[StateVar],
) -> Result<TraversalState, TraversalModelError> {
let distance = BASE_DISTANCE_UNIT.convert(edge.distance, self.distance_unit);
let speed = get_speed(&self.speed_table, edge.edge_id)?;
Expand All @@ -108,7 +130,7 @@ impl TraversalModel for SpeedTraversalModel {
_v2: &Vertex,
_dst: &Edge,
_v3: &Vertex,
_state: &TraversalState,
_state: &[StateVar],
) -> Result<Option<TraversalState>, TraversalModelError> {
Ok(None)
}
Expand All @@ -117,14 +139,14 @@ impl TraversalModel for SpeedTraversalModel {
&self,
src: &Vertex,
dst: &Vertex,
state: &TraversalState,
state: &[StateVar],
) -> Result<TraversalState, TraversalModelError> {
let distance =
haversine::coord_distance(&src.coordinate, &dst.coordinate, self.distance_unit)
.map_err(TraversalModelError::NumericError)?;

if distance == Distance::ZERO {
return Ok(state.clone());
return Ok(state.to_vec());
}

let time = Time::create(
Expand All @@ -140,18 +162,18 @@ impl TraversalModel for SpeedTraversalModel {
}
}

fn update_state(state: &TraversalState, distance: Distance, time: Time) -> TraversalState {
let mut updated_state = state.clone();
fn update_state(state: &[StateVar], distance: Distance, time: Time) -> TraversalState {
let mut updated_state = state.to_vec();
updated_state[0] = state[0] + distance.into();
updated_state[1] = state[1] + time.into();
updated_state
}

fn get_distance_from_state(state: &TraversalState) -> Distance {
fn get_distance_from_state(state: &[StateVar]) -> Distance {
Distance::new(state[0].0)
}

fn get_time_from_state(state: &TraversalState) -> Time {
fn get_time_from_state(state: &[StateVar]) -> Time {
Time::new(state[1].0)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use super::speed_traversal_model::SpeedTraversalModel;
use crate::model::traversal::{
traversal_model::TraversalModel, traversal_model_error::TraversalModelError,
traversal_model_service::TraversalModelService,
};
use std::sync::Arc;

pub struct SpeedLookupService {
pub m: Arc<SpeedTraversalModel>,
}

impl TraversalModelService for SpeedLookupService {
fn build(
&self,
_parameters: &serde_json::Value,
) -> Result<Arc<dyn TraversalModel>, TraversalModelError> {
Ok(self.m.clone())
}
}
Loading

0 comments on commit afb288c

Please sign in to comment.