Skip to content

Commit

Permalink
final stuff used for reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
calbaker committed Feb 1, 2025
1 parent 4d8d49c commit d841930
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -238,11 +238,13 @@ hvac:
te_deadband_kelvin: 1.5
p_watts_per_kelvin: 100.0
i: 10.0
# TODO: bump this up
pwr_i_max_watts: 5000.0
d: 5.0
pwr_thrml_max_watts: 10000.0
frac_of_ideal_cop: 0.15
heat_source: FuelConverter
# TODO: bump this up
pwr_aux_for_hvac_max_watts: 5000.0
mass_kilograms: 1508.195
pwr_aux_base_watts: 500.0
Expand Down
265 changes: 253 additions & 12 deletions cal_and_val/thermal/val_hev.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
# %%
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from copy import deepcopy

from cal_hev import cal_mod_obj, val_mod_obj, save_path, time_column, mps_per_mph, speed_column, cyc_files_dict, cell_temp_column
import fastsim as fsim
from cal_hev import cal_mod_obj, val_mod_obj, save_path, time_column, mps_per_mph, speed_column, cyc_files_dict, cell_temp_column, get_exp_energy_fuel

res_df = pd.read_csv(save_path / "pymoo_res_df.csv")
res_df_fuel_energy = res_df.filter(regex="get_mod_energy_fuel")
res_df_fuel_energy_summed = res_df.filter(regex="get_mod_energy_fuel").sum(1)
res_df_fuel_energy_summed = res_df.filter(
regex="get_mod_energy_fuel").sum(1)
best_row_fuel_energy = res_df_fuel_energy_summed.argmin()
param_vals_fuel_energy = res_df.iloc[
best_row_fuel_energy,
Expand All @@ -19,25 +24,30 @@
best_row,
:len(cal_mod_obj.param_fns)].to_numpy()

param_vals_best = param_vals_euclidean

# getting the solved models
(errors_cal, cvs_cal, sds_cal) = cal_mod_obj.get_errors(
sim_drives=cal_mod_obj.update_params(param_vals_fuel_energy),
(errors_cal, cvs_cal, sds_cal_solved, sds_cal) = cal_mod_obj.get_errors(
sim_drives=cal_mod_obj.update_params(param_vals_best),
return_mods=True,
)
# (errors_val, sds_val) = val_mod_obj.get_errors(
# sim_drives=val_mod_obj.update_params(param_vals),
# return_mods=True,
# )
)
(errors_val, cvs_val, sds_val_solved, sds_val) = val_mod_obj.get_errors(
sim_drives=val_mod_obj.update_params(param_vals_best),
return_mods=True,
)

# %%

# plotting
# plotting
plot_save_path = save_path / "plots"
plot_save_path.mkdir(exist_ok=True)

for ((key, df_cal), (sd_key, sd_cal)) in zip(cal_mod_obj.dfs.items(), sds_cal.items()):
for ((key, df_cal), (sd_key, sd_cal)) in zip(cal_mod_obj.dfs.items(), sds_cal_solved.items()):
if not isinstance(sd_cal, dict):
print(f"skipping {key}")
continue
assert key == sd_key

