Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions nrel/hive/model/roadnetwork/routetraversal.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
from __future__ import annotations

import functools as ft
from typing import Optional, NamedTuple
from typing import Optional, NamedTuple, Tuple

from nrel.hive.model.roadnetwork.linktraversal import (
LinkTraversalResult,
LinkTraversal,
)
from nrel.hive.model.roadnetwork.linktraversal import traverse_up_to
from nrel.hive.model.roadnetwork.roadnetwork import RoadNetwork
from nrel.hive.model.roadnetwork.route import Route
from nrel.hive.util import TupleOps
from nrel.hive.util.typealiases import *
from nrel.hive.util.units import Kilometers, Seconds


Expand Down Expand Up @@ -84,15 +84,17 @@ def add_link_not_traversed(self, link: LinkTraversal) -> RouteTraversal:


def traverse(
route_estimate: Route, duration_seconds: Seconds
route_estimate: Route, duration_seconds: Seconds, road_network: RoadNetwork
) -> Tuple[Optional[Exception], Optional[RouteTraversal]]:
"""
step through the route from the current agent position (assumed to be start.link_id) toward the destination
step through the route from the current agent position
(assumed to be start.link_id) toward the destination


:param route_estimate: the current route estimate

:param duration_seconds: size of the time step for this traversal, in seconds
:param road_network: the road network; used to get current travel times

:return: a route experience and updated route estimate;
or, nothing (None, Empty RouteTraversal) if the route is consumed.
an exception is possible if the current step is not found on the link or
Expand All @@ -116,14 +118,25 @@ def _traverse(

if acc_traversal.no_time_left():
return acc_failures, acc_traversal.add_link_not_traversed(link)

# update the link traversal speed with a current speed from the road network
ground_truth_link = road_network.link_from_link_id(link.link_id)
if ground_truth_link is None:
response = Exception("failure during traverse")
response.__cause__ = Exception(f"link {link.link_id} not found in road network")
return response, None
updated_speed_link = link._replace(speed_kmph=ground_truth_link.speed_kmph)

# traverse this link as far as we can go
error, traverse_result = traverse_up_to(link, acc_traversal.remaining_time_seconds)
error, traverse_result = traverse_up_to(
updated_speed_link, acc_traversal.remaining_time_seconds
)
if error:
response = Exception(f"failure during traverse")
response = Exception("failure during traverse")
response.__cause__ = error
return response, None
elif traverse_result is None:
response = Exception(f"failure during traverse")
response = Exception("failure during traverse")
return response, None
else:
updated_experienced_route = acc_traversal.add_traversal(traverse_result)
Expand Down
135 changes: 129 additions & 6 deletions nrel/hive/resources/mock_lobster.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import tempfile
from pathlib import Path
from typing import Union, Callable
from typing import Dict, FrozenSet, Optional, Tuple, Union, Callable

import h3
import immutables
Expand All @@ -10,7 +10,6 @@
from nrel.hive.config import HiveConfig
from nrel.hive.dispatcher.forecaster.forecast import Forecast, ForecastType
from nrel.hive.dispatcher.forecaster.forecaster_interface import ForecasterInterface
from nrel.hive.dispatcher.instruction.instructions import *
from nrel.hive.dispatcher.instruction_generator.charging_fleet_manager import ChargingFleetManager
from nrel.hive.dispatcher.instruction_generator.dispatcher import Dispatcher
from nrel.hive.dispatcher.instruction_generator.instruction_generator import InstructionGenerator
Expand All @@ -22,8 +21,10 @@
from nrel.hive.model.roadnetwork.geofence import GeoFence
from nrel.hive.model.roadnetwork.haversine_roadnetwork import HaversineRoadNetwork
from nrel.hive.model.roadnetwork.link import Link
from nrel.hive.model.roadnetwork.linktraversal import LinkTraversal
from nrel.hive.model.roadnetwork.osm.osm_roadnetwork import OSMRoadNetwork
from nrel.hive.model.roadnetwork.roadnetwork import RoadNetwork
from nrel.hive.model.roadnetwork.route import Route
from nrel.hive.model.sim_time import SimTime
from nrel.hive.model.station.station import Station
from nrel.hive.model.vehicle.mechatronics.bev import BEV
Expand All @@ -33,7 +34,6 @@
from nrel.hive.model.vehicle.mechatronics.powertrain.tabular_powertrain import TabularPowertrain
from nrel.hive.model.vehicle.vehicle import Vehicle
from nrel.hive.reporting.reporter import Reporter, Report
from nrel.hive.reporting.handler.kepler_feature import *
from nrel.hive.runner.environment import Environment
from nrel.hive.runner.runner_payload import RunnerPayload
from nrel.hive.state.driver_state.autonomous_driver_state.autonomous_available import (
Expand All @@ -54,9 +54,21 @@
from nrel.hive.state.simulation_state.simulation_state import SimulationState
from nrel.hive.state.simulation_state.update.step_simulation import StepSimulation
from nrel.hive.state.simulation_state.update.update import Update
from nrel.hive.state.vehicle_state.idle import Idle
from nrel.hive.state.vehicle_state.vehicle_state import VehicleState
from nrel.hive.util.typealiases import *
from nrel.hive.util.units import *
from nrel.hive.util.typealiases import (
ChargerId,
RequestId,
VehicleId,
StationId,
BaseId,
MechatronicsId,
ScheduleId,
MembershipId,
GeoId,
H3Resolution,
)
from nrel.hive.util.units import Currency, Kmph, Ratio, Seconds


class DefaultIds:
Expand Down Expand Up @@ -136,6 +148,117 @@ def mock_network(h3_res: H3Resolution = 15, geofence_res: H3Resolution = 10) ->
)


def mock_osm_route() -> Route:
"""A mock route taken from the mock osm network"""
return (
LinkTraversal(
link_id="176080957-176080956",
start="8f268cdacac236d",
end="8f268cdacace2db",
distance_km=0.10470500000000002,
speed_kmph=39.7,
),
LinkTraversal(
link_id="176080956-176092324",
start="8f268cdacace2db",
end="8f268cdac375852",
distance_km=0.14674299999999998,
speed_kmph=40.2,
),
LinkTraversal(
link_id="176092324-176092321",
start="8f268cdac375852",
end="8f268cdac354143",
distance_km=0.14593199999999998,
speed_kmph=40.2,
),
LinkTraversal(
link_id="176092321-176092319",
start="8f268cdac354143",
end="8f268cdac2213b0",
distance_km=0.146583,
speed_kmph=40.2,
),
LinkTraversal(
link_id="176092319-176092317",
start="8f268cdac2213b0",
end="8f268cdac200663",
distance_km=0.14668,
speed_kmph=40.2,
),
LinkTraversal(
link_id="176092317-176092315",
start="8f268cdac200663",
end="8f268cdac21a99e",
distance_km=0.145991,
speed_kmph=40.2,
),
LinkTraversal(
link_id="176092315-176084469",
start="8f268cdac21a99e",
end="8f268cdac28dc4b",
distance_km=0.146845,
speed_kmph=40.2,
),
LinkTraversal(
link_id="176084469-659623106",
start="8f268cdac28dc4b",
end="8f268cda8924086",
distance_km=0.146254,
speed_kmph=40.2,
),
LinkTraversal(
link_id="659623106-3329646638",
start="8f268cda8924086",
end="8f268cda893178c",
distance_km=0.148035,
speed_kmph=40.2,
),
LinkTraversal(
link_id="3329646638-5063215690",
start="8f268cda893178c",
end="8f268cda8913d26",
distance_km=0.145185,
speed_kmph=40.2,
),
LinkTraversal(
link_id="5063215690-5313640931",
start="8f268cda8913d26",
end="8f268cda891e263",
distance_km=0.10451800000000001,
speed_kmph=40.2,
),
LinkTraversal(
link_id="5313640931-176100374",
start="8f268cda891e263",
end="8f268cda8826870",
distance_km=0.10771800000000001,
speed_kmph=40.2,
),
LinkTraversal(
link_id="176100374-176104476",
start="8f268cda8826870",
end="8f268cda8821000",
distance_km=0.10456100000000002,
speed_kmph=40.2,
),
LinkTraversal(
link_id="176104476-637816565",
start="8f268cda8821000",
end="8f268cda882d781",
distance_km=0.10358000000000002,
speed_kmph=40.2,
),
LinkTraversal(
link_id="637816565-279916144",
start="8f268cda882d781",
end="8f268cda8942091",
distance_km=0.146861,
speed_kmph=39.7,
),
)


def mock_osm_network(h3_res: H3Resolution = 15, geofence_res: H3Resolution = 10) -> OSMRoadNetwork:
road_network_file = resource_filename(
"nrel.hive.resources.scenarios.denver_downtown.road_network",
Expand Down Expand Up @@ -592,7 +715,7 @@ def close(self, runner_payload: RunnerPayload):


def mock_route_from_geoids(src: GeoId, dst: GeoId, speed_kmph: Kmph = 1) -> Tuple[Link, ...]:
link = Link.build("1", src, dst, speed_kmph=speed_kmph)
link = Link.build(f"{src}-{dst}", src, dst, speed_kmph=speed_kmph)
return (link,)


Expand Down
46 changes: 29 additions & 17 deletions nrel/hive/state/vehicle_state/vehicle_state_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ def charge(
:param charger_id: the charger_id we are using
:return: an exception due to failure or an optional updated simulation
"""
context = f"vehicle {vehicle_id} attempting to charge at station {station_id} with charger {charger_id}"
context = f"""
vehicle {vehicle_id} attempting to charge at station {station_id} with charger {charger_id}"
"""

