From e5834a78de8171623995e1b965761334dcdb58ab Mon Sep 17 00:00:00 2001 From: Kyle Carow Date: Fri, 24 Jan 2025 12:10:36 -0700 Subject: [PATCH 1/6] started cal_bev.py --- cal_and_val/thermal/cal_bev.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cal_and_val/thermal/cal_bev.py b/cal_and_val/thermal/cal_bev.py index e69de29bb..f274ede52 100644 --- a/cal_and_val/thermal/cal_bev.py +++ b/cal_and_val/thermal/cal_bev.py @@ -0,0 +1,10 @@ +""" +Calibration script for 2020 Chevrolet Bolt EV +""" + +# critical import +from pathlib import Path + +import fastsim as fsim + +# cyc_folder_path = Path(__file__) / "dyno_test_data/2020 Chevrolet Bolt EV/Extended Datasets" From efd4c3783f8a093e605c37c3628dea1ea17086a4 Mon Sep 17 00:00:00 2001 From: Kyle Carow Date: Fri, 24 Jan 2025 12:43:19 -0700 Subject: [PATCH 2/6] cal_bev is further along --- cal_and_val/thermal/cal_bev.py | 141 ++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 1 deletion(-) diff --git a/cal_and_val/thermal/cal_bev.py b/cal_and_val/thermal/cal_bev.py index f274ede52..138f76e1b 100644 --- a/cal_and_val/thermal/cal_bev.py +++ b/cal_and_val/thermal/cal_bev.py @@ -5,6 +5,145 @@ # critical import from pathlib import Path +# anticipated cricital imports +import numpy as np # noqa: F401 +import matplotlib.pyplot as plt # noqa: F401 +import seaborn as sns +import pandas as pd # noqa: F401 +import polars as pl # noqa: F401 + import fastsim as fsim -# cyc_folder_path = Path(__file__) / "dyno_test_data/2020 Chevrolet Bolt EV/Extended Datasets" +# Initialize seaborn plot configuration +sns.set_style() + +veh = fsim.Vehicle.from_file(Path(__file__).parent / "f3-vehicles/2020 Chevrolet Bolt EV.yaml") + +# Obtain the data from +# https://nrel.sharepoint.com/:f:/r/sites/EEMSCoreModelingandDecisionSupport2022-2024/Shared%20Documents/FASTSim/DynoTestData?csf=1&web=1&e=F4FEBp +# and then copy it to the local folder below +cyc_folder_path = Path(__file__) / "dyno_test_data/2020 Chevrolet Bolt EV/Extended Datasets" +assert cyc_folder_path.exists() + +# See 2021_Hyundai_Sonata_Hybrid_TestSummary_2022-03-01_D3.xlsx for cycle-level data +cyc_files = [ + # TODO: try to find 3 hot cycles, 3 room temp cycles, and 3 cold cycles. + # The hot and cold cycles must have HVAC active! + # - wide range of initial and ambient temperatures + # - good signal quality -- somewhat subjective + # HWY x2, hot (M155), HVAC active (B155) + # TODO: check for solar load (should be around 1 kW / m^2) and implement place for this somewhere (`drive_cycle`???) + "62202004 Test Data.txt", + # US06 x2, hot, HVAC active + # TODO: check for solar load (should be around 1 kW / m^2) and implement or this somewhere (`drive_cycle`???) + "62202005 Test Data.txt", + # UDDS x1, room temperature ambient + "62201013 Test Data.txt", + # HWY x2, room temperature ambient + "62201014 Test Data.txt", + # TODO: check for seat heater usage in cold cycles and account for that in model! +] +assert len(cyc_files) > 0 +cyc_files = [cyc_folder_path / cyc_file for cyc_file in cyc_files] + +# TODO: use random selection to retain ~70% of cycles for calibration, and +# reserve the remaining for validation +cyc_files_for_cal = [ + # TOOD: populate this somehow -- e.g. random split of `cyc_files` +] +assert len(cyc_files_for_cal) > 0 +dfs_for_cal = {} +for cyc_file in cyc_files_for_cal: + cyc_file: Path + # `delimiter="\t"` should work for tab separated variables + dfs_for_cal[cyc_file.stem] = pd.read_csv(cyc_file, delimiter="\t") +cycs_for_cal = {} +for (cyc_file_stem, df) in dfs_for_cal.items(): + cyc_file_stem: str + df: pd.DataFrame + cyc_dict = df.to_dict() + # TODO: be ready to do some massaging of `cyc_dict`, like making sure that + # keys match expected, purging invalid keys, and massaging data types + + # TODO: make sure this catches ambient temperature + cycs_for_cal[cyc_file_stem] = fsim.Cycle.from_pydict(cyc_dict) +sds_for_cal = {} +for (cyc_file_stem, cyc) in cycs_for_cal.items(): + cyc_file_stem: str + cyc: fsim.Cycle + # TODO: clone veh and set up initial conditions for: + # - SOC + # - cabin temp + # - battery temp if available, else use cabin temp + # - engine temp for HEV + # NOTE: maybe change `save_interval` to 5 + sds_for_cal[cyc_file_stem] = fsim.SimDrive(veh, cyc).to_pydict() + +# TODO: flesh this out for validation stuff +# cyc_files_for_val = [] + +# Setup model objectives +## Parameter Functions +def new_em_eff_max(sd_dict, new_eff_peak): + """ + Set `new_eff_max` in `ElectricMachine` + """ + em = fsim.ElectricMachine.from_pydict(sd_dict['veh']['pt_type']['HybridElectricVehicle']['em']) + em.set_eff_peak(new_eff_peak) + sd_dict['veh']['pt_type']['HybridElectricVehicle']['em'] = em.to_pydict() + # TODO: check that `sd_dict` is mutably modified outside the scope of this function, e.g. with a debugger + +def new_em_eff_range(sd_dict, new_eff_range): + """ + Set `new_eff_range` in `ElectricMachine` + """ + em = fsim.ElectricMachine.from_pydict(sd_dict['veh']['pt_type']['HybridElectricVehicle']['em']) + em.set_eff_range(new_eff_range) + sd_dict['veh']['pt_type']['HybridElectricVehicle']['em'] = em.to_pydict() + # TODO: check that `sd_dict` is mutably modified outside the scope of this function, e.g. with a debugger + +## Model Objectives +cal_mod_obj = fsim.pymoo_api.ModelObjectives( + models = sds_for_cal, + dfs = dfs_for_cal, + obj_fns=( + ( + lambda sd_dict: np.array(sd_dict['veh']['pt_type']['HybridElectricVehicle']['res']['history']['soc']), + lambda df: df['TODO: find signal for test data soc'] + ), + # TODO: add objectives for: + # - engine fuel usage + # - battery temperature + # - engine temperature + # - cabin temperature + # - HVAC power, if available + ), + param_fns=( + new_em_eff_max, + new_em_eff_range, + # TODO: make sure this has functions for modifying + # - HVAC PID controls for cabin (not for battery because Sonata has + # passive thermal management, but make sure to do battery thermal + # controls for BEV) + # - battery thermal + # - thermal mass + # - convection to ambient + # - convection to cabin + # - cabin thermal + # - thermal mass + # - length + # - htc to amb when stopped + # - set width from vehicle specs -- no need to calibrate + ), + # must match order and length of `params_fns` + bounds=( + (0.80, 0.99), + (0.1, 0.6), + ), + +) + +# Setup calibration problem +cal_prob = fsim.pymoo_api.CalibrationProblem( + mod_obj=cal_mod_obj, +) From 4c3fe44392ea1c09da5d5d7fef1a66c0dcd59067 Mon Sep 17 00:00:00 2001 From: Kyle Carow Date: Fri, 24 Jan 2025 12:55:18 -0700 Subject: [PATCH 3/6] Hybrid -> Battery --- cal_and_val/thermal/cal_bev.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cal_and_val/thermal/cal_bev.py b/cal_and_val/thermal/cal_bev.py index 138f76e1b..19a2b1320 100644 --- a/cal_and_val/thermal/cal_bev.py +++ b/cal_and_val/thermal/cal_bev.py @@ -88,18 +88,18 @@ def new_em_eff_max(sd_dict, new_eff_peak): """ Set `new_eff_max` in `ElectricMachine` """ - em = fsim.ElectricMachine.from_pydict(sd_dict['veh']['pt_type']['HybridElectricVehicle']['em']) + em = fsim.ElectricMachine.from_pydict(sd_dict['veh']['pt_type']['BatteryElectricVehicle']['em']) em.set_eff_peak(new_eff_peak) - sd_dict['veh']['pt_type']['HybridElectricVehicle']['em'] = em.to_pydict() + sd_dict['veh']['pt_type']['BatteryElectricVehicle']['em'] = em.to_pydict() # TODO: check that `sd_dict` is mutably modified outside the scope of this function, e.g. with a debugger def new_em_eff_range(sd_dict, new_eff_range): """ Set `new_eff_range` in `ElectricMachine` """ - em = fsim.ElectricMachine.from_pydict(sd_dict['veh']['pt_type']['HybridElectricVehicle']['em']) + em = fsim.ElectricMachine.from_pydict(sd_dict['veh']['pt_type']['BatteryElectricVehicle']['em']) em.set_eff_range(new_eff_range) - sd_dict['veh']['pt_type']['HybridElectricVehicle']['em'] = em.to_pydict() + sd_dict['veh']['pt_type']['BatteryElectricVehicle']['em'] = em.to_pydict() # TODO: check that `sd_dict` is mutably modified outside the scope of this function, e.g. with a debugger ## Model Objectives @@ -108,7 +108,7 @@ def new_em_eff_range(sd_dict, new_eff_range): dfs = dfs_for_cal, obj_fns=( ( - lambda sd_dict: np.array(sd_dict['veh']['pt_type']['HybridElectricVehicle']['res']['history']['soc']), + lambda sd_dict: np.array(sd_dict['veh']['pt_type']['BatteryElectricVehicle']['res']['history']['soc']), lambda df: df['TODO: find signal for test data soc'] ), # TODO: add objectives for: From f4623ad2ed49dec9208168adcb7aabed72b3929a Mon Sep 17 00:00:00 2001 From: Kyle Carow Date: Fri, 24 Jan 2025 13:32:23 -0700 Subject: [PATCH 4/6] selected BEV cycles --- cal_and_val/thermal/cal_bev.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/cal_and_val/thermal/cal_bev.py b/cal_and_val/thermal/cal_bev.py index 19a2b1320..2b0f02730 100644 --- a/cal_and_val/thermal/cal_bev.py +++ b/cal_and_val/thermal/cal_bev.py @@ -25,23 +25,24 @@ cyc_folder_path = Path(__file__) / "dyno_test_data/2020 Chevrolet Bolt EV/Extended Datasets" assert cyc_folder_path.exists() -# See 2021_Hyundai_Sonata_Hybrid_TestSummary_2022-03-01_D3.xlsx for cycle-level data +# See 2020_Chevrolet_Bolt_TestSummary_201005.xlsm for cycle-level data cyc_files = [ - # TODO: try to find 3 hot cycles, 3 room temp cycles, and 3 cold cycles. - # The hot and cold cycles must have HVAC active! - # - wide range of initial and ambient temperatures - # - good signal quality -- somewhat subjective - # HWY x2, hot (M155), HVAC active (B155) - # TODO: check for solar load (should be around 1 kW / m^2) and implement place for this somewhere (`drive_cycle`???) - "62202004 Test Data.txt", - # US06 x2, hot, HVAC active - # TODO: check for solar load (should be around 1 kW / m^2) and implement or this somewhere (`drive_cycle`???) - "62202005 Test Data.txt", - # UDDS x1, room temperature ambient - "62201013 Test Data.txt", - # HWY x2, room temperature ambient - "62201014 Test Data.txt", # TODO: check for seat heater usage in cold cycles and account for that in model! + # 20F (heater maybe on? Col R in test summary), UDDS + HWY + UDDS + US06 + "62009051 Test Data.txt" + # 20F (heater maybe on? Col R in test summary), US06 + UDDS + HWY + UDDS + "62009053 Test Data.txt" + + # room temperature (no HVAC), UDDS + HWY + UDDS + US06 + "62009019 Test Data.txt", + # room temperature (no HVAC), US06 + UDDS + HWY + UDDS + "62009021 Test Data.txt", + + # TODO: check for solar load (should be around 1 kW / m^2) and implement or this somewhere (`drive_cycle`???) + # 95F (HVAC on), UDDS + HWY + UDDS + "62009040 Test Data.txt" + # 95F (HVAC on), US06 + "62009041 Test Data.txt" ] assert len(cyc_files) > 0 cyc_files = [cyc_folder_path / cyc_file for cyc_file in cyc_files] From 610a47e8c08b0b9d1c6454a391a118019a58cf6d Mon Sep 17 00:00:00 2001 From: Kyle Carow Date: Fri, 24 Jan 2025 13:43:34 -0700 Subject: [PATCH 5/6] kelvin to celsius in vehicle files --- cal_and_val/thermal/f3-vehicles/2020 Chevrolet Bolt EV.yaml | 4 ++-- .../f3-vehicles/2021_Hyundai_Sonata_Hybrid_Blue.yaml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) 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 716f82212..d11c4d4d5 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_kelvin: 0.05 + cab_shell_htc_to_amb_watts_per_square_meter_degree_celsius: 0.05 # 0.05 is Chad's uncalibrated estimate - cab_htc_to_amb_stop_watts_per_square_meter_kelvin: 0.05 + cab_htc_to_amb_stop_watts_per_square_meter_degree_celsius: 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 d4ab10cc5..0f93011a7 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_kelvin: 50.0 + htc_to_amb_stop_watts_per_square_meter_degree_celsius: 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_kelvin: 0.05 - cab_htc_to_amb_stop_watts_per_square_meter_kelvin: 0.05 + 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 heat_capacitance_joules_per_kelvin: 200000.0 length_meters: 2.5 width_meters: 2.0 From c4d1f84e9623f2335c08a12ef6433a93d5432a75 Mon Sep 17 00:00:00 2001 From: Kyle Carow Date: Fri, 24 Jan 2025 15:16:54 -0700 Subject: [PATCH 6/6] battery temp --- cal_and_val/thermal/cal_bev.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cal_and_val/thermal/cal_bev.py b/cal_and_val/thermal/cal_bev.py index 2b0f02730..c7bdd36a7 100644 --- a/cal_and_val/thermal/cal_bev.py +++ b/cal_and_val/thermal/cal_bev.py @@ -110,12 +110,15 @@ def new_em_eff_range(sd_dict, new_eff_range): obj_fns=( ( lambda sd_dict: np.array(sd_dict['veh']['pt_type']['BatteryElectricVehicle']['res']['history']['soc']), - lambda df: df['TODO: find signal for test data soc'] + lambda df: df['HVBatt_SOC_CAN4__per'] ), # TODO: add objectives for: - # - engine fuel usage # - battery temperature - # - engine temperature + ( + lambda sd_dict: np.array(sd_dict['veh']['pt_type']['BatteryElectricVehicle']['res']['thermal']['RESLumpedThermal']['history']['temperature_kelvin']), + # HVBatt_cell_temp_1_CAN3__C (or average of temps?) or HVBatt_pack_average_temp_HPCM2__C? + lambda df: df['HVBatt_pack_average_temp_HPCM2__C'] + ), # - cabin temperature # - HVAC power, if available ),