for obj_fn in cal_mod_obj.obj_fns:
fig, ax = plt.subplots(2, 1, sharex=True)
cell_temp = next(iter(
Expand Down Expand Up @@ -70,4 +80,235 @@
)
ax[1].legend()
ax[1].set_ylabel("Speed [m/s]")
plt.savefig(plot_save_path / f"{key}.svg")
plt.savefig(plot_save_path / f"{key}_{obj_fn[0].__name__}_cal.svg")

for ((key, df_val), (sd_key, sd_val)) in zip(val_mod_obj.dfs.items(), sds_val_solved.items()):
if not isinstance(sd_val, dict):
print(f"skipping {key}")
continue
assert key == sd_key

for obj_fn in val_mod_obj.obj_fns:
fig, ax = plt.subplots(2, 1, sharex=True)
cell_temp = next(iter(
[v[cell_temp_column]
for k, v in cyc_files_dict.items() if k.replace(".txt", "") == key]
))
fig.suptitle(f"{key}\ncell temp [*C]: {cell_temp}")
ax[0].plot(
sd_val['veh']['history']['time_seconds'],
obj_fn[0](sd_val),
label='mod',
)
ax[0].plot(
df_val[time_column],
obj_fn[1](df_val),
label='exp',
)
ax[0].legend()
ax[0].set_ylabel(obj_fn[0].__name__)

ax[1].plot(
sd_val['veh']['history']['time_seconds'],
sd_val['veh']['history']['speed_ach_meters_per_second'],
label='mod',
)
ax[1].plot(
df_val[time_column],
df_val[speed_column] * mps_per_mph,
label='exp',
)
ax[1].legend()
ax[1].set_ylabel("Speed [m/s]")
plt.savefig(plot_save_path / f"{key}_{obj_fn[0].__name__}_val.svg")

# %%

def draw_error_zones(ax):
"""Draw 0%, ±5%, ±10% error regions on MPL Axes object"""
xl, xu = ax.get_xlim()
yl, yu = ax.get_ylim()
l = min(xl, yl)
u = max(xu, yu)
lims = np.array([0, 200])

# Plot 0% error diagonalx
ax.plot(lims, lims, linestyle="dotted", color="g", label="0% error")

# Plot ±5%, ±10% error regions with transparencies
counter = 0
error_1 = 0
error_2 = 0
error_3 = 0
for err, alpha in zip((0.05, 0.10, 0.15), (0.35, 0.2, 0.15)):
error = ax.fill_between(
lims,
lims * (1 - err),
lims * (1 + err),
alpha=alpha,
color="g",
label=f"±{err*100:.0f}% error",
)

ax.set_xlim(left=l, right=u)
ax.set_ylim(bottom=l, top=u)
# ax.legend(loc="lower right", framealpha=0.5, fontsize=8, borderpad=0.25)

return error

# %%
# Scatter plots with temperature effects

fuel_energy_exp_cal = []
fuel_energy_mod_cal = []
for ((key, df_cal), (sd_key, sd_cal)) in zip(cal_mod_obj.dfs.items(), sds_cal_solved.items()):
if not isinstance(sd_cal, dict):
print(f"skipping {key}")
continue
assert key == sd_key

fuel_energy_mod_cal.append(
sd_cal['veh']['pt_type']['HybridElectricVehicle']['fc']['state']['energy_fuel_joules'] * 1e-6
)
df_cal = df_cal[df_cal[time_column] <=
sd_cal['veh']['state']['time_seconds']]
fuel_energy_exp_cal.append(
get_exp_energy_fuel(df_cal).iloc[-1] * 1e-6
)

fuel_energy_exp_val = []
fuel_energy_mod_val = []

for ((key, df_val), (sd_key, sd_val)) in zip(val_mod_obj.dfs.items(), sds_val_solved.items()):
if not isinstance(sd_val, dict):
print(f"skipping {key}")
continue
assert key == sd_key

fuel_energy_mod_val.append(
sd_val['veh']['pt_type']['HybridElectricVehicle']['fc']['state']['energy_fuel_joules'] * 1e-6
)
df_val = df_val[df_val[time_column] <=
sd_val['veh']['state']['time_seconds']]
fuel_energy_exp_val.append(
get_exp_energy_fuel(df_val).iloc[-1] * 1e-6
)

fig, ax = plt.subplots()
fig.suptitle("Model v. Test Data With Thermal Effects")
ax.scatter(
fuel_energy_exp_cal,
fuel_energy_mod_cal,
label='cal',
)
ax.scatter(
fuel_energy_exp_val,
fuel_energy_mod_val,
label='val',
)
draw_error_zones(ax)
ax.set_xlabel("Test Data Fuel Used [MJ]")
ax.set_ylabel("FASTSim Fuel Used [MJ]")
ax.set_xlim(0, 55)
ax.set_ylim(0, 55)
ax.legend()
plt.savefig(plot_save_path / "scatter with thrml effects.svg")

# %%

# Scatter plots without temperature effects

fuel_energy_mod_cal_no_thrml = []
fuel_energy_exp_cal_no_thrml = []
for ((key, df_cal), (sd_key, sd_cal)) in zip(cal_mod_obj.dfs.items(), sds_cal.items()):
if not isinstance(sd_cal, dict):
print(f"skipping {key}")
continue
assert key == sd_key

sd_cal_no_thrml = deepcopy(sd_cal)

sd_cal_no_thrml['veh']['hvac'] = 'None'
sd_cal_no_thrml['veh']['cabin'] = 'None'
sd_cal_no_thrml['veh']['pt_type']['HybridElectricVehicle']['fc']['thrml'] = 'None'
sd_cal_no_thrml['veh']['pt_type']['HybridElectricVehicle']['res']['thrml'] = 'None'
res = fsim.ReversibleEnergyStorage.from_pydict(
sd_cal_no_thrml['veh']['pt_type']['HybridElectricVehicle']['res'], skip_init=False)
res.set_default_pwr_interp()
sd_cal_no_thrml['veh']['pt_type']['HybridElectricVehicle']['res'] = res.to_pydict()
sd_cal_no_thrml['veh']['pt_type']['HybridElectricVehicle']['pt_cntrl']['RGWDB']['temp_fc_allowed_off_kelvin'] = None
sd_cal_no_thrml['veh']['pt_type']['HybridElectricVehicle']['pt_cntrl']['RGWDB']['temp_fc_forced_on_kelvin'] = None

sd_cal_no_thrml = fsim.SimDrive.from_pydict(
sd_cal_no_thrml, skip_init=False)
try:
sd_cal_no_thrml.walk_once()
except:
pass
sd_cal_no_thrml = sd_cal_no_thrml.to_pydict()

fuel_energy_mod_cal_no_thrml.append(
sd_cal_no_thrml['veh']['pt_type']['HybridElectricVehicle']['fc']['state']['energy_fuel_joules'] * 1e-6
)
df_cal = df_cal[df_cal[time_column] <=
sd_cal_no_thrml['veh']['state']['time_seconds']]
fuel_energy_exp_cal_no_thrml.append(
get_exp_energy_fuel(df_cal)
)

fuel_energy_exp_val_no_thrml = []
fuel_energy_mod_val_no_thrml = []
for ((key, df_val), (sd_key, sd_val)) in zip(val_mod_obj.dfs.items(), sds_val.items()):
if not isinstance(sd_val, dict):
print(f"skipping {key}")
continue
assert key == sd_key

sd_val_no_thrml = deepcopy(sd_val)

sd_val_no_thrml['veh']['hvac'] = 'None'
sd_val_no_thrml['veh']['cabin'] = 'None'
sd_val_no_thrml['veh']['pt_type']['HybridElectricVehicle']['fc']['thrml'] = 'None'
sd_val_no_thrml['veh']['pt_type']['HybridElectricVehicle']['res']['thrml'] = 'None'
res = fsim.ReversibleEnergyStorage.from_pydict(
sd_val_no_thrml['veh']['pt_type']['HybridElectricVehicle']['res'], skip_init=False)
res.set_default_pwr_interp()
sd_val_no_thrml['veh']['pt_type']['HybridElectricVehicle']['res'] = res.to_pydict()
sd_val_no_thrml['veh']['pt_type']['HybridElectricVehicle']['pt_cntrl']['RGWDB']['temp_fc_allowed_off_kelvin'] = None
sd_val_no_thrml['veh']['pt_type']['HybridElectricVehicle']['pt_cntrl']['RGWDB']['temp_fc_forced_on_kelvin'] = None
sd_val_no_thrml = fsim.SimDrive.from_pydict(
sd_val_no_thrml, skip_init=False)
try:
sd_val_no_thrml.walk_once()
except:
pass
sd_val_no_thrml = sd_val_no_thrml.to_pydict()

fuel_energy_mod_val_no_thrml.append(
sd_val_no_thrml['veh']['pt_type']['HybridElectricVehicle']['fc']['state']['energy_fuel_joules'] * 1e-6
)
df_val = df_val[df_val[time_column] <=
sd_val_no_thrml['veh']['state']['time_seconds']]
fuel_energy_exp_val_no_thrml.append(
get_exp_energy_fuel(df_val)
)

fig, ax = plt.subplots()
fig.suptitle("Model v. Test Data Without Thermal Effects")
ax.scatter(
fuel_energy_exp_cal,
fuel_energy_mod_cal_no_thrml,
label='cal',
)
ax.scatter(
fuel_energy_exp_val,
fuel_energy_mod_val_no_thrml,
label='val',
)
draw_error_zones(ax)
ax.set_xlabel("Test Data Fuel Used [MJ]")
ax.set_ylabel("FASTSim Fuel Used [MJ]")
ax.set_xlim(0, 55)
ax.set_ylim(0, 55)
ax.legend()
plt.savefig(plot_save_path / "scatter without thrml effects.svg")
2 changes: 1 addition & 1 deletion fastsim-core/resources/interpolators/res/default_pwr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ Interp1D:
- 0.927243266086586
- 0.864025853742296
strategy: Linear
extrapolate: Error
extrapolate: Clamp
1 change: 1 addition & 0 deletions fastsim-core/src/vehicle/powertrain/fuel_converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,7 @@ pub struct FCTempEffModelExponential {
impl Default for FCTempEffModelExponential {
fn default() -> Self {
Self {
// TODO: update after reasonable calibration
offset: 0.0 * uc::KELVIN,
lag: 25.0 * uc::KELVIN_INT,
minimum: 0.2 * uc::R,
Expand Down
5 changes: 4 additions & 1 deletion python/fastsim/demos/demo_hev_thrml_cs_ca.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@
# simulation start time
t0 = time.perf_counter()
# run simulation
sd.walk()
try:
sd.walk()
except Exception:
pass
# simulation end time
t1 = time.perf_counter()
t_fsim3_si1 = t1 - t0
Expand Down
6 changes: 5 additions & 1 deletion python/fastsim/pymoo_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ def get_errors(
objectives: Dict = {}
constraint_violations: Dict = {}
solved_mods: Dict = {}
unsolved_mods: Dict = {}

# loop through all the provided trips
for ((key, df_exp), sd) in zip(self.dfs.items(), sim_drives.values()):
Expand All @@ -197,6 +198,9 @@ def get_errors(
# objectives[key] = [1.0e12] * len(self.obj_fns)
# continue

if return_mods:
unsolved_mods[key] = sd.to_pydict()

try:
t0 = time.perf_counter()
sd.walk_once() # type: ignore
Expand Down Expand Up @@ -270,7 +274,7 @@ def get_errors(
# pprint.pp(objectives)
# print("")
if return_mods:
return objectives, constraint_violations, solved_mods
return objectives, constraint_violations, solved_mods, unsolved_mods
else:
return objectives, constraint_violations

Expand Down

0 comments on commit d841930

Please sign in to comment.