Skip to content

Commit

Permalink
Merge branch 'fastsim-2' of https://github.com/NREL/fastsim into fix-…
Browse files Browse the repository at this point in the history
…demos

# Conflicts:
#	python/fastsim/demos/cav_demo.py
#	python/fastsim/tests/test_cav_demo.py
#	python/fastsim/tests/test_demo.py
  • Loading branch information
Kyle Carow authored and Kyle Carow committed Dec 12, 2023
2 parents f79cc4f + 469745f commit 92b3f2f
Show file tree
Hide file tree
Showing 20 changed files with 6,826 additions and 79 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies = [
"typing_extensions",
"pyyaml",
"pytest",
"openpyxl>=3.1.2"
]

[project.urls]
Expand Down
87 changes: 30 additions & 57 deletions python/fastsim/demos/cav_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,17 @@

import fastsim as fsim
from fastsim.tests.test_coasting import make_coasting_plot
import fastsim.utilities as utils

#for testing demo files, false when running automatic tests
# for testing demo files, false when running automatic tests
SHOW_PLOTS = fsim.utils.show_plots()

def maybe_str_to_bool(x, default=True):
"""
Turn values of None or string to bool
- x: str | None, some parameter, a string or None
- default: Bool, the default if x is None or blank
RETURN: True or False
"""
if x is None:
return default
if x is True or x is False:
return x
try:
lower_cased = x.lower().strip()
if lower_cased == 'false':
return False
if lower_cased == 'true':
return True
return default
except:
return default


