Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate variable paths #136

Closed
wants to merge 17 commits into from
Closed
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
9 changes: 7 additions & 2 deletions fastsim-core/src/drive_cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ use fastsim_2::cycle::RustCycle as Cycle2;
self.len()
}

#[setter]
fn set_grade(&mut self, grade: Vec<f64>) {
#[setter("__grade")]
fn set_grade_py(&mut self, grade: Vec<f64>) {
self.grade = grade.iter().map(|x| *x * uc::R).collect();
}

#[getter]
fn get_grade_py(&self) -> Vec<f64> {
self.grade.iter().map(|x| x.get::<si::ratio>()).collect()
}
)]
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
/// Container
Expand Down
32 changes: 16 additions & 16 deletions fastsim-core/src/vehicle/powertrain/reversible_energy_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,30 +58,30 @@ const TOL: f64 = 1e-3;
Ok(())
}

#[getter("eff_max")]
fn get_eff_max_py(&self) -> f64 {
self.get_eff_max()
}
// #[getter("eff_max")]
// fn get_eff_max_py(&self) -> f64 {
// self.get_eff_max()
// }

#[setter("__eff_max")]
fn set_eff_max_py(&mut self, eff_max: f64) -> PyResult<()> {
self.set_eff_max(eff_max).map_err(PyValueError::new_err)
}
// #[setter("__eff_max")]
// fn set_eff_max_py(&mut self, eff_max: f64) -> PyResult<()> {
// self.set_eff_max(eff_max).map_err(PyValueError::new_err)
// }

#[getter("eff_min")]
fn get_eff_min_py(&self) -> f64 {
self.get_eff_min()
}
// #[getter("eff_min")]
// fn get_eff_min_py(&self) -> f64 {
// self.get_eff_min()
// }

#[getter("eff_range")]
fn get_eff_range_py(&self) -> f64 {
self.get_eff_range()
}

#[setter("__eff_range")]
fn set_eff_range_py(&mut self, eff_range: f64) -> anyhow::Result<()> {
self.set_eff_range(eff_range)
}
// #[setter("__eff_range")]
// fn set_eff_range_py(&mut self, eff_range: f64) -> anyhow::Result<()> {
// self.set_eff_range(eff_range)
// }

// TODO: decide on way to deal with `side_effect` coming after optional arg and uncomment
#[pyo3(name = "set_mass")]
Expand Down
57 changes: 28 additions & 29 deletions fastsim-core/src/vehicle/vehicle_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,42 +42,41 @@ impl Init for AuxSource {}
fn set_fc_py(&mut self, _fc: FuelConverter) -> PyResult<()> {
Err(PyAttributeError::new_err(DIRECT_SET_ERR))
}

#[setter(__fc)]
#[setter("__fc")]
fn set_fc_hidden(&mut self, fc: FuelConverter) -> PyResult<()> {
self.set_fc(fc).map_err(|e| PyAttributeError::new_err(e.to_string()))
}

// #[getter]
// fn get_res(&self) -> Option<ReversibleEnergyStorage> {
// self.reversible_energy_storage().cloned()
// }
// #[setter]
// fn set_res(&mut self, _res: ReversibleEnergyStorage) -> PyResult<()> {
// Err(PyAttributeError::new_err(DIRECT_SET_ERR))
// }
#[getter]
fn get_res(&self) -> Option<ReversibleEnergyStorage> {
self.res().cloned()
}
#[setter("res")]
fn set_res_py(&mut self, _res: ReversibleEnergyStorage) -> PyResult<()> {
Err(PyAttributeError::new_err(DIRECT_SET_ERR))
}
#[setter("__res")]
fn set_res_hidden(&mut self, res: ReversibleEnergyStorage) -> PyResult<()> {
self.set_res(res).map_err(|e| PyAttributeError::new_err(e.to_string()))
}

// #[setter(__res)]
// fn set_res_hidden(&mut self, res: ReversibleEnergyStorage) -> PyResult<()> {
// self.set_reversible_energy_storage(res).map_err(|e| PyAttributeError::new_err(e.to_string()))
// }
// #[getter]
// fn get_em(&self) -> ElectricMachine {
// self.em().clone()
// }
#[getter]
fn get_em(&self) -> Option<ElectricMachine> {
self.em().cloned()
}

