From ff07e3bd7b5f2067a2c9271d0de7e7a257c23ba1 Mon Sep 17 00:00:00 2001 From: Chad Baker Date: Fri, 20 Sep 2024 10:58:25 -0600 Subject: [PATCH 1/3] added a single vehicle to the `from_resources` updated `list_resources` to live in the trait removed `resources` module `list_resources` ignores files with invalid extensions --- python/fastsim/__init__.py | 4 ++ rust/fastsim-cli/src/bin/fastsim-cli.rs | 6 +-- rust/fastsim-core/build.rs | 20 ++++++++ rust/fastsim-core/src/lib.rs | 1 - rust/fastsim-core/src/resources.rs | 41 ----------------- .../src/simdrive/simdrive_impl.rs | 1 - rust/fastsim-core/src/traits.rs | 46 +++++++++++++++++-- 7 files changed, 69 insertions(+), 50 deletions(-) delete mode 100644 rust/fastsim-core/src/resources.rs diff --git a/python/fastsim/__init__.py b/python/fastsim/__init__.py index 350bac73..70f17608 100644 --- a/python/fastsim/__init__.py +++ b/python/fastsim/__init__.py @@ -21,6 +21,10 @@ def package_root() -> Path: """Returns the package root directory.""" return Path(__file__).parent +def resources_root() -> Path: + """Returns the resources root directory.""" + return Path(__file__).parent / "resources" + DEFAULT_LOGGING_CONFIG = dict( format = "%(asctime)s.%(msecs)03d | %(filename)s:%(lineno)s | %(levelname)s: %(message)s", diff --git a/rust/fastsim-cli/src/bin/fastsim-cli.rs b/rust/fastsim-cli/src/bin/fastsim-cli.rs index 90e057aa..687c1176 100644 --- a/rust/fastsim-cli/src/bin/fastsim-cli.rs +++ b/rust/fastsim-cli/src/bin/fastsim-cli.rs @@ -117,8 +117,8 @@ pub fn calculate_mpgge_for_h2_diesel_ice( dist_mi: f64, max_fc_power_kw: f64, kwh_per_gge: f64, - fc_kw_out_ach: &Vec, - fs_kwh_out_ach: &Vec, + fc_kw_out_ach: &[f64], + fs_kwh_out_ach: &[f64], fc_pwr_out_perc: &Vec, h2share: &Vec, ) -> anyhow::Result { @@ -168,7 +168,7 @@ pub fn calculate_mpgge_for_h2_diesel_ice( }) } -pub fn integrate_power_to_kwh(dts_s: &Vec, ps_kw: &Vec) -> anyhow::Result> { +pub fn integrate_power_to_kwh(dts_s: &[f64], ps_kw: &[f64]) -> anyhow::Result> { anyhow::ensure!(dts_s.len() == ps_kw.len()); let mut energy_kwh = Vec::::with_capacity(dts_s.len()); for idx in 0..dts_s.len() { diff --git a/rust/fastsim-core/build.rs b/rust/fastsim-core/build.rs index f6c98cd3..b03c7657 100644 --- a/rust/fastsim-core/build.rs +++ b/rust/fastsim-core/build.rs @@ -23,6 +23,26 @@ fn main() { return; } + check_files_consistent(&prepath); + + // make sure at least one vehicle is available in the `from_resources` + let veh_path_in_python = PathBuf::from(format!( + // "{}/{}/vehdb/2017_Toyota_Highlander_3.5_L.yaml", + "{}/{}/vehdb/2017_Toyota_Highlander_3.5_L.yaml", + env::current_dir().unwrap().as_os_str().to_str().unwrap(), + prepath + )); + assert!(veh_path_in_python.exists()); + let veh_path_in_rust = PathBuf::from(format!( + "{}/resources/vehicles/2017_Toyota_Highlander_3.5_L.yaml", + env::current_dir().unwrap().as_os_str().to_str().unwrap() + )); + println!("{:?}", &veh_path_in_rust); + std::fs::copy(veh_path_in_python, veh_path_in_rust).unwrap(); +} + +/// Checks if rust and python resource files are consistent +fn check_files_consistent(prepath: &String) { let truth_files = [ format!( "{}/{}/longparams.json", diff --git a/rust/fastsim-core/src/lib.rs b/rust/fastsim-core/src/lib.rs index 35a1fb14..ca20e799 100644 --- a/rust/fastsim-core/src/lib.rs +++ b/rust/fastsim-core/src/lib.rs @@ -43,7 +43,6 @@ pub mod params; pub mod pyo3imports; pub mod simdrive; pub use simdrive::simdrive_impl; -pub mod resources; pub mod simdrivelabel; pub mod thermal; pub mod traits; diff --git a/rust/fastsim-core/src/resources.rs b/rust/fastsim-core/src/resources.rs deleted file mode 100644 index da2282cc..00000000 --- a/rust/fastsim-core/src/resources.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![cfg(feature = "resources")] - -use include_dir::{include_dir, Dir}; -pub const RESOURCES_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/resources"); - -/// List the available resources in the resources directory -/// - subdir: &str, a subdirectory to choose from the resources directory -/// NOTE: if subdir cannot be resolved, returns an empty list -/// RETURNS: a vector of strings for resources that can be loaded -pub fn list_resources(subdir: &str) -> Vec { - if subdir.is_empty() { - Vec::::new() - } else if let Some(resources_path) = RESOURCES_DIR.get_dir(subdir) { - let mut file_names: Vec = resources_path - .files() - .filter_map(|entry| entry.path().file_name()?.to_str().map(String::from)) - .collect(); - file_names.sort(); - file_names - } else { - Vec::::new() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_list_resources() { - let result = list_resources("cycles"); - assert!(result.len() == 3); - assert!(result[0] == "HHDDTCruiseSmooth.csv"); - // NOTE: at the time of writing this test, there is no - // vehicles subdirectory. The agreed-upon behavior in - // that case is that list_resources should return an - // empty vector of string. - let another_result = list_resources("vehicles"); - assert!(another_result.len() == 0); - } -} diff --git a/rust/fastsim-core/src/simdrive/simdrive_impl.rs b/rust/fastsim-core/src/simdrive/simdrive_impl.rs index 4a94db7f..82cdb2b8 100644 --- a/rust/fastsim-core/src/simdrive/simdrive_impl.rs +++ b/rust/fastsim-core/src/simdrive/simdrive_impl.rs @@ -1856,7 +1856,6 @@ impl RustSimDrive { (self.dist_m.sum() - dist_m).abs() / dist_m } else { bail!("Vehicle did not move forward."); - 0.0 }; self.trace_miss_time_frac = (self .cyc diff --git a/rust/fastsim-core/src/traits.rs b/rust/fastsim-core/src/traits.rs index 8c93c963..f37ff10c 100644 --- a/rust/fastsim-core/src/traits.rs +++ b/rust/fastsim-core/src/traits.rs @@ -1,7 +1,11 @@ -use crate::{imports::*, resources}; +use crate::imports::*; +use include_dir::{include_dir, Dir}; use std::collections::HashMap; +use std::path::PathBuf; use ureq; +pub const RESOURCES_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/resources"); + pub trait SerdeAPI: Serialize + for<'a> Deserialize<'a> { const ACCEPTED_BYTE_FORMATS: &'static [&'static str] = &["yaml", "json", "toml", "bin"]; const ACCEPTED_STR_FORMATS: &'static [&'static str] = &["yaml", "json", "toml"]; @@ -14,11 +18,25 @@ pub trait SerdeAPI: Serialize + for<'a> Deserialize<'a> { } /// List available (compiled) resources (stored in the rust binary) - /// RESULT: + /// # RESULT /// vector of string of resource names that can be loaded #[cfg(feature = "resources")] fn list_resources() -> Vec { - resources::list_resources(Self::RESOURCE_PREFIX) + if Self::RESOURCE_PREFIX.is_empty() { + Vec::::new() + } else if let Some(resources_path) = RESOURCES_DIR.get_dir(Self::RESOURCE_PREFIX) { + let mut file_names: Vec = resources_path + .files() + .filter_map(|entry| entry.path().file_name()?.to_str().map(String::from)) + .collect(); + file_names.retain(|f| { + Self::ACCEPTED_STR_FORMATS.contains(&f.split(".").last().unwrap_or_default()) + }); + file_names.sort(); + file_names + } else { + Vec::::new() + } } /// Read (deserialize) an object from a resource file packaged with the `fastsim-core` crate @@ -33,7 +51,7 @@ pub trait SerdeAPI: Serialize + for<'a> Deserialize<'a> { .extension() .and_then(OsStr::to_str) .with_context(|| format!("File extension could not be parsed: {filepath:?}"))?; - let file = crate::resources::RESOURCES_DIR + let file = RESOURCES_DIR .get_file(&filepath) .with_context(|| format!("File not found in resources: {filepath:?}"))?; Self::from_reader(file.contents(), extension, skip_init) @@ -434,3 +452,23 @@ impl IterMaxMin for Array1 { }) } } + +#[cfg(test)] +mod tests { + use crate::imports::SerdeAPI; + + #[test] + fn test_list_resources() { + let cyc_resource_list = crate::cycle::RustCycle::list_resources(); + assert!(cyc_resource_list.len() == 3); + assert!(cyc_resource_list[0] == "HHDDTCruiseSmooth.csv"); + // NOTE: at the time of writing this test, there is no + // vehicles subdirectory. The agreed-upon behavior in + // that case is that list_resources should return an + // empty vector of string. + let veh_resource_list = crate::vehicle::RustVehicle::list_resources(); + println!("{:?}", veh_resource_list); + assert!(veh_resource_list.len() == 1); + assert!(veh_resource_list[0] == "2017_Toyota_Highlander_3.5_L.yaml") + } +} From 042f283ad6194f93d035919607e5ac5ad6c40e39 Mon Sep 17 00:00:00 2001 From: Chad Baker Date: Fri, 20 Sep 2024 11:23:28 -0600 Subject: [PATCH 2/3] add feature gate Co-authored-by: Kyle Carow <40699307+kylecarow@users.noreply.github.com> --- rust/fastsim-core/src/traits.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/fastsim-core/src/traits.rs b/rust/fastsim-core/src/traits.rs index f37ff10c..f40fa8c3 100644 --- a/rust/fastsim-core/src/traits.rs +++ b/rust/fastsim-core/src/traits.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use std::path::PathBuf; use ureq; +#[cfg(feature = "resources")] pub const RESOURCES_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/resources"); pub trait SerdeAPI: Serialize + for<'a> Deserialize<'a> { From 0577b9bf9367c5af86c131d6c51de422b8256898 Mon Sep 17 00:00:00 2001 From: Chad Baker Date: Fri, 20 Sep 2024 11:26:46 -0600 Subject: [PATCH 3/3] commited vehicle and build now checks that's it correct rather than copying added feature gate --- python/fastsim/tests/test_resources.py | 8 +- rust/fastsim-core/build.rs | 24 +-- .../2017_Toyota_Highlander_3.5_L.yaml | 168 ++++++++++++++++++ rust/fastsim-core/src/traits.rs | 1 + 4 files changed, 179 insertions(+), 22 deletions(-) create mode 100644 rust/fastsim-core/resources/vehicles/2017_Toyota_Highlander_3.5_L.yaml diff --git a/python/fastsim/tests/test_resources.py b/python/fastsim/tests/test_resources.py index 44e888c9..5e965d1a 100644 --- a/python/fastsim/tests/test_resources.py +++ b/python/fastsim/tests/test_resources.py @@ -16,12 +16,6 @@ def test_list_resources_for_cycle(self): def test_list_resources_for_vehicles(self): "check if list_resources works for RustVehicle" - # NOTE: at the time of writing this test, - # there are no vehicle assets in resources. - # Therefore, we expect to get an empty vector. - # If resources are committed, this test should - # fail and we should use the following assert: - # self.assertTrue(len(resources) > 0) rv = vehicle.Vehicle.from_vehdb(1).to_rust() resources = rv.list_resources() - self.assertTrue(len(resources) == 0) + self.assertTrue(len(resources) == 1) diff --git a/rust/fastsim-core/build.rs b/rust/fastsim-core/build.rs index b03c7657..7a54d3b9 100644 --- a/rust/fastsim-core/build.rs +++ b/rust/fastsim-core/build.rs @@ -24,26 +24,16 @@ fn main() { } check_files_consistent(&prepath); - - // make sure at least one vehicle is available in the `from_resources` - let veh_path_in_python = PathBuf::from(format!( - // "{}/{}/vehdb/2017_Toyota_Highlander_3.5_L.yaml", - "{}/{}/vehdb/2017_Toyota_Highlander_3.5_L.yaml", - env::current_dir().unwrap().as_os_str().to_str().unwrap(), - prepath - )); - assert!(veh_path_in_python.exists()); - let veh_path_in_rust = PathBuf::from(format!( - "{}/resources/vehicles/2017_Toyota_Highlander_3.5_L.yaml", - env::current_dir().unwrap().as_os_str().to_str().unwrap() - )); - println!("{:?}", &veh_path_in_rust); - std::fs::copy(veh_path_in_python, veh_path_in_rust).unwrap(); } /// Checks if rust and python resource files are consistent fn check_files_consistent(prepath: &String) { let truth_files = [ + format!( + "{}/{}/vehdb/2017_Toyota_Highlander_3.5_L.yaml", + env::current_dir().unwrap().as_os_str().to_str().unwrap(), + prepath + ), format!( "{}/{}/longparams.json", env::current_dir().unwrap().as_os_str().to_str().unwrap(), @@ -62,6 +52,10 @@ fn check_files_consistent(prepath: &String) { ]; let compare_files = [ + format!( + "{}/resources/vehicles/2017_Toyota_Highlander_3.5_L.yaml", + env::current_dir().unwrap().as_os_str().to_str().unwrap() + ), format!( "{}/resources/longparams.json", env::current_dir().unwrap().as_os_str().to_str().unwrap() diff --git a/rust/fastsim-core/resources/vehicles/2017_Toyota_Highlander_3.5_L.yaml b/rust/fastsim-core/resources/vehicles/2017_Toyota_Highlander_3.5_L.yaml new file mode 100644 index 00000000..0ab0286c --- /dev/null +++ b/rust/fastsim-core/resources/vehicles/2017_Toyota_Highlander_3.5_L.yaml @@ -0,0 +1,168 @@ +--- +scenario_name: 2017 Toyota Highlander 3.5 L +selection: 0 +veh_year: 2017 +veh_pt_type: Conv +drag_coef: 0.1966 +frontal_area_m2: 6.0 +glider_kg: 791.0702892 +veh_cg_m: 0.53 +drive_axle_weight_frac: 0.59 +wheel_base_m: 2.72 +cargo_kg: 136.0 +veh_override_kg: 2270.0 +comp_mass_multiplier: 1.4 +fs_max_kw: 1000.0 +fs_secs_to_peak_pwr: 1.0 +fs_kwh: 590.0 +fs_kwh_per_kg: 9.89473291 +fc_max_kw: 220.0 +fc_pwr_out_perc: + v: 1 + dim: + - 12 + data: + - 0.0 + - 0.005 + - 0.015 + - 0.04 + - 0.06 + - 0.1 + - 0.14 + - 0.2 + - 0.4 + - 0.6 + - 0.8 + - 1.0 +fc_eff_map: + v: 1 + dim: + - 12 + data: + - 0.1 + - 0.12 + - 0.16 + - 0.22 + - 0.28 + - 0.33 + - 0.35 + - 0.36 + - 0.35 + - 0.34 + - 0.32 + - 0.3 +fc_eff_type: SI +fc_sec_to_peak_pwr: 6.0 +fc_base_kg: 61.0 +fc_kw_per_kg: 0.47 +min_fc_time_on: 30.0 +idle_fc_kw: 0.0 +mc_max_kw: 0.0 +mc_pwr_out_perc: + v: 1 + dim: + - 11 + data: + - 0.0 + - 0.02 + - 0.04 + - 0.06 + - 0.08 + - 0.1 + - 0.2 + - 0.4 + - 0.6 + - 0.8 + - 1.0 +mc_eff_map: + v: 1 + dim: + - 11 + data: + - 0.12 + - 0.16 + - 0.21 + - 0.29 + - 0.35 + - 0.42 + - 0.75 + - 0.92 + - 0.93 + - 0.93 + - 0.92 +mc_sec_to_peak_pwr: 5.0 +mc_pe_kg_per_kw: 0.83 +mc_pe_base_kg: 21.6 +ess_max_kw: 0.0 +ess_max_kwh: 0.0 +ess_kg_per_kwh: 44.0 +ess_base_kg: 0.0 +ess_round_trip_eff: 0.834 +ess_life_coef_a: 110.0 +ess_life_coef_b: -0.68 +min_soc: 0.4 +max_soc: 0.8 +ess_dischg_to_fc_max_eff_perc: 0.0 +ess_chg_to_fc_max_eff_perc: 0.0 +wheel_inertia_kg_m2: 0.82 +num_wheels: 4.0 +wheel_rr_coef: 0.009449 +wheel_radius_m: 0.326 +wheel_coef_of_fric: 0.7 +max_accel_buffer_mph: 60.0 +max_accel_buffer_perc_of_useable_soc: 0.2 +perc_high_acc_buf: 0.0 +mph_fc_on: 47.0 +kw_demand_fc_on: 100.0 +max_regen: 0.9 +stop_start: false +force_aux_on_fc: false +alt_eff: 1.0 +chg_eff: 0.86 +aux_kw: 0.7 +trans_kg: 114.0 +trans_eff: 0.875 +ess_to_fuel_ok_error: 0.005 +small_motor_power_kw: 7.5 +large_motor_power_kw: 75.0 +regen_a: 500.0 +regen_b: 0.99 +charging_on: false +max_roadway_chg_kw: + v: 1 + dim: + - 6 + data: + - 0.0 + - 0.0 + - 0.0 + - 0.0 + - 0.0 + - 0.0 +modern_max: 0.95 +veh_kg: 2270.0 +max_trac_mps2: 3.5652415399547075 +ess_mass_kg: 0.0 +mc_mass_kg: 0.0 +fc_mass_kg: 0.0 +fs_mass_kg: 0.0 +val_udds_mpgge: 22.0 +val_hwy_mpgge: 32.0 +val_comb_mpgge: 25.6 +val_udds_kwh_per_mile: .nan +val_hwy_kwh_per_mile: .nan +val_comb_kwh_per_mile: .nan +val_cd_range_mi: .nan +val_const65_mph_kwh_per_mile: .nan +val_const60_mph_kwh_per_mile: .nan +val_const55_mph_kwh_per_mile: .nan +val_const45_mph_kwh_per_mile: .nan +val_unadj_udds_kwh_per_mile: .nan +val_unadj_hwy_kwh_per_mile: .nan +val0_to60_mph: .nan +val_ess_life_miles: .nan +val_range_miles: .nan +val_veh_base_cost: .nan +val_msrp: .nan +fc_peak_eff_override: ~ +mc_peak_eff_override: ~ diff --git a/rust/fastsim-core/src/traits.rs b/rust/fastsim-core/src/traits.rs index f40fa8c3..82551760 100644 --- a/rust/fastsim-core/src/traits.rs +++ b/rust/fastsim-core/src/traits.rs @@ -459,6 +459,7 @@ mod tests { use crate::imports::SerdeAPI; #[test] + #[cfg(feature = "resources")] fn test_list_resources() { let cyc_resource_list = crate::cycle::RustCycle::list_resources(); assert!(cyc_resource_list.len() == 3);