From 4d8d49c4b008e07ec16d98cb3c8f978f99f8b419 Mon Sep 17 00:00:00 2001 From: Chad Baker Date: Fri, 31 Jan 2025 15:07:27 -0700 Subject: [PATCH] fixed (hopefully) bug into temperature-dependent engine efficiency --- cal_and_val/thermal/cal_hev.py | 13 +++++----- cal_and_val/thermal/val_hev.py | 2 +- .../src/vehicle/powertrain/fuel_converter.rs | 26 +++++++------------ python/fastsim/pymoo_api.py | 22 +++++++++------- 4 files changed, 30 insertions(+), 33 deletions(-) diff --git a/cal_and_val/thermal/cal_hev.py b/cal_and_val/thermal/cal_hev.py index 60b8256d..56462d96 100644 --- a/cal_and_val/thermal/cal_hev.py +++ b/cal_and_val/thermal/cal_hev.py @@ -409,7 +409,7 @@ def get_exp_pwr_hvac(df): ## Constraint functions def get_fc_temp_too_hot(sd_dict): - te_fc_deg_c = sd_dict['veh']['pt_type']['HybridElectricVehicle']['fc']['thrml']['FuelConverterThermal']['state']['temperature_kelvin'] + celsius_to_kelvin_offset + te_fc_deg_c = sd_dict['veh']['pt_type']['HybridElectricVehicle']['fc']['thrml']['FuelConverterThermal']['state']['temperature_kelvin'] - celsius_to_kelvin_offset if np.any(te_fc_deg_c > 115): return 1 else: @@ -513,11 +513,12 @@ def get_fc_temp_too_hot(sd_dict): (10, 100), # new_fc_thrml_htc_to_amb_stop_watts_per_square_meter_kelvin, (10, 1000), # new_fc_thrml_conductance_from_comb_watts_per_kelvin, # (), # new_fc_thrml_max_frac_from_comb, - (3, 20), # new_fc_thrml_radiator_effectiveness, - (220, 380), # new_fc_thrml_fc_eff_model_Exponential_offset, + (3, 200), # new_fc_thrml_radiator_effectiveness, + (220, 300), # new_fc_thrml_fc_eff_model_Exponential_offset, (10, 60), # new_fc_thrml_fc_eff_model_Exponential_lag, (0.15, 0.35), # new_fc_thrml_fc_eff_model_Exponential_minimum, ), + # TODO: make `constr_fns` accept both `sd_dict` and `df` constr_fns=( get_fc_temp_too_hot, ), @@ -564,13 +565,13 @@ def perturb_params(pos_perturb_dec: float = 0.05, neg_perturb_dec: float = 0.1): print("Verifying that model responds to input parameter changes by individually perturbing parameters") baseline_errors = cal_mod_obj.get_errors( cal_mod_obj.update_params(baseline_params) - ) + )[0] for i, param in enumerate(baseline_params): # +5% perturbed_params = baseline_params.copy() perturbed_params[i] = param * (1 + pos_perturb_dec) - perturbed_errors = cal_mod_obj.get_errors(cal_mod_obj.update_params(perturbed_params)) + perturbed_errors = cal_mod_obj.get_errors(cal_mod_obj.update_params(perturbed_params))[0] if np.all(perturbed_errors == baseline_errors): print("\nperturbed_errros:") pprint.pp(perturbed_errors) @@ -582,7 +583,7 @@ def perturb_params(pos_perturb_dec: float = 0.05, neg_perturb_dec: float = 0.1): # -5% perturbed_params = baseline_params.copy() perturbed_params[i] = param * (1 - neg_perturb_dec) - perturbed_errors = cal_mod_obj.get_errors(cal_mod_obj.update_params(perturbed_params)) + perturbed_errors = cal_mod_obj.get_errors(cal_mod_obj.update_params(perturbed_params))[0] if np.all(perturbed_errors == baseline_errors): print("\nperturbed_errros:") pprint.pp(perturbed_errors) diff --git a/cal_and_val/thermal/val_hev.py b/cal_and_val/thermal/val_hev.py index 0df7ba65..fd3e0df0 100644 --- a/cal_and_val/thermal/val_hev.py +++ b/cal_and_val/thermal/val_hev.py @@ -20,7 +20,7 @@ :len(cal_mod_obj.param_fns)].to_numpy() # getting the solved models -(errors_cal, sds_cal) = cal_mod_obj.get_errors( +(errors_cal, cvs_cal, sds_cal) = cal_mod_obj.get_errors( sim_drives=cal_mod_obj.update_params(param_vals_fuel_energy), return_mods=True, ) diff --git a/fastsim-core/src/vehicle/powertrain/fuel_converter.rs b/fastsim-core/src/vehicle/powertrain/fuel_converter.rs index 45de01e1..65b08a2a 100755 --- a/fastsim-core/src/vehicle/powertrain/fuel_converter.rs +++ b/fastsim-core/src/vehicle/powertrain/fuel_converter.rs @@ -271,7 +271,6 @@ impl FuelConverter { ) ); self.state.pwr_prop = pwr_out_req; - // TODO: make this temperature dependent self.state.eff = if fc_on { uc::R * self @@ -772,17 +771,12 @@ impl FuelConverterThermal { offset, lag, minimum, - }) => ((1.0 - - f64::exp({ - (-1.0 / { - let exp_denom: si::Ratio = - (lag / uc::KELVIN) * (self.state.temperature - offset); - exp_denom - }) - .get::() - })) - * uc::R) - .max(minimum), + }) => { + let dte: si::TemperatureInterval = (self.state.temperature.get::() + - offset.get::()) + * uc::KELVIN_INT; + ((1.0 - f64::exp((-dte / lag).get::())) * uc::R).max(minimum) + } }; Ok(()) } @@ -931,9 +925,9 @@ 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::TemperatureInterval, + pub offset: si::Temperature, /// exponential lag parameter [K^-1] - pub lag: f64, + pub lag: si::TemperatureInterval, /// minimum value that `fc_eta_temp_coeff` can take pub minimum: si::Ratio, } @@ -941,8 +935,8 @@ pub struct FCTempEffModelExponential { impl Default for FCTempEffModelExponential { fn default() -> Self { Self { - offset: 0.0 * uc::KELVIN_INT, - lag: 25.0, + offset: 0.0 * uc::KELVIN, + lag: 25.0 * uc::KELVIN_INT, minimum: 0.2 * uc::R, } } diff --git a/python/fastsim/pymoo_api.py b/python/fastsim/pymoo_api.py index 258cda4c..739c4ec3 100644 --- a/python/fastsim/pymoo_api.py +++ b/python/fastsim/pymoo_api.py @@ -2,6 +2,7 @@ Module containing functions and classes for easy interaction with PyMOO """ import numpy as np +import pprint import numpy.typing as npt from typing import Tuple, Any, List, Callable, Dict, Optional, Union from pathlib import Path @@ -123,6 +124,7 @@ def new_peak_res_eff (sd_dict, new_peak_eff) -> Dict: # calculated in __post_init__ n_obj: Optional[int] = None + n_constr: Optional[int] = None def __post_init__(self): assert self.n_obj is None, "`n_obj` is not intended to be user provided" @@ -130,7 +132,7 @@ def __post_init__(self): self.models), f"{len(self.dfs)} != {len(self.models)}" assert len(self.bounds) == len(self.param_fns) self.n_obj = len(self.models) * len(self.obj_fns) - self.n_obj = len(self.models) * len(self.constr_fns) + self.n_constr = len(self.models) * len(self.constr_fns) def update_params(self, xs: List[Any]): """ @@ -190,11 +192,10 @@ def get_errors( key: str df_exp: pd.DataFrame - if not isinstance(sd, fsim.SimDrive): - solved_mods[key] = sd - objectives[key] = [1.0e12] * len(self.obj_fns) - constraint_violations[key] = [1] * len(self.constr_fns) - continue + # if not isinstance(sd, fsim.SimDrive): + # solved_mods[key] = sd + # objectives[key] = [1.0e12] * len(self.obj_fns) + # continue try: t0 = time.perf_counter() @@ -239,7 +240,6 @@ def get_errors( if not walk_success: objectives[key].append(1.02e12) - constraint_violations[key].append(1) else: try: objectives[key].append(get_error_val( @@ -266,6 +266,9 @@ def get_errors( t2 = time.perf_counter() if self.verbose: print(f"Time to postprocess: {t2 - t1:.3g} s") + # print("\nobjectives:") + # pprint.pp(objectives) + # print("") if return_mods: return objectives, constraint_violations, solved_mods else: @@ -287,7 +290,6 @@ def __init__( self, mod_obj: ModelObjectives, elementwise_runner=LoopedElementwiseEvaluation(), - n_constr: int=0, ): self.mod_obj = mod_obj assert len(self.mod_obj.bounds) == len( @@ -300,7 +302,7 @@ def __init__( xu=[bounds[1] for bounds in self.mod_obj.bounds], elementwise_runner=elementwise_runner, - n_ieq_constr=n_constr, + n_ieq_constr=self.mod_obj.n_constr, ) def _evaluate(self, x, out, *args, **kwargs): @@ -308,7 +310,7 @@ def _evaluate(self, x, out, *args, **kwargs): (errs, cvs) = self.mod_obj.get_errors(sim_drives) out['F'] = list(errs.values()) if self.n_ieq_constr > 0: - out['G'] = list(cvs.values()) + out['G'] = list(cvs.values()) class CustomOutput(Output): def __init__(self):