// #[setter]
// fn set_em_py(&mut self, _em: ElectricMachine) -> PyResult<()> {
// Err(PyAttributeError::new_err(DIRECT_SET_ERR))
// }
// #[setter(__em)]
// fn set_em_hidden(&mut self, em: ElectricMachine) -> PyResult<()> {
// self.set_em(em).map_err(|e| PyAttributeError::new_err(e.to_string()))
// }
#[setter("em")]
fn set_em_py(&mut self, _em: ElectricMachine) -> PyResult<()> {
Err(PyAttributeError::new_err(DIRECT_SET_ERR))
}
#[setter("__em")]
fn set_em_hidden(&mut self, em: ElectricMachine) -> PyResult<()> {
self.set_em(em).map_err(|e| PyAttributeError::new_err(e.to_string()))
}

// fn veh_type(&self) -> PyResult<String> {
// Ok(self.pt_type.to_string())
// }
fn veh_type(&self) -> PyResult<String> {
Ok(self.pt_type.to_string())
}

// #[getter]
// fn get_pwr_rated_kilowatts(&self) -> f64 {
Expand Down
70 changes: 69 additions & 1 deletion python/fastsim/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
from pathlib import Path
from typing import Union, Any
from typing import Any, List
import numpy as np
import re
import inspect

from .fastsim import *
import fastsim as fsim

def package_root() -> Path:
"""Returns the package root directory."""
return Path(__file__).parent

def resources_root() -> Path:
"""
Returns the resources root directory.
"""
path = package_root() / "resources"
return path


from pkg_resources import get_distribution
__version__ = get_distribution("fastsim").version

Expand Down Expand Up @@ -73,4 +83,62 @@ def _get_list(path_elem, container):
def __array__(self):
return np.array(self.tolist())


# creates a list of all python classes from rust structs that need variable_path_list() and
# history_path_list() added as methods
ACCEPTED_RUST_STRUCTS = [attr for attr in fsim.__dir__() if not\
attr.startswith("__") and\
isinstance(getattr(fsim,attr), type) and\
attr[0].isupper() and\
("fastsim" in str(inspect.getmodule(getattr(fsim,attr))))]

def variable_path_list(self, path = "") -> List[str]:
"""Returns list of relative paths to all variables and sub-variables within
class (relative to the class the method was called on).
See example usage in `fastsim/demos/demo_variable_paths.py`.
Arguments: ----------
path : Defaults to empty string. This is mainly used within the method in
order to call the method recursively and should not be specified by user.
Specifies a path to be added in front of all paths returned by the method.
"""
variable_list = [attr for attr in self.__dir__() if not attr.startswith("__")\
and not callable(getattr(self,attr))]
variable_paths = []
for variable in variable_list:
if not type(getattr(self,variable)).__name__ in ACCEPTED_RUST_STRUCTS:
if path == "":
variable_path = variable
else:
variable_path = path + "." + variable
variable_paths.append(variable_path)
elif len([attr for attr in getattr(self,variable).__dir__() if not attr.startswith("__")\
and not callable(getattr(getattr(self,variable),attr))]) == 0:
if path == "":
variable_path = variable
else:
variable_path = path + "." + variable
variable_paths.append(variable_path)
else:
if path == "":
variable_path = variable
else:
variable_path = path + "." + variable
variable_paths.extend(getattr(self,variable).variable_path_list(path = variable_path))
return variable_paths

def history_path_list(self) -> List[str]:
"""Returns a list of relative paths to all history variables (all variables
that contain history as a subpath).
See example usage in `fastsim/demos/demo_variable_paths.py`."""
return [item for item in self.variable_path_list() if "history" in item]




setattr(Pyo3VecWrapper, "__array__", __array__) # noqa: F405

# adds variable_path_list() and history_path_list() as methods to all classes in
# ACCEPTED_RUST_STRUCTS
for item in ACCEPTED_RUST_STRUCTS:
setattr(getattr(fsim, item), "variable_path_list", variable_path_list)
setattr(getattr(fsim, item), "history_path_list", history_path_list)
2 changes: 1 addition & 1 deletion python/fastsim/demos/demo_conv.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