# %% [markdown]
# ## Create a Vehicle and Cycle
#
#
# We're going to use a conventional vehicle with
# the UDDS cycle.
# %%
cyc = fsim.cycle.Cycle.from_file('udds').to_rust()
cyc = fsim.cycle.Cycle.from_file("udds").to_rust()
veh = fsim.vehicle.Vehicle.from_vehdb(1).to_rust()
sd = fsim.simdrive.RustSimDrive(cyc, veh)
sd.sim_drive()
Expand All @@ -59,20 +36,21 @@ def maybe_str_to_bool(x, default=True):
# %% [markdown]
# ## Eco-Coasting
# %%
cyc = fsim.cycle.Cycle.from_file('udds').to_rust()
cyc = fsim.cycle.Cycle.from_file("udds").to_rust()
veh = fsim.vehicle.Vehicle.from_vehdb(1).to_rust()
sd = fsim.simdrive.RustSimDrive(cyc, veh)
sd.sim_params = fsim.auxiliaries.set_nested_values(sd.sim_params,
sd.sim_params = fsim.auxiliaries.set_nested_values(
sd.sim_params,
coast_allow=True,
coast_allow_passing=False,
coast_start_speed_m_per_s=-1.0
coast_start_speed_m_per_s=-1.0,
)
sd.sim_drive()

coast_mpg = sd.mpgge
if SHOW_PLOTS:
print(f"Coast fuel economy over UDDS: {sd.mpgge} mpg")
pct_savings = ((1.0/base_mpg) - (1.0/coast_mpg)) * 100.0 / ((1.0/base_mpg))
pct_savings = ((1.0 / base_mpg) - (1.0 / coast_mpg)) * 100.0 / ((1.0 / base_mpg))
print(f"Percent Savings: {pct_savings} %")
make_coasting_plot(sd.cyc0, sd.cyc, do_show=SHOW_PLOTS)

Expand All @@ -81,31 +59,30 @@ def maybe_str_to_bool(x, default=True):
#
# Here we set up an "automated cruise control" for a system
# that drives the average speed of the cycle.
cyc_udds = fsim.cycle.Cycle.from_file('udds').to_dict()
cyc_udds = fsim.cycle.Cycle.from_file("udds").to_dict()
cyc_stop = fsim.cycle.resample(
fsim.cycle.make_cycle([0.0, 200.0], [0.0, 0.0]),
new_dt=1.0,
)
cyc = fsim.cycle.Cycle.from_dict(
fsim.cycle.concat([cyc_udds, cyc_stop])
).to_rust()
cyc = fsim.cycle.Cycle.from_dict(fsim.cycle.concat([cyc_udds, cyc_stop])).to_rust()
veh = fsim.vehicle.Vehicle.from_vehdb(1).to_rust()
sd = fsim.simdrive.RustSimDrive(cyc, veh)

sd.sim_params = fsim.auxiliaries.set_nested_values(sd.sim_params,
sd.sim_params = fsim.auxiliaries.set_nested_values(
sd.sim_params,
idm_allow=True,
idm_accel_m_per_s2=1.0,
idm_decel_m_per_s2=-2.5,
idm_dt_headway_s=2.0,
idm_minimum_gap_m=0.0,
idm_v_desired_m_per_s=np.average(np.array(cyc.mps))
idm_v_desired_m_per_s=np.average(np.array(cyc.mps)),
)
sd.sim_drive()

cruise_mpg = sd.mpgge
if SHOW_PLOTS:
print(f"Cruise fuel economy over UDDS: {sd.mpgge} mpg")
pct_savings = ((1.0/base_mpg) - (1.0/cruise_mpg)) * 100.0 / ((1.0/base_mpg))
pct_savings = ((1.0 / base_mpg) - (1.0 / cruise_mpg)) * 100.0 / ((1.0 / base_mpg))
print(f"Percent Savings: {pct_savings} %")
make_coasting_plot(sd.cyc0, sd.cyc, do_show=SHOW_PLOTS)

Expand All @@ -116,14 +93,12 @@ def maybe_str_to_bool(x, default=True):
# the average speed of the microtrip assuming the vehicle
# is able to get the average speed per microtrip from some
# external source.
cyc_udds = fsim.cycle.Cycle.from_file('udds').to_dict()
cyc_udds = fsim.cycle.Cycle.from_file("udds").to_dict()
cyc_stop = fsim.cycle.resample(
fsim.cycle.make_cycle([0.0, 200.0], [0.0, 0.0]),
new_dt=1.0,
)
cyc = fsim.cycle.Cycle.from_dict(
fsim.cycle.concat([cyc_udds, cyc_stop])
).to_rust()
cyc = fsim.cycle.Cycle.from_dict(fsim.cycle.concat([cyc_udds, cyc_stop])).to_rust()
veh = fsim.vehicle.Vehicle.from_vehdb(1).to_rust()
sd = fsim.simdrive.RustSimDrive(cyc, veh)
dist_and_avg_speeds = []
Expand All @@ -140,19 +115,18 @@ def maybe_str_to_bool(x, default=True):
print(f"mt dist (m) : {mt_dist_m}")
print(f"mt time (s) : {mt_time_s}")
print(f"mt avg speed (m/s) : {mt_avg_spd_m_per_s}")
dist_and_avg_speeds.append(
(dist_at_start_of_microtrip_m, mt_avg_spd_m_per_s)
)
dist_and_avg_speeds.append((dist_at_start_of_microtrip_m, mt_avg_spd_m_per_s))
dist_at_start_of_microtrip_m += mt_dist_m
if SHOW_PLOTS:
print(f"Found speeds for {len(dist_and_avg_speeds)} microtrips")
sd.sim_params = fsim.auxiliaries.set_nested_values(sd.sim_params,
sd.sim_params = fsim.auxiliaries.set_nested_values(
sd.sim_params,
idm_allow=True,
idm_accel_m_per_s2=0.5,
idm_decel_m_per_s2=-2.5,
idm_dt_headway_s=2.0,
idm_minimum_gap_m=10.0,
idm_v_desired_m_per_s=dist_and_avg_speeds[0][1]
idm_v_desired_m_per_s=dist_and_avg_speeds[0][1],
)
sd.init_for_step(init_soc=veh.max_soc)
current_mt_idx = 0
Expand All @@ -164,33 +138,33 @@ def maybe_str_to_bool(x, default=True):
if current_mt_idx < len(dist_and_avg_speeds):
mt_start_dist_m, mt_avg_spd_m_per_s = dist_and_avg_speeds[current_mt_idx]
if dist_traveled_m >= mt_start_dist_m:
sd.sim_params = fsim.auxiliaries.set_nested_values(sd.sim_params,
idm_v_desired_m_per_s=mt_avg_spd_m_per_s
sd.sim_params = fsim.auxiliaries.set_nested_values(
sd.sim_params, idm_v_desired_m_per_s=mt_avg_spd_m_per_s
)
print(
f"... setting idm_v_desired_m_per_s = {sd.sim_params.idm_v_desired_m_per_s}"
)
print(f"... setting idm_v_desired_m_per_s = {sd.sim_params.idm_v_desired_m_per_s}")
current_mt_idx += 1
sd.sim_drive_step()
sd.set_post_scalars()

cruise_mpg = sd.mpgge
if SHOW_PLOTS:
print(f"Cruise fuel economy over UDDS: {sd.mpgge} mpg")
pct_savings = ((1.0/base_mpg) - (1.0/cruise_mpg)) * 100.0 / ((1.0/base_mpg))
pct_savings = ((1.0 / base_mpg) - (1.0 / cruise_mpg)) * 100.0 / ((1.0 / base_mpg))
print(f"Percent Savings: {pct_savings} %")
make_coasting_plot(sd.cyc0, sd.cyc, do_show=SHOW_PLOTS)

# %% [markdown]
# # Eco-Cruise and Eco-Approach running at the same time
#
# Here, we run an Eco-Cruise and Eco-Approach at the same time.
cyc_udds = fsim.cycle.Cycle.from_file('udds').to_dict()
cyc_udds = fsim.cycle.Cycle.from_file("udds").to_dict()
cyc_stop = fsim.cycle.resample(
fsim.cycle.make_cycle([0.0, 400.0], [0.0, 0.0]),
new_dt=1.0,
)
cyc = fsim.cycle.Cycle.from_dict(
fsim.cycle.concat([cyc_udds, cyc_stop])
).to_rust()
cyc = fsim.cycle.Cycle.from_dict(fsim.cycle.concat([cyc_udds, cyc_stop])).to_rust()
veh = fsim.vehicle.Vehicle.from_vehdb(1).to_rust()
sd = fsim.simdrive.RustSimDrive(cyc, veh)
params = sd.sim_params
Expand All @@ -203,14 +177,13 @@ def maybe_str_to_bool(x, default=True):
params.idm_decel_m_per_s2 = -2.5
params.idm_dt_headway_s = 2.0
params.idm_minimum_gap_m = 10.0
params.idm_v_desired_m_per_s = 15.0 # np.sum(cyc_udds['mps']) / cyc_udds['time_s'][-1]
params.idm_v_desired_m_per_s = 15.0 # np.sum(cyc_udds['mps']) / cyc_udds['time_s'][-1]
sd.sim_params = params
sd.sim_drive()

if SHOW_PLOTS:
eco_mpg = sd.mpgge
print(f"Cruise and Coast fuel economy over UDDS: {sd.mpgge} mpg")
pct_savings = ((1.0/base_mpg) - (1.0/eco_mpg)) * 100.0 / ((1.0/base_mpg))
pct_savings = ((1.0 / base_mpg) - (1.0 / eco_mpg)) * 100.0 / ((1.0 / base_mpg))
print(f"Percent Savings: {pct_savings} %")
make_coasting_plot(sd.cyc0, sd.cyc, do_show=SHOW_PLOTS)

25 changes: 25 additions & 0 deletions python/fastsim/demos/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""
Utility functions for demo code
"""
DEMO_TEST_ENV_VAR = 'FASTSIM_DEMO_IS_INTERACTIVE'

def maybe_str_to_bool(x, default=True):
"""
Turn values of None or string to bool
- x: str | None, some parameter, a string or None
- default: Bool, the default if x is None or blank
RETURN: True or False
"""
if x is None:
return default
if x is True or x is False:
return x
try:
lower_cased = x.lower().strip()
if lower_cased == 'false':
return False
if lower_cased == 'true':
return True
return default
except:
return default
91 changes: 91 additions & 0 deletions python/fastsim/demos/vehicle_import_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""
Vehicle Import Demonstration
This module demonstrates the vehicle import API
"""
# %%
# Preamble: Basic imports
import os, pathlib

import fastsim.fastsimrust as fsr
from fastsim.demos.utils import maybe_str_to_bool, DEMO_TEST_ENV_VAR

RAN_SUCCESSFULLY = False
IS_INTERACTIVE = maybe_str_to_bool(os.getenv(DEMO_TEST_ENV_VAR))

# %%
# Setup some directories
THIS_DIR = pathlib.Path(__file__).parent.absolute()
OUTPUT_DIR = pathlib.Path(THIS_DIR) / "test_output"
if not OUTPUT_DIR.exists():
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

# %%
# List available options for the given year / make / model
make = "Toyota"
model = "Corolla"
year = "2022"

# NOTE: two additional optional arguments can be passed to get_options_for_year_make_model.
# They are the cache_url which is the URL for a file download service to retrieve cache data by year
# and also a data directory where that cache data will be stored.
# If not provided, they default to NREL's cache URL and the OS specific config data directory for this application.
# Also note that, due to interop requirements, these must be passed in as python strings. For example, a
# Python pathlib.Path object will be rejected.

options = fsr.get_options_for_year_make_model(year, make, model)
if IS_INTERACTIVE:
for opt in options:
print(f"{opt.id}: {opt.transmission}")

# %%
# Get the data for the given option
data = options[1]
if IS_INTERACTIVE:
print(
f"{data.year} {data.make} {data.model}: {data.comb_mpg_fuel1} mpg ({data.city_mpg_fuel1} CITY / {data.highway_mpg_fuel1} HWY)"
)

# %%
# Import the vehicle
other_inputs = fsr.OtherVehicleInputs(
vehicle_width_in=68.0,
vehicle_height_in=58.0,
fuel_tank_gal=12.0,
ess_max_kwh=0.0,
mc_max_kw=0.0,
ess_max_kw=0.0,
fc_max_kw=None,
) # None -> calculate from EPA data

# NOTE: two additional optional arguments can be passed to vehicle_import_by_id_and_year.
# They are the cache_url which is the URL for a file download service to retrieve cache data by year
# and also a data directory where that cache data will be stored.
# If not provided, they default to NREL's cache URL and the OS specific config data directory for this application.
# Also note that, due to interop requirements, these must be passed in as python strings. For example, a
# Python pathlib.Path object will be rejected.

rv = fsr.vehicle_import_by_id_and_year(data.id, int(year), other_inputs)

fsr.export_vehicle_to_file(rv, str(OUTPUT_DIR / "demo-vehicle.yaml"))

# %%
# Alternative API for importing all vehicles at once
# This API will import all matching configurations for
# the given year, make, and model.

# NOTE: two additional optional arguments can be passed to import_all_vehicles.
# They are the cache_url which is the URL for a file download service to retrieve cache data by year
# and also a data directory where that cache data will be stored.
# If not provided, they default to NREL's cache URL and the OS specific config data directory for this application.
# Also note that, due to interop requirements, these must be passed in as python strings. For example, a
# Python pathlib.Path object will be rejected.

vehs = fsr.import_all_vehicles(int(year), make, model, other_inputs)
if IS_INTERACTIVE:
for v in vehs:
print(f"Imported {v.scenario_name}")


# %%
# Used for automated testing
RAN_SUCCESSFULLY = True
Loading

0 comments on commit 92b3f2f

Please sign in to comment.