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

F2/pyo3=0.23 #177

Merged
merged 5 commits into from
Jan 3, 2025
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
3 changes: 2 additions & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
resolver = "2"
package.rust-version = "1.83"

members = [
"fastsim-cli", # command line app
Expand All @@ -17,7 +18,7 @@ codegen-units = 1 # optimize connection between modules
[workspace.dependencies]
anyhow = "1.0.57"
ndarray = { version = "0.15.4", features = ["serde"] }
pyo3 = "0.19"
pyo3 = "0.23.3"
pyo3-log = "*"
serde = "1.0.143"
serde_json = "1.0.83"
Expand Down
1 change: 1 addition & 0 deletions rust/fastsim-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name = "fastsim-cli"
version = "0.1.0"
edition = "2021"
rust-version.workspace = true
license = "Apache-2.0"
authors = ["NREL/MTES/CIMS/MBAP Group <[email protected]>"]
description = "CLI app for FASTSim models for vehicle energy usage simulation"
Expand Down
4 changes: 2 additions & 2 deletions rust/fastsim-cli/src/bin/fastsim-cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,11 +367,11 @@ struct ArrayObject {

/// Takes a vector of floats and transforms it into an object representation
/// used by the ndarray library.
fn array_to_object_representation(xs: &Vec<f64>) -> ArrayObject {
fn array_to_object_representation(xs: &[f64]) -> ArrayObject {
ArrayObject {
v: 1,
dim: vec![xs.len()],
data: xs.clone(),
data: xs.to_vec().clone(),
}
}

Expand Down
1 change: 1 addition & 0 deletions rust/fastsim-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name = "fastsim-core"
version = "0.1.7"
edition = "2021"
rust-version.workspace = true
license = "Apache-2.0"
authors = ["NREL/MTES/CIMS/MBAP Group <[email protected]>"]
description = "Core FASTSim models for vehicle energy usage simulation"
Expand Down
64 changes: 39 additions & 25 deletions rust/fastsim-core/fastsim-proc-macros/src/add_pyo3_api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ pub fn add_pyo3_api(attr: TokenStream, item: TokenStream) -> TokenStream {
py_impl_block.extend::<TokenStream2>(quote! {
pub fn copy(&self) -> Self {self.clone()}
pub fn __copy__(&self) -> Self {self.clone()}
pub fn __deepcopy__(&self, _memo: &PyDict) -> Self {self.clone()}
pub fn __deepcopy__(&self, _memo: &Bound<PyDict>) -> Self {self.clone()}

/// Read (deserialize) an object from a resource file packaged with the `fastsim-core` crate
///
Expand All @@ -215,8 +215,12 @@ pub fn add_pyo3_api(attr: TokenStream, item: TokenStream) -> TokenStream {
#[cfg(feature = "resources")]
#[staticmethod]
#[pyo3(name = "from_resource")]
pub fn from_resource_py(filepath: &PyAny, skip_init: Option<bool>) -> PyResult<Self> {
Self::from_resource(PathBuf::extract(filepath)?, skip_init.unwrap_or_default()).map_err(|e| PyIOError::new_err(format!("{:?}", e)))
#[pyo3(signature = (filepath, skip_init=None))]
pub fn from_resource_py(filepath: &Bound<PyAny>, skip_init: Option<bool>) -> PyResult<Self> {
Self::from_resource(
PathBuf::extract_bound(filepath)?,
skip_init.unwrap_or_default()
).map_err(|e| PyIOError::new_err(format!("{:?}", e)))
}

/// Write (serialize) an object to a file.
Expand All @@ -228,8 +232,10 @@ pub fn add_pyo3_api(attr: TokenStream, item: TokenStream) -> TokenStream {
/// * `filepath`: `str | pathlib.Path` - The filepath at which to write the object
///
#[pyo3(name = "to_file")]
pub fn to_file_py(&self, filepath: &PyAny) -> PyResult<()> {
self.to_file(PathBuf::extract(filepath)?).map_err(|e| PyIOError::new_err(format!("{:?}", e)))
pub fn to_file_py(&self, filepath: &Bound<PyAny>) -> PyResult<()> {
self.to_file(
PathBuf::extract_bound(filepath)?
).map_err(|e| PyIOError::new_err(format!("{:?}", e)))
}

/// Read (deserialize) an object from a file.
Expand All @@ -241,8 +247,13 @@ pub fn add_pyo3_api(attr: TokenStream, item: TokenStream) -> TokenStream {
///
#[staticmethod]
#[pyo3(name = "from_file")]
pub fn from_file_py(filepath: &PyAny, skip_init: Option<bool>) -> PyResult<Self> {
Self::from_file(PathBuf::extract(filepath)?, skip_init.unwrap_or_default()).map_err(|e| PyIOError::new_err(format!("{:?}", e)))
#[pyo3(signature = (filepath, skip_init=None))]
pub fn from_file_py(filepath: &Bound<PyAny>, skip_init: Option<bool>) -> PyResult<Self> {
Self::from_file(
PathBuf::extract_bound(filepath)?,
skip_init.unwrap_or_default()
)
.map_err(|e| PyIOError::new_err(format!("{:?}", e)))
}

/// Write (serialize) an object into a string
Expand All @@ -265,6 +276,7 @@ pub fn add_pyo3_api(attr: TokenStream, item: TokenStream) -> TokenStream {
///
#[staticmethod]
#[pyo3(name = "from_str")]
#[pyo3(signature = (contents, format, skip_init=None))]
pub fn from_str_py(contents: &str, format: &str, skip_init: Option<bool>) -> PyResult<Self> {
Self::from_str(contents, format, skip_init.unwrap_or_default()).map_err(|e| PyIOError::new_err(format!("{:?}", e)))
}
Expand All @@ -283,6 +295,7 @@ pub fn add_pyo3_api(attr: TokenStream, item: TokenStream) -> TokenStream {
///
#[staticmethod]
#[pyo3(name = "from_json")]
#[pyo3(signature = (json_str, skip_init=None))]
pub fn from_json_py(json_str: &str, skip_init: Option<bool>) -> PyResult<Self> {
Self::from_json(json_str, skip_init.unwrap_or_default()).map_err(|e| PyIOError::new_err(format!("{:?}", e)))
}
Expand All @@ -301,29 +314,30 @@ pub fn add_pyo3_api(attr: TokenStream, item: TokenStream) -> TokenStream {
///
#[staticmethod]
#[pyo3(name = "from_yaml")]
#[pyo3(signature = (yaml_str, skip_init=None))]
pub fn from_yaml_py(yaml_str: &str, skip_init: Option<bool>) -> PyResult<Self> {
Self::from_yaml(yaml_str, skip_init.unwrap_or_default()).map_err(|e| PyIOError::new_err(format!("{:?}", e)))
}

/// Write (serialize) an object to bincode-encoded `bytes`
#[cfg(feature = "bincode")]
#[pyo3(name = "to_bincode")]
pub fn to_bincode_py<'py>(&self, py: Python<'py>) -> PyResult<&'py PyBytes> {
PyResult::Ok(PyBytes::new(py, &self.to_bincode()?)).map_err(|e| PyIOError::new_err(format!("{:?}", e)))
}
// /// Write (serialize) an object to bincode-encoded `bytes`
// #[cfg(feature = "bincode")]
// #[pyo3(name = "to_bincode")]
// pub fn to_bincode_py<'py>(&self, py: Python<'py>) -> PyResult<&'py PyBytes> {
// PyResult::Ok(PyBytes::new(py, &self.to_bincode()?)).map_err(|e| PyIOError::new_err(format!("{:?}", e)))
// }

/// Read (deserialize) an object from bincode-encoded `bytes`
///
/// # Arguments
///
/// * `encoded`: `bytes` - Encoded bytes to deserialize from
///
#[cfg(feature = "bincode")]
#[staticmethod]
#[pyo3(name = "from_bincode")]
pub fn from_bincode_py(encoded: &PyBytes, skip_init: Option<bool>) -> PyResult<Self> {
Self::from_bincode(encoded.as_bytes(), skip_init.unwrap_or_default()).map_err(|e| PyIOError::new_err(format!("{:?}", e)))
}
// /// Read (deserialize) an object from bincode-encoded `bytes`
// ///
// /// # Arguments
// ///
// /// * `encoded`: `bytes` - Encoded bytes to deserialize from
// ///
// #[cfg(feature = "bincode")]
// #[staticmethod]
// #[pyo3(name = "from_bincode")]
// pub fn from_bincode_py(encoded: &PyBytes, skip_init: Option<bool>) -> PyResult<Self> {
// Self::from_bincode(encoded.as_bytes(), skip_init.unwrap_or_default()).map_err(|e| PyIOError::new_err(format!("{:?}", e)))
// }
});

let impl_block = quote! {
Expand Down
39 changes: 27 additions & 12 deletions rust/fastsim-core/src/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ pub fn extend_cycle(

#[cfg(feature = "pyo3")]
#[allow(unused)]
pub fn register(_py: Python<'_>, m: &PyModule) -> anyhow::Result<()> {
pub fn register(_py: Python<'_>, m: &Bound<PyModule>) -> anyhow::Result<()> {
m.add_function(wrap_pyfunction!(calc_constant_jerk_trajectory, m)?)?;
m.add_function(wrap_pyfunction!(accel_for_constant_jerk, m)?)?;
m.add_function(wrap_pyfunction!(speed_for_constant_jerk, m)?)?;
Expand Down Expand Up @@ -487,32 +487,46 @@ impl RustCycleCache {

#[staticmethod]
#[pyo3(name = "from_csv")]
pub fn from_csv_py(filepath: &PyAny, skip_init: Option<bool>) -> anyhow::Result<Self> {
Self::from_csv_file(PathBuf::extract(filepath)?, skip_init.unwrap_or_default())
#[pyo3(signature = (filepath, skip_init=None))]
pub fn from_csv_py(filepath: &Bound<PyAny>, skip_init: Option<bool>) -> anyhow::Result<Self> {
Self::from_csv_file(PathBuf::extract_bound(filepath)?, skip_init.unwrap_or_default())
}

pub fn to_rust(&self) -> Self {
self.clone()
}

#[staticmethod]
pub fn from_dict(dict: &PyDict, skip_init: Option<bool>) -> anyhow::Result<Self> {
let time_s = Array::from_vec(PyAny::get_item(dict, "time_s")?.extract()?);
#[pyo3(signature = (dict, skip_init=None))]
pub fn from_dict(dict: &Bound<PyDict>, skip_init: Option<bool>) -> PyResult<Self> {
let time_s = Array::from_vec(dict.get_item("time_s")?.with_context(|| format_dbg!())?.extract()?);
let cyc_len = time_s.len();
let mut cyc = Self {
time_s,
mps: Array::from_vec(PyAny::get_item(dict, "mps")?.extract()?),
grade: if let Ok(value) = PyAny::get_item(dict, "grade") {
Array::from_vec(value.extract()?)
mps: Array::from_vec(dict.get_item("mps")?.with_context(|| format_dbg!())?.extract()?),
grade: if let Ok(Some(item_res)) = dict.get_item("grade") {
if let Ok(grade) = item_res.extract() {
Array::from_vec(grade)
} else {
Array::default(cyc_len)
}
} else {
Array::default(cyc_len)
},
road_type: if let Ok(value) = PyAny::get_item(dict, "road_type") {
Array::from_vec(value.extract()?)
road_type: if let Ok(Some(item_res)) = dict.get_item("road_type") {
if let Ok(road_type) = item_res.extract() {
Array::from_vec(road_type)
} else {
Array::default(cyc_len)
}
} else {
Array::default(cyc_len)
},
name: PyAny::get_item(dict, "name").and_then(String::extract).unwrap_or_default(),
name: if let Ok(Some(item_res)) = dict.get_item("name") {
String::extract_bound(&item_res).unwrap_or_default()
} else {
Default::default()
},
orphaned: false,
};
if !skip_init.unwrap_or_default() {
Expand All @@ -521,7 +535,7 @@ impl RustCycleCache {
Ok(cyc)
}

pub fn to_dict<'py>(&self, py: Python<'py>) -> anyhow::Result<&'py PyDict> {
pub fn to_dict<'py>(&self, py: Python<'py>) -> anyhow::Result<Bound<'py, PyDict>> {
let dict = PyDict::new(py);
dict.set_item("time_s", self.time_s.to_vec())?;
dict.set_item("mps", self.mps.to_vec())?;
Expand All @@ -548,6 +562,7 @@ impl RustCycleCache {
}

#[pyo3(name = "modify_with_braking_trajectory")]
#[pyo3(signature = (brake_accel_m_per_s2, idx, dts_m=None))]
pub fn modify_with_braking_trajectory_py(
&mut self,
brake_accel_m_per_s2: f64,
Expand Down
36 changes: 0 additions & 36 deletions rust/fastsim-core/src/macros.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,3 @@
/// Given pairs of arbitrary keys and values, prints "key: value" to python intepreter.
/// Given str, prints str.
/// Using this will break `cargo test` but work with `maturin develop`.
#[macro_export]
macro_rules! print_to_py {
( $( $x:expr, $y:expr ),* ) => {
{
pyo3::Python::with_gil(|py| {
let locals = pyo3::types::PyDict::new(py);
$(
locals.set_item($x, $y).unwrap();
py.run(
&format!("print(f\"{}: {{{}:.3g}}\")", $x, $x),
None,
Some(locals),
)
.expect(&format!("printing `{}` failed", $x));
)*
});
};
};
( $x:expr ) => {
{
// use pyo3::py_run;
pyo3::Python::with_gil(|py| {
py.run(
&format!("print({})", $x),
None,
None,
)
.expect(&format!("printing `{}` failed", $x));
});
};
}
}

#[macro_export]
macro_rules! check_orphaned_and_set {
($struct_self: ident, $field: ident, $value: expr) => {
Expand Down
4 changes: 4 additions & 0 deletions rust/fastsim-core/src/simdrive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ impl Default for RustSimDriveParams {
}

#[pyo3(name = "sim_drive")]
#[pyo3(signature = (init_soc=None, aux_in_kw_override=None))]
/// Initialize and run sim_drive_walk as appropriate for vehicle attribute vehPtType.
/// Arguments
/// ------------
Expand All @@ -213,6 +214,7 @@ impl Default for RustSimDriveParams {
self.sim_drive(init_soc, aux_in_kw_override)
}

#[pyo3(signature = (init_soc, aux_in_kw_override=None))]
/// Receives second-by-second cycle information, vehicle properties,
/// and an initial state of charge and runs sim_drive_step to perform a
/// backward facing powertrain simulation. Method 'sim_drive' runs this
Expand All @@ -233,6 +235,7 @@ impl Default for RustSimDriveParams {
self.walk(init_soc, aux_in_kw_override)
}

#[pyo3(signature = (by_microtrip=None, extend_fraction=None, blend_factor=None, min_target_speed_m_per_s=None))]
/// Sets the intelligent driver model parameters for an eco-cruise driving trajectory.
/// This is a convenience method instead of setting the sim_params.idm* parameters yourself.
/// - by_microtrip: bool, if True, target speed is set by microtrip, else by cycle
Expand All @@ -257,6 +260,7 @@ impl Default for RustSimDriveParams {
}

#[pyo3(name = "init_for_step")]
#[pyo3(signature = (init_soc, aux_in_kw_override=None))]
/// This is a specialty method which should be called prior to using
/// sim_drive_step in a loop.
/// Arguments
Expand Down
1 change: 1 addition & 0 deletions rust/fastsim-core/src/simdrive/simdrive_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use rayon::prelude::*;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[add_pyo3_api(
#[pyo3(name = "sim_drive")]
#[pyo3(signature = (parallelize=None))]
/// Calls `sim_drive` method for each simdrive instance in vec.
/// # Arguments:
/// * parallelize: whether to parallelize `sim_drive` calls, defaults to `true`
Expand Down
Loading
Loading