From 180b7d24ea1351b3bc366b80070ce5fe630b4d3c Mon Sep 17 00:00:00 2001 From: Kyle Carow Date: Tue, 24 Feb 2026 11:49:34 -0700 Subject: [PATCH 1/2] add simparams input to simdrivelabel functions, python api is missing still --- fastsim-core/src/simdrivelabel.rs | 56 ++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/fastsim-core/src/simdrivelabel.rs b/fastsim-core/src/simdrivelabel.rs index fdfac639a..52e8b60f0 100644 --- a/fastsim-core/src/simdrivelabel.rs +++ b/fastsim-core/src/simdrivelabel.rs @@ -1,10 +1,12 @@ //! Module containing classes and methods for calculating label fuel economy. use std::collections::HashMap; +use std::hash::Hash; // crate local use crate::drive_cycle::{Cycle, CYC_ACCEL}; use crate::imports::*; +use crate::prelude::SimParams; use crate::simdrive::SimDrive; use crate::vehicle::{PowertrainType, Vehicle}; @@ -54,8 +56,15 @@ pub fn get_0_to_60_time_from_accel_data(accel_data: &AccelData) -> anyhow::Resul } /// Run the acceleration test and return the time/speed trace. -pub fn run_accel(veh: &Vehicle) -> anyhow::Result { - let mut sd_accel = SimDrive::new(veh.clone(), CYC_ACCEL.clone(), None); +pub fn run_accel( + veh: &Vehicle, + sim_params: &HashMap<&str, SimParams>, +) -> anyhow::Result { + let mut sd_accel = SimDrive::new( + veh.clone(), + CYC_ACCEL.clone(), + sim_params.get("accel").cloned(), + ); sd_accel.sim_params.trace_miss_opts = TraceMissOptions::Allow; sd_accel.walk_once().with_context(|| format_dbg!())?; // Extract speed values in mph @@ -995,12 +1004,13 @@ fn run_simdrive_with_init_soc( /// Runs the appropriate simulations required for calculating /// the label fuel economy for the given vehicle. /// NOTE: does not run the acceleration test. -pub fn run_label_simulations( - veh: &mut Vehicle, +pub fn run_label_simulations<'a>( + veh: &'a mut Vehicle, // max_epa_adj: Option, fuel_props: Option, phev_utilization_params: Option, -) -> anyhow::Result<(SimulationDataForLabel, HashMap<&str, SimDrive>)> { + sim_params: &HashMap<&'a str, SimParams>, +) -> anyhow::Result<(SimulationDataForLabel, HashMap<&'a str, SimDrive>)> { // let max_epa_adj = max_epa_adj.unwrap_or(0.3); let phev_utilization_params = &phev_utilization_params.unwrap_or_default(); let fuel_props = fuel_props.unwrap_or_default(); @@ -1025,9 +1035,20 @@ pub fn run_label_simulations( // run simdrive for non-phev powertrains sd.insert( "udds", - SimDrive::new(veh.clone(), cyc["udds"].clone(), None), + SimDrive::new( + veh.clone(), + cyc["udds"].clone(), + sim_params.get("udds").cloned(), + ), + ); + sd.insert( + "hwy", + SimDrive::new( + veh.clone(), + cyc["hwy"].clone(), + sim_params.get("hwy").cloned(), + ), ); - sd.insert("hwy", SimDrive::new(veh.clone(), cyc["hwy"].clone(), None)); for (k, val) in sd.iter_mut() { val.walk().map_err(|e| { @@ -1300,25 +1321,29 @@ pub fn run_label_simulations( /// /// Returns label fuel economy values as a struct and (optionally) /// simdrive::SimDrive objects. -pub fn get_label_fe( - veh: &mut Vehicle, +pub fn get_label_fe<'a>( + veh: &'a mut Vehicle, max_epa_adj: Option, full_detail: bool, fuel_props: Option, phev_utilization_params: Option, + sim_params: Option>, verbose: bool, -) -> anyhow::Result<(LabelFe, Option>)> { +) -> anyhow::Result<(LabelFe, Option>)> { let max_epa_adj = max_epa_adj.unwrap_or(0.3); let phev_utilization_params = &phev_utilization_params.unwrap_or_default(); let fuel_props = fuel_props.unwrap_or_default(); let veh_copy = veh.clone(); + let sim_params = sim_params.unwrap_or_default(); + let (sim_data, sd) = run_label_simulations( veh, Some(fuel_props.clone()), Some(phev_utilization_params.clone()), + &sim_params, )?; - let accel_data = run_accel(&veh_copy)?; + let accel_data = run_accel(&veh_copy, &sim_params)?; let mut label_fe = calculate_label_fuel_economy( &fuel_props, phev_utilization_params, @@ -1363,6 +1388,7 @@ pub fn get_label_fe_py( full_detail.unwrap_or_default(), fuel_props, phev_utilization_params, + None, // TODO: allow sim_params input from Python when SimParams has a better Python interface verbose.unwrap_or_default(), )?; Ok(label_fe) @@ -2022,7 +2048,7 @@ mod tests { let mut veh = Vehicle::try_from(f2veh.clone()).unwrap(); // Get FASTSim-3 label FE results - let (label_fe_f3, _) = get_label_fe(&mut veh, None, false, None, None, false) + let (label_fe_f3, _) = get_label_fe(&mut veh, None, false, None, None, None, false) .with_context(|| format_dbg!()) .unwrap(); @@ -2057,7 +2083,7 @@ mod tests { let mut veh = Vehicle::try_from(f2veh.clone()).unwrap(); // Get FASTSim-3 label FE results - let (label_fe_f3, _) = get_label_fe(&mut veh, None, false, None, None, false) + let (label_fe_f3, _) = get_label_fe(&mut veh, None, false, None, None, None, false) .with_context(|| format_dbg!()) .unwrap(); @@ -2091,7 +2117,7 @@ mod tests { let mut veh = Vehicle::try_from(f2veh.clone()).unwrap(); // Get FASTSim-3 label FE results - let (label_fe_f3, _) = get_label_fe(&mut veh, None, false, None, None, false) + let (label_fe_f3, _) = get_label_fe(&mut veh, None, false, None, None, None, false) .with_context(|| format_dbg!()) .unwrap(); @@ -2148,7 +2174,7 @@ mod tests { ); // Get FASTSim-3 label FE results (if PHEV functionality is implemented) - let label_fe_f3 = get_label_fe(&mut veh, None, false, None, None, false) + let label_fe_f3 = get_label_fe(&mut veh, None, false, None, None, None, false) .unwrap() .0; From d335f0d87659a4f33f1192366d67710f643ff473 Mon Sep 17 00:00:00 2001 From: Kyle Carow Date: Mon, 13 Jul 2026 12:27:24 -0600 Subject: [PATCH 2/2] add simparams input to python side for get_label_fe --- fastsim-core/src/simdrivelabel/mod.rs | 41 ++++++++++++++++++--------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/fastsim-core/src/simdrivelabel/mod.rs b/fastsim-core/src/simdrivelabel/mod.rs index 1baca1a5b..662dd9cd1 100644 --- a/fastsim-core/src/simdrivelabel/mod.rs +++ b/fastsim-core/src/simdrivelabel/mod.rs @@ -1,13 +1,11 @@ //! Module containing classes and methods for calculating label fuel economy. use std::collections::HashMap; -use std::hash::Hash; // crate local use crate::drive_cycle::{Cycle, CYC_ACCEL}; use crate::imports::*; -use crate::prelude::SimParams; -use crate::simdrive::SimDrive; +use crate::simdrive::{params::SimParams, SimDrive}; use crate::vehicle::{PowertrainType, Vehicle}; /// Return first index of `arr` greater than `cut` @@ -58,7 +56,7 @@ pub fn get_0_to_60_time_from_accel_data(accel_data: &AccelData) -> anyhow::Resul /// Run the acceleration test and return the time/speed trace. pub fn run_accel( veh: &Vehicle, - sim_params: &HashMap<&str, SimParams>, + sim_params: &HashMap<&'static str, SimParams>, ) -> anyhow::Result { let mut sd_accel = SimDrive::new( veh.clone(), @@ -1009,13 +1007,13 @@ fn run_simdrive_with_init_soc( /// Runs the appropriate simulations required for calculating /// the label fuel economy for the given vehicle. /// NOTE: does not run the acceleration test. -pub fn run_label_simulations<'a>( - veh: &'a mut Vehicle, +pub fn run_label_simulations( + veh: &mut Vehicle, // max_epa_adj: Option, fuel_props: Option, phev_utilization_params: Option, - sim_params: &HashMap<&'a str, SimParams>, -) -> anyhow::Result<(SimulationDataForLabel, HashMap<&'a str, SimDrive>)> { + sim_params: &HashMap<&'static str, SimParams>, +) -> anyhow::Result<(SimulationDataForLabel, HashMap<&'static str, SimDrive>)> { // let max_epa_adj = max_epa_adj.unwrap_or(0.3); let phev_utilization_params = &phev_utilization_params.unwrap_or_default(); let fuel_props = fuel_props.unwrap_or_default(); @@ -1326,15 +1324,15 @@ pub fn run_label_simulations<'a>( /// /// Returns label fuel economy values as a struct and (optionally) /// simdrive::SimDrive objects. -pub fn get_label_fe<'a>( - veh: &'a mut Vehicle, +pub fn get_label_fe( + veh: &mut Vehicle, max_epa_adj: Option, full_detail: bool, fuel_props: Option, phev_utilization_params: Option, - sim_params: Option>, + sim_params: Option>, verbose: bool, -) -> anyhow::Result<(LabelFe, Option>)> { +) -> anyhow::Result<(LabelFe, Option>)> { let max_epa_adj = max_epa_adj.unwrap_or(0.3); let phev_utilization_params = &phev_utilization_params.unwrap_or_default(); let fuel_props = fuel_props.unwrap_or_default(); @@ -1376,7 +1374,7 @@ pub fn get_label_fe<'a>( #[cfg_attr( feature = "pyo3", pyo3(signature = ( - veh, max_epa_adj=None, full_detail=None, fuel_props=None, phev_utilization_params=None, verbose=None)) + veh, max_epa_adj=None, full_detail=None, fuel_props=None, phev_utilization_params=None, sim_params=None, verbose=None)) )] /// pyo3 version of [get_label_fe] pub fn get_label_fe_py( @@ -1385,6 +1383,7 @@ pub fn get_label_fe_py( full_detail: Option, fuel_props: Option, phev_utilization_params: Option, + sim_params: Option>, verbose: Option, ) -> anyhow::Result { let (label_fe, _) = get_label_fe( @@ -1393,7 +1392,21 @@ pub fn get_label_fe_py( full_detail.unwrap_or_default(), fuel_props, phev_utilization_params, - None, // TODO: allow sim_params input from Python when SimParams has a better Python interface + sim_params + .map(|m| { + m.into_iter() + .map(|(k, v)| -> anyhow::Result<(&'static str, SimParams)> { + let key = match k.as_str() { + "udds" => "udds", + "hwy" => "hwy", + "accel" => "accel", + other => bail!("Unknown sim_params key: {other:?}"), + }; + Ok((key, v)) + }) + .collect() + }) + .transpose()?, verbose.unwrap_or_default(), )?; Ok(label_fe)