vehicle = sim.vehicles.get(vehicle_id)
mechatronics = env.mechatronics.get(vehicle.mechatronics_id) if vehicle else None
Expand Down Expand Up @@ -142,7 +144,8 @@ def _go_out_of_service_on_empty(
:return: an optional error, or an optional sim with the out of service vehicle
"""
# TODO: ways we can improve this:
# - find the exact point in the route where a vehicle runs out of energy and move it there before transitioning
# - find the exact point in the route where a vehicle runs out of
# energy and move it there before transitioning
# to out of service.
# - report stranded passengers if we're servicing a trip when this happens.
next_state = OutOfService.build(vehicle_id)
Expand Down Expand Up @@ -182,19 +185,13 @@ def move(
SimulationStateError(f"vehicle state does not have route; context {context}"),
None,
)
elif not hasattr(vehicle.vehicle_state, "update_route"):
return (
SimulationStateError(
f"vehicle state does not have update_route method; context {context}"
),
None,
)
else:
route = vehicle.vehicle_state.route

error, traverse_result = traverse(
route_estimate=route,
duration_seconds=int(sim.sim_timestep_duration_seconds),
road_network=sim.road_network,
)
if error:
return error, None
Expand All @@ -203,9 +200,17 @@ def move(

if not traverse_result.experienced_route:
# vehicle did not traverse so we set an empty route
# ignore mypy error since we explicitly check for attribute above
updated_vehicle_state = vehicle.vehicle_state.update_route(route=empty_route()) # type: ignore
updated_vehicle = vehicle.modify_vehicle_state(updated_vehicle_state)
if not hasattr(vehicle.vehicle_state, "update_route"):
return (
SimulationStateError(
f"vehicle state does not have update_route method; context {context}"
),
None,
)
else:
er = empty_route()
updated_vehicle_state = vehicle.vehicle_state.update_route(route=er)
updated_vehicle = vehicle.modify_vehicle_state(updated_vehicle_state)
else:
experienced_route = traverse_result.experienced_route
remaining_route = traverse_result.remaining_route
Expand All @@ -222,12 +227,19 @@ def move(
position=vehicle_position
).tick_distance_traveled_km(step_distance_km)

# ignore mypy error since we explicitly check for attribute above
new_route_state = new_position_vehicle.vehicle_state.update_route(route=remaining_route) # type: ignore
updated_vehicle = new_position_vehicle.modify_vehicle_state(new_route_state)
if not hasattr(new_position_vehicle.vehicle_state, "update_route"):
return (
SimulationStateError(
f"vehicle state does not have update_route method; context {context}"
),
None,
)
else:
new_route_state = new_position_vehicle.vehicle_state.update_route(route=remaining_route)
updated_vehicle = new_position_vehicle.modify_vehicle_state(new_route_state)

report = vehicle_move_event(sim, vehicle, updated_vehicle, traverse_result, env)
env.reporter.file_report(report)
report = vehicle_move_event(sim, vehicle, updated_vehicle, traverse_result, env)
env.reporter.file_report(report)

error, moved_sim = simulation_state_ops.modify_vehicle(sim, updated_vehicle)
if error:
Expand Down
12 changes: 10 additions & 2 deletions tests/test_bev_mechatronics.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
from unittest import TestCase

from nrel.hive.resources.mock_lobster import *
from nrel.hive.model.energy.energytype import EnergyType

from nrel.hive.resources.mock_lobster import (
mock_bev,
mock_dcfc_charger,
mock_l2_charger,
mock_route,
mock_vehicle,
)
from nrel.hive.util.units import KM_TO_MILE, MILE_TO_KM, hours_to_seconds


class TestBEV(TestCase):
Expand Down
3 changes: 2 additions & 1 deletion tests/test_cancel_requests.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from unittest import TestCase

from returns.result import Success
from nrel.hive.resources.mock_lobster import mock_env, mock_request, mock_sim
from nrel.hive.state.simulation_state import simulation_state_ops

from nrel.hive.state.simulation_state.update.cancel_requests import CancelRequests
from nrel.hive.resources.mock_lobster import *


class TestCancelRequests(TestCase):
Expand Down
13 changes: 12 additions & 1 deletion tests/test_charging_price_update.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
from unittest import TestCase
import immutables

from pkg_resources import resource_filename
from nrel.hive.resources.mock_lobster import (
mock_dcfc_charger_id,
mock_env,
mock_l1_charger_id,
mock_l2_charger_id,
mock_sim,
mock_station,
mock_station_from_geoid,
)

from nrel.hive.state.simulation_state.update.charging_price_update import ChargingPriceUpdate
from nrel.hive.resources.mock_lobster import *


class TestChargingPriceUpdate(TestCase):
Expand Down
Loading