diff --git a/Cargo.lock b/Cargo.lock index 76ce49c2..1083b9e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1043,9 +1043,9 @@ dependencies = [ [[package]] name = "ninterp" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e60a408704882574368061cb86dba333d64027a93d8634dfe39c85da167df2a5" +checksum = "c6ad0d5c7daec14bf8d66d66eb1025ea4e24d0e2d6b0881fe3697d17054c88fe" dependencies = [ "itertools 0.13.0", "ndarray 0.16.1", diff --git a/cal_and_val/thermal/f3-vehicles/2020 Chevrolet Bolt EV.yaml b/cal_and_val/thermal/f3-vehicles/2020 Chevrolet Bolt EV.yaml index d11c4d4d..716f8221 100644 --- a/cal_and_val/thermal/f3-vehicles/2020 Chevrolet Bolt EV.yaml +++ b/cal_and_val/thermal/f3-vehicles/2020 Chevrolet Bolt EV.yaml @@ -5,9 +5,9 @@ year: 2020 cabin: LumpedCabin: # 0.05 is Chad's uncalibrated estimate - cab_shell_htc_to_amb_watts_per_square_meter_degree_celsius: 0.05 + cab_shell_htc_to_amb_watts_per_square_meter_kelvin: 0.05 # 0.05 is Chad's uncalibrated estimate - cab_htc_to_amb_stop_watts_per_square_meter_degree_celsius: 0.05 + cab_htc_to_amb_stop_watts_per_square_meter_kelvin: 0.05 # 200,000 is Chad's uncalibrated estimate heat_capacitance_joules_per_kelvin: 200000 # TODO: get actual vehicle length, but calibrate this later diff --git a/cal_and_val/thermal/f3-vehicles/2021_Hyundai_Sonata_Hybrid_Blue.yaml b/cal_and_val/thermal/f3-vehicles/2021_Hyundai_Sonata_Hybrid_Blue.yaml index 87543688..d4ab10cc 100644 --- a/cal_and_val/thermal/f3-vehicles/2021_Hyundai_Sonata_Hybrid_Blue.yaml +++ b/cal_and_val/thermal/f3-vehicles/2021_Hyundai_Sonata_Hybrid_Blue.yaml @@ -94,7 +94,7 @@ pt_type: FuelConverterThermal: heat_capacitance_joules_per_kelvin: 200000.0 length_for_convection_meters: 1.0 - htc_to_amb_stop_watts_per_square_meter_degree_celsius: 50.0 + htc_to_amb_stop_watts_per_square_meter_kelvin: 50.0 conductance_from_comb_watts_per_kelvin: 5.0 max_frac_from_comb: 0.5 tstat_te_sto_kelvin: 358.15 @@ -226,8 +226,8 @@ chassis: cargo_mass_kilograms: ~ cabin: LumpedCabin: - cab_shell_htc_to_amb_watts_per_square_meter_degree_celsius: 0.05 - cab_htc_to_amb_stop_watts_per_square_meter_degree_celsius: 0.05 + cab_shell_htc_to_amb_watts_per_square_meter_kelvin: 0.05 + cab_htc_to_amb_stop_watts_per_square_meter_kelvin: 0.05 heat_capacitance_joules_per_kelvin: 200000.0 length_meters: 2.5 width_meters: 2.0 @@ -253,32 +253,3 @@ mass_kilograms: 1508.195 pwr_aux_base_watts: 500.0 trans_eff: 0.98 save_interval: 1 -state: - i: 0 - pwr_prop_fwd_max_watts: 0.0 - pwr_prop_bwd_max_watts: 0.0 - pwr_tractive_watts: 0.0 - pwr_tractive_for_cyc_watts: 0.0 - energy_tractive_joules: 0.0 - pwr_aux_watts: 0.0 - energy_aux_joules: 0.0 - pwr_drag_watts: 0.0 - energy_drag_joules: 0.0 - pwr_accel_watts: 0.0 - energy_accel_joules: 0.0 - pwr_ascent_watts: 0.0 - energy_ascent_joules: 0.0 - pwr_rr_watts: 0.0 - energy_rr_joules: 0.0 - pwr_whl_inertia_watts: 0.0 - energy_whl_inertia_joules: 0.0 - pwr_brake_watts: 0.0 - energy_brake_joules: 0.0 - cyc_met: true - cyc_met_overall: true - speed_ach_meters_per_second: 0.0 - dist_meters: 0.0 - grade_curr: 0.0 - elev_curr_meters: .nan - air_density_kilograms_per_cubic_meter: 1.172 - mass_kilograms: .nan diff --git a/fastsim-core/Cargo.toml b/fastsim-core/Cargo.toml index bcb18e46..dfbce0b9 100644 --- a/fastsim-core/Cargo.toml +++ b/fastsim-core/Cargo.toml @@ -38,7 +38,7 @@ toml = { version = "0.8.12", optional = true } derive_more = { version = "1.0.0", features = ["from_str", "from", "is_variant", "try_into"] } ureq = { version = "2.9.1", optional = true } url = { version = "2.5.0", optional = true } -ninterp = { version = "0.2.0", features = ["serde"] } +ninterp = { version = "0.2.1", features = ["serde"] } [dev-dependencies] pretty_assertions = "1.4.1" diff --git a/fastsim-core/fastsim-proc-macros/src/fastsim_api/fastsim_api_utils.rs b/fastsim-core/fastsim-proc-macros/src/fastsim_api/fastsim_api_utils.rs index 5f696657..7e1c65da 100755 --- a/fastsim-core/fastsim-proc-macros/src/fastsim_api/fastsim_api_utils.rs +++ b/fastsim-core/fastsim-proc-macros/src/fastsim_api/fastsim_api_utils.rs @@ -193,8 +193,8 @@ pub(crate) fn impl_getters_and_setters(field: &mut syn::Field) -> Option<()> { "Ratio" => extract_units!(uom::si::ratio::ratio), "Time" => extract_units!(uom::si::time::second, uom::si::time::hour), "HeatTransferCoeff" => extract_units!( - uom::si::heat_transfer::watt_per_square_meter_degree_celsius, - uom::si::heat_transfer::watt_per_square_meter_kelvin + uom::si::heat_transfer::watt_per_square_meter_kelvin, + uom::si::heat_transfer::watt_per_square_meter_degree_celsius ), "HeatCapacity" => { extract_units!( @@ -202,7 +202,10 @@ pub(crate) fn impl_getters_and_setters(field: &mut syn::Field) -> Option<()> { uom::si::heat_capacity::joule_per_degree_celsius ) } - "Temperature" => extract_units!(uom::si::temperature_interval::kelvin), + "TemperatureInterval" => extract_units!(uom::si::temperature_interval::kelvin), + "Temperature" => { + extract_units!(uom::si::thermodynamic_temperature::kelvin) + } "ThermalConductance" => { extract_units!(uom::si::thermal_conductance::watt_per_kelvin) } diff --git a/fastsim-core/src/gas_properties.rs b/fastsim-core/src/gas_properties.rs index 7d663884..db65f09f 100644 --- a/fastsim-core/src/gas_properties.rs +++ b/fastsim-core/src/gas_properties.rs @@ -45,7 +45,7 @@ lazy_static! { #[pyo3(name = "get_therm_cond")] #[staticmethod] pub fn get_therm_cond_py(te_air: f64) -> anyhow::Result { - Ok(Self::get_therm_cond(te_air * uc::KELVIN - *uc::CELSIUS_TO_KELVIN)?.get::()) + Ok(Self::get_therm_cond((te_air + uc::CELSIUS_TO_KELVIN) * uc::KELVIN)?.get::()) } /// Returns constant pressure specific heat [J/(kg*K)] of air @@ -54,7 +54,7 @@ lazy_static! { #[pyo3(name = "get_specific_heat_cp")] #[staticmethod] pub fn get_specific_heat_cp_py(te_air: f64) -> anyhow::Result { - Ok(Self::get_specific_heat_cp(te_air * uc::KELVIN - *uc::CELSIUS_TO_KELVIN)?.get::()) + Ok(Self::get_specific_heat_cp((te_air + uc::CELSIUS_TO_KELVIN) * uc::KELVIN)?.get::()) } /// Returns specific enthalpy [J/kg] of air @@ -63,7 +63,7 @@ lazy_static! { #[pyo3(name = "get_specific_enthalpy")] #[staticmethod] pub fn get_specific_enthalpy_py(te_air: f64) -> anyhow::Result { - Ok(Self::get_specific_enthalpy(te_air * uc::KELVIN - *uc::CELSIUS_TO_KELVIN)?.get::()) + Ok(Self::get_specific_enthalpy((te_air - uc::CELSIUS_TO_KELVIN) * uc::KELVIN)?.get::()) } /// Returns specific energy [J/kg] of air @@ -72,7 +72,7 @@ lazy_static! { #[pyo3(name = "get_specific_energy")] #[staticmethod] pub fn get_specific_energy_py(te_air: f64) -> anyhow::Result { - Ok(Self::get_specific_energy(te_air * uc::KELVIN - *uc::CELSIUS_TO_KELVIN)?.get::()) + Ok(Self::get_specific_energy((te_air - uc::CELSIUS_TO_KELVIN) * uc::KELVIN)?.get::()) } /// Returns thermal Prandtl number of air @@ -81,7 +81,7 @@ lazy_static! { #[pyo3(name = "get_pr")] #[staticmethod] pub fn get_pr_py(te_air: f64) -> anyhow::Result { - Ok(Self::get_pr(te_air * uc::KELVIN - *uc::CELSIUS_TO_KELVIN)?.get::()) + Ok(Self::get_pr((te_air - uc::CELSIUS_TO_KELVIN) * uc::KELVIN )?.get::()) } /// Returns dynamic viscosity \[Pa*s\] of air @@ -90,7 +90,7 @@ lazy_static! { #[pyo3(name = "get_dyn_visc")] #[staticmethod] pub fn get_dyn_visc_py(te_air: f64) -> anyhow::Result { - Ok(Self::get_dyn_visc(te_air * uc::KELVIN - *uc::CELSIUS_TO_KELVIN)?.get::()) + Ok(Self::get_dyn_visc((te_air - uc::CELSIUS_TO_KELVIN) * uc::KELVIN)?.get::()) } /// Returns temperature [°C] of air @@ -99,7 +99,7 @@ lazy_static! { #[pyo3(name = "get_te_from_h")] #[staticmethod] pub fn get_te_from_h_py(h: f64) -> anyhow::Result { - Ok(Self::get_te_from_h(h * uc::J_PER_KG)?.get::() - uc::CELSIUS_TO_KELVIN.value) + Ok(Self::get_te_from_h(h * uc::J_PER_KG)?.get::()) } /// Returns temperature [°C] of air @@ -108,7 +108,7 @@ lazy_static! { #[pyo3(name = "get_te_from_u")] #[staticmethod] pub fn get_te_from_u_py(u: f64) -> anyhow::Result { - Ok(Self::get_te_from_u(u * uc::J_PER_KG)?.get::() - uc::CELSIUS_TO_KELVIN.value) + Ok(Self::get_te_from_u(u * uc::J_PER_KG)?.get::()) } )] @@ -151,7 +151,7 @@ impl Air { /// - `te_air`: temperature of air pub fn get_therm_cond(te_air: si::Temperature) -> anyhow::Result { Ok( - asp::THERMAL_CONDUCTIVITY_INTERP.interpolate(&[te_air.get::()])? + asp::THERMAL_CONDUCTIVITY_INTERP.interpolate(&[te_air.get::()])? * uc::WATT_PER_METER_KELVIN, ) } @@ -162,35 +162,38 @@ impl Air { pub fn get_specific_heat_cp( te_air: si::Temperature, ) -> anyhow::Result { - Ok(asp::C_P_INTERP.interpolate(&[te_air.get::()])? * uc::J_PER_KG_K) + Ok(asp::C_P_INTERP.interpolate(&[te_air.get::()])? * uc::J_PER_KG_K) } /// Returns specific enthalpy of air /// # Arguments /// - `te_air`: temperature of air pub fn get_specific_enthalpy(te_air: si::Temperature) -> anyhow::Result { - Ok(asp::ENTHALPY_INTERP.interpolate(&[te_air.get::()])? * uc::J_PER_KG) + Ok(asp::ENTHALPY_INTERP.interpolate(&[te_air.get::()])? * uc::J_PER_KG) } /// Returns specific energy of air /// # Arguments /// - `te_air`: temperature of air pub fn get_specific_energy(te_air: si::Temperature) -> anyhow::Result { - Ok(asp::ENERGY_INTERP.interpolate(&[te_air.get::()])? * uc::J_PER_KG) + Ok(asp::ENERGY_INTERP.interpolate(&[te_air.get::()])? * uc::J_PER_KG) } /// Returns thermal Prandtl number of air /// # Arguments /// - `te_air`: temperature of air pub fn get_pr(te_air: si::Temperature) -> anyhow::Result { - Ok(asp::PRANDTL_INTERP.interpolate(&[te_air.get::()])? * uc::R) + Ok(asp::PRANDTL_INTERP.interpolate(&[te_air.get::()])? * uc::R) } /// Returns dynamic viscosity \[Pa*s\] of air /// # Arguments /// te_air: temperature of air pub fn get_dyn_visc(te_air: si::Temperature) -> anyhow::Result { - Ok(asp::DYN_VISC_INTERP.interpolate(&[te_air.get::()])? * uc::PASCAL_SECOND) + Ok( + asp::DYN_VISC_INTERP.interpolate(&[te_air.get::()])? + * uc::PASCAL_SECOND, + ) } /// Returns temperature of air @@ -247,7 +250,7 @@ mod air_static_props { use super::*; lazy_static! { /// Array of temperatures at which properties are evaluated - static ref TEMPERATURE_VALUES: Vec = [ + static ref TEMPERATURE_DEG_C_VALUES: Vec = vec![ -60., -57.03690616, -53.1958198, @@ -273,19 +276,16 @@ mod air_static_props { 2947.10642291, 3841.10336915, 5000. - ] - .iter() - .map(|x| *x * uc::KELVIN + *uc::CELSIUS_TO_KELVIN) - .collect(); + ]; pub static ref TEMP_FROM_ENTHALPY: Interpolator = Interpolator::new_1d( ENTHALPY_VALUES.iter().map(|x| x.get::()).collect::>(), - TEMPERATURE_VALUES.iter().map(|x| x.get::()).collect::>(), + TEMPERATURE_DEG_C_VALUES.clone(), Strategy::Linear, Extrapolate::Error ).unwrap(); pub static ref TEMP_FROM_ENERGY: Interpolator = Interpolator::new_1d( ENERGY_VALUES.iter().map(|x| x.get::()).collect::>(), - TEMPERATURE_VALUES.iter().map(|x| x.get::()).collect::>(), + TEMPERATURE_DEG_C_VALUES.clone(), Strategy::Linear, Extrapolate::Error ).unwrap(); @@ -321,7 +321,7 @@ mod air_static_props { .map(|x| *x * uc::WATT_PER_METER_KELVIN) .collect(); pub static ref THERMAL_CONDUCTIVITY_INTERP: Interpolator = Interpolator::new_1d( - TEMPERATURE_VALUES.iter().map(|x| x.get::()).collect::>(), + TEMPERATURE_DEG_C_VALUES.clone(), THERMAL_CONDUCTIVITY_VALUES.iter().map(|x| x.get::()).collect::>(), Strategy::Linear, Extrapolate::Error @@ -358,7 +358,7 @@ mod air_static_props { .map(|x| *x * uc::J_PER_KG_K) .collect(); pub static ref C_P_INTERP: Interpolator = Interpolator::new_1d( - TEMPERATURE_VALUES.iter().map(|x| x.get::()).collect::>(), + TEMPERATURE_DEG_C_VALUES.clone(), C_P_VALUES.iter().map(|x| x.get::()).collect::>(), Strategy::Linear, Extrapolate::Error @@ -394,7 +394,7 @@ mod air_static_props { .map(|x| *x * uc::J_PER_KG) .collect(); pub static ref ENTHALPY_INTERP: Interpolator = Interpolator::new_1d( - TEMPERATURE_VALUES.iter().map(|x| x.get::()).collect::>(), + TEMPERATURE_DEG_C_VALUES.clone(), ENTHALPY_VALUES.iter().map(|x| x.get::()).collect::>(), Strategy::Linear, Extrapolate::Error @@ -430,7 +430,7 @@ mod air_static_props { .map(|x| *x * uc::J_PER_KG) .collect(); pub static ref ENERGY_INTERP: Interpolator = Interpolator::new_1d( - TEMPERATURE_VALUES.iter().map(|x| x.get::()).collect::>(), + TEMPERATURE_DEG_C_VALUES.clone(), ENERGY_VALUES.iter().map(|x| x.get::()).collect::>(), Strategy::Linear, Extrapolate::Error @@ -466,7 +466,7 @@ mod air_static_props { .map(|x| *x * uc::PASCAL_SECOND) .collect(); pub static ref DYN_VISC_INTERP: Interpolator = Interpolator::new_1d( - TEMPERATURE_VALUES.iter().map(|x| x.get::()).collect::>(), + TEMPERATURE_DEG_C_VALUES.clone(), DYN_VISCOSITY_VALUES.iter().map(|x| x.get::()).collect::>(), Strategy::Linear, Extrapolate::Error @@ -478,7 +478,7 @@ mod air_static_props { .map(|((mu, c_p), k)| -> si::Ratio {*mu * *c_p / *k}) .collect::>(); pub static ref PRANDTL_INTERP: Interpolator = Interpolator::new_1d( - TEMPERATURE_VALUES.iter().map(|x| x.get::()).collect::>(), + TEMPERATURE_DEG_C_VALUES.clone(), PRANDTL_VALUES.iter().map(|x| x.get::()).collect::>(), Strategy::Linear, Extrapolate::Error @@ -525,7 +525,7 @@ mod octane_static_props { use super::*; lazy_static! { /// Array of temperatures at which properties are evaluated - static ref TEMPERATURE_VALUES: Vec = [ + static ref TEMPERATURE_DEG_C_VALUES: Vec = vec![ -4.00000000e+01, -3.70369062e+01, -3.31958198e+01, @@ -551,13 +551,10 @@ mod octane_static_props { 2.96710642e+03, 3.86110337e+03, 5.02000000e+03 - ] - .iter() - .map(|x| *x * uc::KELVIN + *uc::CELSIUS_TO_KELVIN) - .collect(); + ]; pub static ref TEMP_FROM_ENERGY: Interpolator = Interpolator::new_1d( ENERGY_VALUES.iter().map(|x| x.get::()).collect::>(), - TEMPERATURE_VALUES.iter().map(|x| x.get::()).collect::>(), + TEMPERATURE_DEG_C_VALUES.clone(), Strategy::Linear, Extrapolate::Error ).unwrap(); @@ -592,7 +589,7 @@ mod octane_static_props { .map(|x| *x * uc::J_PER_KG) .collect(); pub static ref ENERGY_INTERP: Interpolator = Interpolator::new_1d( - TEMPERATURE_VALUES.iter().map(|x| x.get::()).collect::>(), + TEMPERATURE_DEG_C_VALUES.clone(), ENERGY_VALUES.iter().map(|x| x.get::()).collect::>(), Strategy::Linear, Extrapolate::Error @@ -607,7 +604,7 @@ mod octane_static_props { #[pyo3(name = "get_specific_energy")] #[staticmethod] pub fn get_specific_energy_py(te_octane: f64) -> anyhow::Result { - Ok(Self::get_specific_energy(te_octane * uc::KELVIN - *uc::CELSIUS_TO_KELVIN)?.get::()) + Ok(Self::get_specific_energy((te_octane - uc::CELSIUS_TO_KELVIN) * uc::KELVIN )?.get::()) } /// Returns temperature [°C] of octane @@ -616,7 +613,7 @@ mod octane_static_props { #[pyo3(name = "get_te_from_u")] #[staticmethod] pub fn get_te_from_u_py(u: f64) -> anyhow::Result { - Ok(Self::get_te_from_u(u * uc::J_PER_KG)?.get::() - uc::CELSIUS_TO_KELVIN.value) + Ok(Self::get_te_from_u(u * uc::J_PER_KG)?.get::()) } )] #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, HistoryMethods)] @@ -629,7 +626,10 @@ impl Octane { /// # Arguments /// - `te_octane`: temperature of octane pub fn get_specific_energy(te_octane: si::Temperature) -> anyhow::Result { - Ok(osp::ENERGY_INTERP.interpolate(&[te_octane.get::()])? * uc::J_PER_KG) + Ok( + osp::ENERGY_INTERP.interpolate(&[te_octane.get::()])? + * uc::J_PER_KG, + ) } /// Returns temperature of octane diff --git a/fastsim-core/src/si.rs b/fastsim-core/src/si.rs index 50e7574d..f3ca284d 100644 --- a/fastsim-core/src/si.rs +++ b/fastsim-core/src/si.rs @@ -12,8 +12,8 @@ pub use si::f64::{ Acceleration, Angle, Area, AvailableEnergy as SpecificEnergy, Curvature, DynamicViscosity, Energy, Force, Frequency, HeatCapacity, HeatTransfer as HeatTransferCoeff, InverseVelocity, Length, Mass, MassDensity, MomentOfInertia, Power, PowerRate, Pressure, Ratio, - SpecificHeatCapacity, SpecificPower, TemperatureInterval as Temperature, ThermalConductance, - ThermalConductivity, Time, Velocity, Volume, + SpecificHeatCapacity, SpecificPower, TemperatureInterval, ThermalConductance, + ThermalConductivity, ThermodynamicTemperature as Temperature, Time, Velocity, Volume, }; pub use si::force::{newton, pound_force}; pub use si::heat_capacity::{joule_per_degree_celsius, joule_per_kelvin}; @@ -33,6 +33,7 @@ pub use si::specific_power::{kilowatt_per_kilogram, watt_per_kilogram}; pub use si::temperature_interval::kelvin; pub use si::thermal_conductance::watt_per_kelvin; pub use si::thermal_conductivity::{watt_per_meter_degree_celsius, watt_per_meter_kelvin}; +pub use si::thermodynamic_temperature::{degree_celsius, kelvin as kelvin_abs}; pub use si::time::{hour, second}; pub use si::velocity::{meter_per_second, mile_per_hour}; pub use si::volume::cubic_meter; diff --git a/fastsim-core/src/uc.rs b/fastsim-core/src/uc.rs index e310cf72..3149b3e4 100644 --- a/fastsim-core/src/uc.rs +++ b/fastsim-core/src/uc.rs @@ -85,10 +85,8 @@ unit_const!( 9.80 ); -lazy_static::lazy_static! { - pub static ref CELSIUS_TO_KELVIN: crate::si::Temperature = 273.15 * KELVIN; -} unit_const!(KELVIN, Temperature, 1.0); +unit_const!(KELVIN_INT, TemperatureInterval, 1.0); unit_const!(J_PER_KG_K, SpecificHeatCapacity, 1.0); unit_const!(J_PER_K, HeatCapacity, 1.0); unit_const!(J_PER_KG, SpecificEnergy, 1.0); @@ -96,3 +94,5 @@ unit_const!(PASCAL_SECOND, DynamicViscosity, 1.0); unit_const!(PASCAL, Pressure, 1.0); unit_const!(WATT_PER_METER_SQUARED_KELVIN, ThermalConductance, 1.0); unit_const!(WATT_PER_METER_KELVIN, ThermalConductivity, 1.0); + +pub const CELSIUS_TO_KELVIN: f64 = 273.15; diff --git a/fastsim-core/src/vehicle/cabin.rs b/fastsim-core/src/vehicle/cabin.rs index 934893c4..68b90742 100644 --- a/fastsim-core/src/vehicle/cabin.rs +++ b/fastsim-core/src/vehicle/cabin.rs @@ -108,7 +108,9 @@ impl LumpedCabin { self.state.pwr_thrml_to_res = pwr_thrml_to_res; // flat plate model for isothermal, mixed-flow from Incropera and deWitt, Fundamentals of Heat and Mass // Transfer, 7th Edition - let cab_te_film_ext = 0.5 * (self.state.temperature + te_amb_air); + let cab_te_film_ext: si::Temperature = 0.5 + * (self.state.temperature.get::() + te_amb_air.get::()) + * uc::KELVIN; self.state.reynolds_for_plate = Air::get_density(Some(cab_te_film_ext), Some(veh_state.elev_curr)) * veh_state.speed_ach @@ -139,11 +141,17 @@ impl LumpedCabin { * Air::get_therm_cond(cab_te_film_ext).with_context(|| format_dbg!())? / self.length) + 1.0 / self.cab_shell_htc_to_amb); - (self.length * self.width) * htc_overall_moving * (te_amb_air - self.state.temperature) + (self.length * self.width) + * htc_overall_moving + * (te_amb_air.get::() + - self.state.temperature.get::()) + * uc::KELVIN_INT } else { (self.length * self.width) / (1.0 / self.cab_htc_to_amb_stop + 1.0 / self.cab_shell_htc_to_amb) - * (te_amb_air - self.state.temperature) + * (te_amb_air.get::() + - self.state.temperature.get::()) + * uc::KELVIN_INT }; self.state.temp_prev = self.state.temperature; diff --git a/fastsim-core/src/vehicle/hvac/hvac_sys_for_lumped_cabin.rs b/fastsim-core/src/vehicle/hvac/hvac_sys_for_lumped_cabin.rs index 64ca339e..9e0ee146 100644 --- a/fastsim-core/src/vehicle/hvac/hvac_sys_for_lumped_cabin.rs +++ b/fastsim-core/src/vehicle/hvac/hvac_sys_for_lumped_cabin.rs @@ -14,7 +14,7 @@ pub struct HVACSystemForLumpedCabin { pub te_set: si::Temperature, /// deadband range. any cabin temperature within this range of /// `te_set` results in no HVAC power draw - pub te_deadband: si::Temperature, + pub te_deadband: si::TemperatureInterval, /// HVAC proportional gain pub p: si::ThermalConductance, /// HVAC integral gain [W / K / s], resets at zero crossing events @@ -47,7 +47,7 @@ impl Default for HVACSystemForLumpedCabin { fn default() -> Self { Self { te_set: *TE_STD_AIR, - te_deadband: 1.5 * uc::KELVIN, + te_deadband: 1.5 * uc::KELVIN_INT, p: Default::default(), i: Default::default(), d: Default::default(), @@ -98,14 +98,22 @@ impl HVACSystemForLumpedCabin { (si::Power::ZERO, si::Power::ZERO, f64::NAN * uc::R) } else { // outside deadband - let te_delta_vs_set = cab_state.temperature - self.te_set; - let te_delta_vs_amb: si::Temperature = cab_state.temperature - te_amb_air; + let te_delta_vs_set = (cab_state.temperature.get::() + - self.te_set.get::()) + * uc::KELVIN_INT; + let te_delta_vs_amb: si::TemperatureInterval = + (cab_state.temperature.get::() + - te_amb_air.get::()) + * uc::KELVIN_INT; self.state.pwr_p = -self.p * te_delta_vs_set; self.state.pwr_i -= self.i * uc::W / uc::KELVIN / uc::S * te_delta_vs_set * dt; self.state.pwr_i = self.state.pwr_i.max(-self.pwr_i_max).min(self.pwr_i_max); - self.state.pwr_d = - -self.d * uc::J / uc::KELVIN * ((cab_state.temperature - cab_state.temp_prev) / dt); + self.state.pwr_d = -self.d * uc::J / uc::KELVIN + * ((cab_state.temperature.get::() + - cab_state.temp_prev.get::()) + * uc::KELVIN_INT + / dt); let (pwr_thrml_hvac_to_cabin, pwr_thrml_fc_to_cabin, cop) = if cab_state.temperature > self.te_set + self.te_deadband { @@ -116,7 +124,7 @@ impl HVACSystemForLumpedCabin { // cop_ideal is t_c / (t_h - t_c) for cooling // divide-by-zero protection and realistic limit on COP - let cop_ideal = if te_delta_vs_amb.abs() < 5.0 * uc::KELVIN { + let cop_ideal = if te_delta_vs_amb.abs() < 5.0 * uc::KELVIN_INT { // cabin is cooler than ambient + threshold // TODO: make this `5.0` not hardcoded cab_state.temperature / (5.0 * uc::KELVIN) @@ -181,7 +189,7 @@ impl HVACSystemForLumpedCabin { fn handle_heat_source( &mut self, te_fc: Option, - te_delta_vs_amb: si::Temperature, + te_delta_vs_amb: si::TemperatureInterval, pwr_thrml_hvac_to_cabin: &mut si::Power, cab_heat_cap: si::HeatCapacity, cab_state: LumpedCabinState, @@ -199,7 +207,7 @@ impl HVACSystemForLumpedCabin { *pwr_thrml_hvac_to_cabin = pwr_thrml_hvac_to_cabin .min( cab_heat_cap * - (te_fc.unwrap() - cab_state.temperature) + (te_fc.unwrap().get::() - cab_state.temperature.get::()) * uc::KELVIN_INT * 0.1 // so that it's substantially less / dt, ) @@ -226,7 +234,7 @@ impl HVACSystemForLumpedCabin { // divide-by-zero protection and realistic limit on COP // TODO: make sure this is consist with above commented equation for heating! - let cop_ideal = if te_delta_vs_amb.abs() < 5.0 * uc::KELVIN { + let cop_ideal = if te_delta_vs_amb.abs() < 5.0 * uc::KELVIN_INT { // cabin is cooler than ambient + threshold // TODO: make this `5.0` not hardcoded cab_state.temperature / (5.0 * uc::KELVIN) diff --git a/fastsim-core/src/vehicle/hvac/hvac_sys_for_lumped_cabin_and_res.rs b/fastsim-core/src/vehicle/hvac/hvac_sys_for_lumped_cabin_and_res.rs index c76a28e8..3058db19 100644 --- a/fastsim-core/src/vehicle/hvac/hvac_sys_for_lumped_cabin_and_res.rs +++ b/fastsim-core/src/vehicle/hvac/hvac_sys_for_lumped_cabin_and_res.rs @@ -14,7 +14,7 @@ pub struct HVACSystemForLumpedCabinAndRES { pub te_set: si::Temperature, /// Deadband range. Any cabin temperature within this range of `te_set` /// results in no HVAC power draw - pub te_deadband: si::Temperature, + pub te_deadband: si::TemperatureInterval, /// HVAC proportional gain for cabin pub p_cabin: si::ThermalConductance, /// HVAC integral gain [W / K / s] for cabin, resets at zero crossing events @@ -62,7 +62,7 @@ impl Default for HVACSystemForLumpedCabinAndRES { fn default() -> Self { Self { te_set: *TE_STD_AIR, - te_deadband: 1.5 * uc::KELVIN, + te_deadband: 1.5 * uc::KELVIN_INT, p_cabin: Default::default(), i_cabin: Default::default(), d_cabin: Default::default(), @@ -122,81 +122,108 @@ impl HVACSystemForLumpedCabinAndRES { let mut pwr_thrml_hvac_to_res: si::Power = self .solve_for_res(res_temp, res_temp_prev, dt) .with_context(|| format_dbg!())?; - let (cop_ideal, te_ref) = - if pwr_thrml_hvac_to_res + pwr_thrml_hvac_to_cabin > si::Power::ZERO { - // heating mode - // TODO: account for cabin and battery heat sources in COP calculation!!!! + let (cop_ideal, te_ref) = if pwr_thrml_hvac_to_res + pwr_thrml_hvac_to_cabin + > si::Power::ZERO + { + // heating mode + // TODO: account for cabin and battery heat sources in COP calculation!!!! - let (te_ref, te_delta_vs_amb) = if pwr_thrml_hvac_to_res > si::Power::ZERO { - // both powers are positive -- i.e. both are in heating mode + let (te_ref, te_delta_vs_amb) = if pwr_thrml_hvac_to_res > si::Power::ZERO { + // both powers are positive -- i.e. both are in heating mode - let te_ref: si::Temperature = if cab_state.temperature > res_temp { - // cabin is hotter - cab_state.temperature - } else { - // battery is hotter - res_temp - }; - (te_ref, te_ref - te_amb_air) - } else if pwr_thrml_hvac_to_res >= si::Power::ZERO { - // `pwr_thrml_hvac_to_res` dominates need for heating - (res_temp, res_temp - te_amb_air) + let te_ref: si::Temperature = if cab_state.temperature > res_temp { + // cabin is hotter + cab_state.temperature } else { - // `pwr_thrml_hvac_to_res` dominates need for heating - (cab_state.temperature, cab_state.temperature - te_amb_air) + // battery is hotter + res_temp }; + ( + te_ref, + (te_ref.get::() - te_amb_air.get::()) + * uc::KELVIN_INT, + ) + } else if pwr_thrml_hvac_to_res >= si::Power::ZERO { + // `pwr_thrml_hvac_to_res` dominates need for heating + ( + res_temp, + (res_temp.get::() - te_amb_air.get::()) + * uc::KELVIN_INT, + ) + } else { + // `pwr_thrml_hvac_to_res` dominates need for heating + ( + cab_state.temperature, + (cab_state.temperature.get::() + - te_amb_air.get::()) + * uc::KELVIN_INT, + ) + }; - // https://en.wikipedia.org/wiki/Coefficient_of_performance#Theoretical_performance_limits - // cop_ideal is t_h / (t_h - t_c) for heating - // cop_ideal is t_c / (t_h - t_c) for cooling + // https://en.wikipedia.org/wiki/Coefficient_of_performance#Theoretical_performance_limits + // cop_ideal is t_h / (t_h - t_c) for heating + // cop_ideal is t_c / (t_h - t_c) for cooling - // divide-by-zero protection and realistic limit on COP - // TODO: make sure this is consistent with above commented equation for heating! - if te_delta_vs_amb.abs() < 5.0 * uc::KELVIN { - // cabin is cooler than ambient + threshold - // TODO: make this `5.0` not hardcoded - (te_ref / (5.0 * uc::KELVIN), te_ref) - } else { - (te_ref / te_delta_vs_amb.abs(), te_ref) - } - } else if pwr_thrml_hvac_to_res + pwr_thrml_hvac_to_cabin < si::Power::ZERO { - // cooling mode - // TODO: account for battery cooling source in COP calculation!!!! + // divide-by-zero protection and realistic limit on COP + // TODO: make sure this is consistent with above commented equation for heating! + if te_delta_vs_amb.abs() < 5.0 * uc::KELVIN_INT { + // cabin is cooler than ambient + threshold + // TODO: make this `5.0` not hardcoded + (te_ref / (5.0 * uc::KELVIN), te_ref) + } else { + (te_ref / te_delta_vs_amb.abs(), te_ref) + } + } else if pwr_thrml_hvac_to_res + pwr_thrml_hvac_to_cabin < si::Power::ZERO { + // cooling mode + // TODO: account for battery cooling source in COP calculation!!!! - let (te_ref, te_delta_vs_amb) = if pwr_thrml_hvac_to_res < si::Power::ZERO { - // both powers are negative -- i.e. both are in cooling mode + let (te_ref, te_delta_vs_amb) = if pwr_thrml_hvac_to_res < si::Power::ZERO { + // both powers are negative -- i.e. both are in cooling mode - let te_ref: si::Temperature = if cab_state.temperature < res_temp { - // cabin is colder - cab_state.temperature - } else { - // battery is colder - res_temp - }; - (te_ref, te_ref - te_amb_air) - } else if pwr_thrml_hvac_to_res >= si::Power::ZERO { - // `pwr_thrml_hvac_to_cabin` dominates need for cooling - (cab_state.temperature, cab_state.temperature - te_amb_air) + let te_ref: si::Temperature = if cab_state.temperature < res_temp { + // cabin is colder + cab_state.temperature } else { - // `pwr_thrml_hvac_to_res` dominates need for cooling - (res_temp, res_temp - te_amb_air) + // battery is colder + res_temp }; + ( + te_ref, + (te_ref.get::() - te_amb_air.get::()) + * uc::KELVIN_INT, + ) + } else if pwr_thrml_hvac_to_res >= si::Power::ZERO { + // `pwr_thrml_hvac_to_cabin` dominates need for cooling + ( + cab_state.temperature, + (cab_state.temperature.get::() + - te_amb_air.get::()) + * uc::KELVIN_INT, + ) + } else { + // `pwr_thrml_hvac_to_res` dominates need for cooling + ( + res_temp, + (res_temp.get::() - te_amb_air.get::()) + * uc::KELVIN_INT, + ) + }; - // https://en.wikipedia.org/wiki/Coefficient_of_performance#Theoretical_performance_limits - // cop_ideal is t_h / (t_h - t_c) for heating - // cop_ideal is t_c / (t_h - t_c) for cooling + // https://en.wikipedia.org/wiki/Coefficient_of_performance#Theoretical_performance_limits + // cop_ideal is t_h / (t_h - t_c) for heating + // cop_ideal is t_c / (t_h - t_c) for cooling - // divide-by-zero protection and realistic limit on COP - if te_delta_vs_amb.abs() < 5.0 * uc::KELVIN { - // cooling-dominating component is cooler than ambient + threshold - // TODO: make this `5.0` not hardcoded - (te_ref / (5.0 * uc::KELVIN), te_ref) - } else { - (te_ref / te_delta_vs_amb.abs(), te_ref) - } + // divide-by-zero protection and realistic limit on COP + if te_delta_vs_amb.abs() < 5.0 * uc::KELVIN_INT { + // cooling-dominating component is cooler than ambient + threshold + // TODO: make this `5.0` not hardcoded + (te_ref / (5.0 * uc::KELVIN), te_ref) } else { - (si::Ratio::ZERO, f64::NAN * uc::KELVIN) - }; + (te_ref / te_delta_vs_amb.abs(), te_ref) + } + } else { + (si::Ratio::ZERO, f64::NAN * uc::KELVIN) + }; self.state.cop = cop_ideal * self.frac_of_ideal_cop; ensure!( self.state.cop >= 0.0 * uc::R, @@ -272,8 +299,8 @@ impl HVACSystemForLumpedCabinAndRES { cab_heat_cap: si::HeatCapacity, dt: si::Time, ) -> anyhow::Result { - let pwr_thrml_hvac_to_cabin = if cab_state.temperature <= self.te_set + self.te_deadband - && cab_state.temperature >= self.te_set - self.te_deadband + let pwr_thrml_hvac_to_cabin = if cab_state.temperature <= (self.te_set + self.te_deadband) + && cab_state.temperature >= (self.te_set - self.te_deadband) { // inside deadband; no hvac power is needed @@ -283,7 +310,9 @@ impl HVACSystemForLumpedCabinAndRES { si::Power::ZERO } else { // outside deadband - let te_delta_vs_set = cab_state.temperature - self.te_set; + let te_delta_vs_set = (cab_state.temperature.get::() + - self.te_set.get::()) + * uc::KELVIN_INT; self.state.pwr_p = -self.p_cabin * te_delta_vs_set; self.state.pwr_i -= self.i_cabin * uc::W / uc::KELVIN / uc::S * te_delta_vs_set * dt; @@ -293,7 +322,10 @@ impl HVACSystemForLumpedCabinAndRES { .max(-self.pwr_i_max_cabin) .min(self.pwr_i_max_cabin); self.state.pwr_d = -self.d_cabin * uc::J / uc::KELVIN - * ((cab_state.temperature - cab_state.temp_prev) / dt); + * ((cab_state.temperature.get::() + - cab_state.temp_prev.get::()) + * uc::KELVIN_INT + / dt); let pwr_thrml_hvac_to_cabin: si::Power = if cab_state.temperature > self.te_set + self.te_deadband { @@ -361,7 +393,9 @@ impl HVACSystemForLumpedCabinAndRES { si::Power::ZERO } else { // outside deadband - let te_delta_vs_set = res_temp - self.te_set; + let te_delta_vs_set = (res_temp.get::() + - self.te_set.get::()) + * uc::KELVIN_INT; self.state.pwr_p_res = -self.p_res * te_delta_vs_set; self.state.pwr_i_res -= self.i_res * uc::W / uc::KELVIN / uc::S * te_delta_vs_set * dt; self.state.pwr_i_res = self @@ -369,8 +403,11 @@ impl HVACSystemForLumpedCabinAndRES { .pwr_i_res .max(-self.pwr_i_max_res) .min(self.pwr_i_max_res); - self.state.pwr_d_res = - -self.d_res * uc::J / uc::KELVIN * ((res_temp - res_temp_prev) / dt); + self.state.pwr_d_res = -self.d_res * uc::J / uc::KELVIN + * ((res_temp.get::() + - res_temp_prev.get::()) + * uc::KELVIN_INT + / dt); let pwr_thrml_hvac_to_res: si::Power = if res_temp > self.te_set + self.te_deadband { // COOLING MODE; Reversible Energy Storage is hotter than set point @@ -429,7 +466,7 @@ impl HVACSystemForLumpedCabinAndRES { *pwr_thrml_hvac_to_cabin = pwr_thrml_hvac_to_cabin .min( cab_heat_cap * - (te_fc.unwrap() - cab_state.temperature) + (te_fc.unwrap().get::() - cab_state.temperature.get::()) * uc::KELVIN_INT * 0.1 // so that it's substantially less / dt, ) diff --git a/fastsim-core/src/vehicle/powertrain/fuel_converter.rs b/fastsim-core/src/vehicle/powertrain/fuel_converter.rs index e731cc47..eb73be9b 100755 --- a/fastsim-core/src/vehicle/powertrain/fuel_converter.rs +++ b/fastsim-core/src/vehicle/powertrain/fuel_converter.rs @@ -514,7 +514,7 @@ pub struct FuelConverterThermal { /// parameter for temperature at which thermostat starts to open pub tstat_te_sto: Option, /// temperature delta over which thermostat is partially open - pub tstat_te_delta: Option, + pub tstat_te_delta: Option, #[serde(default = "tstat_interp_default")] pub tstat_interp: Interpolator, /// Radiator effectiveness -- ratio of active heat rejection from @@ -553,7 +553,7 @@ lazy_static! { pub static ref GASOLINE_DENSITY: si::MassDensity = 0.75 * uc::KG / uc::L; /// TODO: find a source for this value pub static ref GASOLINE_LHV: si::SpecificEnergy = 33.7 * uc::KWH / uc::GALLON / *GASOLINE_DENSITY; - pub static ref TE_ADIABATIC_STD: si::Temperature= Air::get_te_from_u( + pub static ref TE_ADIABATIC_STD: si::Temperature = Air::get_te_from_u( Air::get_specific_energy(*TE_STD_AIR).with_context(|| format_dbg!()).unwrap() + (Octane::get_specific_energy(*TE_STD_AIR).with_context(|| format_dbg!()).unwrap() + *GASOLINE_LHV) @@ -580,7 +580,9 @@ impl FuelConverterThermal { ) -> anyhow::Result<()> { self.state.pwr_thrml_fc_to_cab = pwr_thrml_fc_to_cab; // film temperature for external convection calculations - let te_air_film = 0.5 * (self.state.temperature + te_amb); + let te_air_film: si::Temperature = 0.5 + * (self.state.temperature.get::() + te_amb.get::()) + * uc::KELVIN; // Reynolds number = density * speed * diameter / dynamic viscosity // NOTE: might be good to pipe in elevation let fc_air_film_re = @@ -588,40 +590,39 @@ impl FuelConverterThermal { / Air::get_dyn_visc(te_air_film).with_context(|| format_dbg!())?; // calculate heat transfer coeff. from engine to ambient [W / (m ** 2 * K)] - self.state.htc_to_amb = - if veh_speed < 1.0 * uc::MPS { - // if stopped, scale based on thermostat opening and constant convection - self.state.tstat_open_frac = - self.tstat_interp - .interpolate(&[(self.state.temperature.get::() - - uc::CELSIUS_TO_KELVIN.value)]) - .with_context(|| format_dbg!())?; - (uc::R + self.state.tstat_open_frac * self.radiator_effectiveness) - * self.htc_to_amb_stop - } else { - // Calculate heat transfer coefficient for sphere, - // from Incropera's Intro to Heat Transfer, 5th Ed., eq. 7.44 - let sphere_conv_params = get_sphere_conv_params(fc_air_film_re.get::()); - let htc_to_amb_sphere: si::HeatTransferCoeff = sphere_conv_params.0 - * fc_air_film_re.get::().powf(sphere_conv_params.1) - * Air::get_pr(te_air_film) - .with_context(|| format_dbg!())? - .get::() - .powf(1.0 / 3.0) - * Air::get_therm_cond(te_air_film).with_context(|| format_dbg!())? - / self.length_for_convection; - // if stopped, scale based on thermostat opening and constant convection - self.state.tstat_open_frac = - self.tstat_interp - .interpolate(&[(self.state.temperature.get::() - - uc::CELSIUS_TO_KELVIN.value)]) - .with_context(|| format_dbg!())?; - self.state.tstat_open_frac * htc_to_amb_sphere - }; + self.state.htc_to_amb = if veh_speed < 1.0 * uc::MPS { + // if stopped, scale based on thermostat opening and constant convection + self.state.tstat_open_frac = self + .tstat_interp + .interpolate(&[self.state.temperature.get::()]) + .with_context(|| format_dbg!())?; + (uc::R + self.state.tstat_open_frac * self.radiator_effectiveness) + * self.htc_to_amb_stop + } else { + // Calculate heat transfer coefficient for sphere, + // from Incropera's Intro to Heat Transfer, 5th Ed., eq. 7.44 + let sphere_conv_params = get_sphere_conv_params(fc_air_film_re.get::()); + let htc_to_amb_sphere: si::HeatTransferCoeff = sphere_conv_params.0 + * fc_air_film_re.get::().powf(sphere_conv_params.1) + * Air::get_pr(te_air_film) + .with_context(|| format_dbg!())? + .get::() + .powf(1.0 / 3.0) + * Air::get_therm_cond(te_air_film).with_context(|| format_dbg!())? + / self.length_for_convection; + // if stopped, scale based on thermostat opening and constant convection + self.state.tstat_open_frac = self + .tstat_interp + .interpolate(&[self.state.temperature.get::()]) + .with_context(|| format_dbg!())?; + self.state.tstat_open_frac * htc_to_amb_sphere + }; self.state.pwr_thrml_to_amb = self.state.htc_to_amb * PI * self.length_for_convection.powi(typenum::P2::new()) / 4.0 - * (self.state.temperature - te_amb); + * (self.state.temperature.get::() + - te_amb.get::()) + * uc::KELVIN_INT; // let heat_to_amb = ; // assumes fuel/air mixture is entering combustion chamber at block temperature @@ -638,14 +639,17 @@ impl FuelConverterThermal { // heat that will go both to the block and out the exhaust port self.state.pwr_fuel_as_heat = fc_state.pwr_fuel - (fc_state.pwr_prop + fc_state.pwr_aux); self.state.pwr_thrml_to_tm = (self.conductance_from_comb - * (self.state.te_adiabatic - self.state.temperature)) + * (self.state.te_adiabatic.get::() + - self.state.temperature.get::()) + * uc::KELVIN_INT) .min(self.max_frac_from_comb * self.state.pwr_fuel_as_heat); - let delta_temp: si::Temperature = ((self.state.pwr_thrml_to_tm + let delta_temp: si::TemperatureInterval = ((self.state.pwr_thrml_to_tm - self.state.pwr_thrml_fc_to_cab - self.state.pwr_thrml_to_amb) * dt) / self.heat_capacitance; self.state.temp_prev = self.state.temperature; + // Interestingly, it seems to be ok to add a `TemperatureInterval` to a `Temperature` here self.state.temperature += delta_temp; self.state.eff_coeff = match self.fc_eff_model { @@ -690,19 +694,19 @@ impl Init for FuelConverterThermal { fn init(&mut self) -> anyhow::Result<()> { self.tstat_te_sto = self .tstat_te_sto - .or(Some(85. * uc::KELVIN + *uc::CELSIUS_TO_KELVIN)); - self.tstat_te_delta = self.tstat_te_delta.or(Some(5. * uc::KELVIN)); + .or(Some((85. + uc::CELSIUS_TO_KELVIN) * uc::KELVIN)); + self.tstat_te_delta = self.tstat_te_delta.or(Some(5. * uc::KELVIN_INT)); self.tstat_interp = Interpolator::new_1d( vec![ - self.tstat_te_sto.unwrap().get::(), - self.tstat_te_sto.unwrap().get::() + self.tstat_te_sto.unwrap().get::(), + self.tstat_te_sto.unwrap().get::() + self.tstat_te_delta.unwrap().get::(), ], vec![0.0, 1.0], Strategy::Linear, Extrapolate::Clamp, ) - .with_context(|| format_dbg!())?; + .with_context(|| format_dbg!((self.tstat_te_sto, self.tstat_te_delta)))?; Ok(()) } } @@ -824,7 +828,7 @@ impl Default for FCTempEffModelLinear { #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] pub struct FCTempEffModelExponential { /// temperature at which `fc_eta_temp_coeff` begins to grow - pub offset: si::Temperature, + pub offset: si::TemperatureInterval, /// exponential lag parameter [K^-1] pub lag: f64, /// minimum value that `fc_eta_temp_coeff` can take @@ -834,7 +838,7 @@ pub struct FCTempEffModelExponential { impl Default for FCTempEffModelExponential { fn default() -> Self { Self { - offset: 0.0 * uc::KELVIN + *uc::CELSIUS_TO_KELVIN, + offset: 0.0 * uc::KELVIN_INT, lag: 25.0, minimum: 0.2 * uc::R, } diff --git a/fastsim-core/src/vehicle/powertrain/reversible_energy_storage.rs b/fastsim-core/src/vehicle/powertrain/reversible_energy_storage.rs index 5973072c..47d0a004 100644 --- a/fastsim-core/src/vehicle/powertrain/reversible_energy_storage.rs +++ b/fastsim-core/src/vehicle/powertrain/reversible_energy_storage.rs @@ -218,8 +218,7 @@ impl ReversibleEnergyStorage { / self.energy_capacity.get::(), te_res .with_context(|| format_dbg!("Expected thermal model to be configured"))? - .get::() - - uc::CELSIUS_TO_KELVIN.value, + .get::(), ], (Interpolator::Interp3D(..), RESEffInterpInputs::CRateSOCTemperature) => &[ state.pwr_out_electrical.get::() @@ -227,8 +226,7 @@ impl ReversibleEnergyStorage { state.soc.get::(), te_res .with_context(|| format_dbg!("Expected thermal model to be configured"))? - .get::() - - uc::CELSIUS_TO_KELVIN.value, + .get::(), ], _ => bail!( " @@ -855,10 +853,15 @@ impl RESLumpedThermal { ) -> anyhow::Result<()> { self.state.temp_prev = self.state.temperature; // TODO: make sure this impacts cabin temperature - self.state.pwr_thrml_from_cabin = - self.conductance_to_cab * (te_cab - self.state.temperature); + self.state.pwr_thrml_from_cabin = self.conductance_to_cab + * (te_cab.get::() + - self.state.temperature.get::()) + * uc::KELVIN_INT; self.state.pwr_thrml_hvac_to_res = pwr_thrml_hvac_to_res; - self.state.pwr_thrml_from_amb = self.conductance_to_cab * (te_amb - self.state.temperature); + self.state.pwr_thrml_from_amb = self.conductance_to_cab + * (te_amb.get::() + - self.state.temperature.get::()) + * uc::KELVIN_INT; self.state.pwr_thrml_loss = res_state.pwr_out_electrical.abs() * (1.0 * uc::R - res_state.eff); self.state.temperature += (self.state.pwr_thrml_hvac_to_res