sns.set_theme()

SHOW_PLOTS = os.environ.get("SHOW_PLOTS", "true").lower() == "true"
SHOW_PLOTS = os.environ.get("SHOW_PLOTS", "true").lower() == "true"

# %% [markdown]

Expand Down
27 changes: 27 additions & 0 deletions python/fastsim/demos/demo_variable_paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""
Script demonstrating how to use variable_path_list() and history_path_list()
demos to find the paths to variables within fastsim classes.
"""

import fastsim as fsim

# load 2012 Ford Fusion from file
veh = fsim.Vehicle.from_file(
str(fsim.package_root() / "../../tests/assets/2016_TOYOTA_Prius_Two.yaml")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@robinsteuteville , switched to the prius to see if it'll work on HEVs, and there is an error with None, perhaps the same as what you encountered in ALTRIOS.

)

# Set `save_interval` at vehicle level -- cascades to all sub-components with time-varying states
veh.save_interval = 1

# load cycle from file
cyc = fsim.Cycle.from_resource("udds.csv")

# instantiate `SimDrive` simulation object
sd = fsim.SimDrive(veh, cyc)

# print out all subpaths for variables in SimDrive
print("List of variable paths for SimDrive:\n", "\n".join(sd.variable_path_list()))
print("\n")

# print out all subpaths for history variables in SimDrive
print("List of history variable paths for SimDrive:\n", "\n".join(sd.history_path_list()))
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
fc.history.i
fc.history.pwr_prop_max_watts
fc.history.pwr_tractive_watts
fc.history.energy_aux_joules
fc.history.pwr_fuel_watts
fc.history.energy_loss_joules
fc.history.fc_on
fc.history.pwr_aux_watts
fc.history.energy_tractive_joules
fc.history.energy_fuel_joules
fc.history.pwr_loss_watts
fc.history.eff
history
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
fc.eff_min
fc.pwr_out_frac_interp
fc.pwr_ramp_lag_seconds
fc.pwr_ramp_lag_hours
fc.pwr_idle_fuel_watts
fc.mass_kg
fc.eff_range
fc.state.pwr_loss_watts
fc.state.pwr_tractive_watts
fc.state.pwr_aux_watts
fc.state.pwr_fuel_watts
fc.state.energy_fuel_joules
fc.state.energy_tractive_joules
fc.state.eff
fc.state.energy_loss_joules
fc.state.energy_aux_joules
fc.state.fc_on
fc.state.pwr_prop_max_watts
fc.state.i
fc.eff_max
fc.specific_pwr_kw_per_kg
fc.pwr_out_max_watts
fc.history.i
fc.history.pwr_prop_max_watts
fc.history.pwr_tractive_watts
fc.history.energy_aux_joules
fc.history.pwr_fuel_watts
fc.history.energy_loss_joules
fc.history.fc_on
fc.history.pwr_aux_watts
fc.history.energy_tractive_joules
fc.history.energy_fuel_joules
fc.history.pwr_loss_watts
fc.history.eff
fc.pwr_out_max_init_watts
fc.eff_interp
fc.save_interval
pwr_aux_watts
trans_eff
state
chassis
year
save_interval
history
name
21 changes: 21 additions & 0 deletions python/fastsim/tests/test_variable_paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import unittest
import fastsim as fsim

class TestParamPath(unittest.TestCase):
def test_param_path_list(self):
# load 2012 Ford Fusion from file
veh = fsim.Vehicle.from_file(
str(fsim.package_root() / "../../tests/assets/2012_Ford_Fusion.yaml")
)
with open(fsim.resources_root() / "benchmark_variable_paths/vehicle_variable_paths.txt") as file:
baseline_variable_paths = [line.strip() for line in file.readlines()]

with open(fsim.resources_root() / "benchmark_variable_paths/vehicle_history_paths.txt") as file:
baseline_history_variable_paths = [line.strip() for line in file.readlines()]

assert(sorted(baseline_variable_paths)==sorted(veh.variable_path_list()))
assert(sorted(baseline_history_variable_paths)==sorted(veh.history_path_list()))


if __name__ == '__main__':
unittest.main()
Loading