From cded74e65f447b2419686a6b2b45d8c830e508fe Mon Sep 17 00:00:00 2001 From: Dhruv Date: Tue, 31 Mar 2026 21:08:48 -0700 Subject: [PATCH 1/5] Add thermal model documentation for FS-3 battery accumulator --- Data/BatteryThermalModel/BatteryThermal.md | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 Data/BatteryThermalModel/BatteryThermal.md diff --git a/Data/BatteryThermalModel/BatteryThermal.md b/Data/BatteryThermalModel/BatteryThermal.md new file mode 100644 index 0000000..758db1b --- /dev/null +++ b/Data/BatteryThermalModel/BatteryThermal.md @@ -0,0 +1,64 @@ +We are modeling and training the thermal model for our accumulator which holds the tractive battery for FS-3 + +To understand the battery module: +There are 5 segments +Each segment has 6 packs of Enpaqa VTC5A Sony/Murata Li-Ion 2x10p +120 cells per segment, 600 cells total + +**Equations:** + +1) HEAT GENERATION + +$Q = I^2 R$ + +Where: +Q: Total Heat Generated by the Tractive Battery +I: ACC_POWER_CURRENT from the parquet +R: 0.8Ω for one pack (2x10) ; 16Ω for the tractive battery + +2) CONVECTION/COOLING FROM FANS + +$\frac{dT}{dt} = hA_s(\frac{Q}{n}-T_\infty)$ + +Where: +$\frac{Q}{n}$: Individual packs temprature (2x10) , n is the number of cells +$h$: Heat Transfer Coefficient +$T_\infty$: Ambient Temprature +$A_s$: Surface Area + +3) CONDUCTION [Cell to Cell] + +$\frac{dT}{dt} = -k \cdot \frac{dT}{dt} \cdot A_c$ + +Where: +$\frac{dT}{dt}$: +$k$: Thermal Conductivity + +4) CONDUCTION [Cell to Enclosure] + +$\frac{dT}{dt} = -k \cdot \frac{dT}{dt} \cdot A_c$ + +Where: +$\frac{dT}{dt}$: +$k$: Thermal Conductivity + +5) CONVECTION (Enclosure to Air) + +$\frac{dT}{dt} = hA_s(T_s-T_\infty)$ + +Where: +$T_s$: Surface Temprature +$h$: Heat Transfer Coeeficient +$T_\infty$: Ambient Temprature +$A_s$: Surface Area + +6) RADIATION (Enclosure to Air) + +$\frac{dT}{dt} = \epsilon \cdot \sigma \cdot A_s(T_s^4 - T_{sur}^4)$ + +Where: +$\epsilon$: Emmisivity of Aluminum (Enclosure) +$\sigma$: 5.67 $\cdot$ $10^{-8}$ +$A_s$: Surface Area +$T_s^4$: Surface Temprature +$T_{sur}^4$: Surrounding/Ambient Temprature \ No newline at end of file From d092a8f815593d3bba5e78369d76ff2779aa5007 Mon Sep 17 00:00:00 2001 From: goob10000 <65315624+goob10000@users.noreply.github.com> Date: Tue, 31 Mar 2026 22:15:17 -0700 Subject: [PATCH 2/5] brief formatting changes --- Data/BatteryThermalModel/BatteryThermal.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Data/BatteryThermalModel/BatteryThermal.md b/Data/BatteryThermalModel/BatteryThermal.md index 758db1b..2db4690 100644 --- a/Data/BatteryThermalModel/BatteryThermal.md +++ b/Data/BatteryThermalModel/BatteryThermal.md @@ -1,13 +1,13 @@ -We are modeling and training the thermal model for our accumulator which holds the tractive battery for FS-3 +## We are modeling and training the thermal model for our accumulator which holds the tractive battery for FS-3 To understand the battery module: There are 5 segments Each segment has 6 packs of Enpaqa VTC5A Sony/Murata Li-Ion 2x10p 120 cells per segment, 600 cells total -**Equations:** +## Equations -1) HEAT GENERATION +### 1) HEAT GENERATION $Q = I^2 R$ @@ -16,7 +16,7 @@ Q: Total Heat Generated by the Tractive Battery I: ACC_POWER_CURRENT from the parquet R: 0.8Ω for one pack (2x10) ; 16Ω for the tractive battery -2) CONVECTION/COOLING FROM FANS +### 2) CONVECTION/COOLING FROM FANS $\frac{dT}{dt} = hA_s(\frac{Q}{n}-T_\infty)$ @@ -26,7 +26,7 @@ $h$: Heat Transfer Coefficient $T_\infty$: Ambient Temprature $A_s$: Surface Area -3) CONDUCTION [Cell to Cell] +### 3) CONDUCTION [Cell to Cell] $\frac{dT}{dt} = -k \cdot \frac{dT}{dt} \cdot A_c$ @@ -34,7 +34,7 @@ Where: $\frac{dT}{dt}$: $k$: Thermal Conductivity -4) CONDUCTION [Cell to Enclosure] +### 4) CONDUCTION [Cell to Enclosure] $\frac{dT}{dt} = -k \cdot \frac{dT}{dt} \cdot A_c$ @@ -42,7 +42,7 @@ Where: $\frac{dT}{dt}$: $k$: Thermal Conductivity -5) CONVECTION (Enclosure to Air) +### 5) CONVECTION (Enclosure to Air) $\frac{dT}{dt} = hA_s(T_s-T_\infty)$ @@ -52,7 +52,7 @@ $h$: Heat Transfer Coeeficient $T_\infty$: Ambient Temprature $A_s$: Surface Area -6) RADIATION (Enclosure to Air) +### 6) RADIATION (Enclosure to Air) $\frac{dT}{dt} = \epsilon \cdot \sigma \cdot A_s(T_s^4 - T_{sur}^4)$ From ff0b3ac2d3adb62842c23d720158f4c1f08f848d Mon Sep 17 00:00:00 2001 From: Dhruv Date: Mon, 13 Apr 2026 18:21:49 -0700 Subject: [PATCH 3/5] Added FanBatteryModel code --- Data/BatteryThermalModel/DataColumns.py | 8 +- Data/BatteryThermalModel/DataFrames.py | 16 ++++ Data/BatteryThermalModel/FS4_FanTherm.py | 4 + .../FanBatteryThermalModeling.py | 91 +++++++++++++++++++ .../Simultaneous_Plot_Viewer.py | 8 +- Data/BatteryThermalModel/__init__.py | 0 .../fs4BatteryThermalModelingEx.py | 7 +- Data/BatteryThermalModel/pyproject.toml | 2 + Data/BatteryThermalModel/teste | 0 Data/__init__.py | 0 fs-data | 1 + 11 files changed, 129 insertions(+), 8 deletions(-) create mode 100644 Data/BatteryThermalModel/DataFrames.py create mode 100644 Data/BatteryThermalModel/FanBatteryThermalModeling.py create mode 100644 Data/BatteryThermalModel/__init__.py create mode 100644 Data/BatteryThermalModel/pyproject.toml create mode 100644 Data/BatteryThermalModel/teste create mode 100644 Data/__init__.py create mode 160000 fs-data diff --git a/Data/BatteryThermalModel/DataColumns.py b/Data/BatteryThermalModel/DataColumns.py index d1035f6..63ead05 100644 --- a/Data/BatteryThermalModel/DataColumns.py +++ b/Data/BatteryThermalModel/DataColumns.py @@ -1,13 +1,13 @@ -"""import matplotlib +import matplotlib matplotlib.use('TkAgg') import matplotlib.pyplot as plt import polars as pl dfa = pl.read_parquet("fs-data/FS-3/08172025/08172025_27autox2&45C_35C_~28Cambient_100fans.parquet") dfb = pl.read_parquet("fs-data/FS-3/08172025/08172025_28autox3&4_45C_40C_~29Cambient_0fans.parquet") dfc = pl.read_parquet("08172025_20_Endurance1P1.parquet") -print(dfc.columns)""" -"""print(dfb.columns) -print(dfa.columns)""" +print(dfc.columns) +print(dfb.columns) +print(dfa.columns) import polars as pl import matplotlib.pyplot as plt diff --git a/Data/BatteryThermalModel/DataFrames.py b/Data/BatteryThermalModel/DataFrames.py new file mode 100644 index 0000000..8f384bf --- /dev/null +++ b/Data/BatteryThermalModel/DataFrames.py @@ -0,0 +1,16 @@ +import polars as pl +import matplotlib.pyplot as plt +import numpy as np +from scipy.optimize import curve_fit +import sys +sys.path.append(".") +sys.path.append("..") +sys.path.append("./Data") +from Data.FSLib.IntegralsAndDerivatives import * +dfa = pl.read_parquet("fs-data/FS-3/08172025/08172025_27autox2&45C_35C_~28Cambient_100fans.parquet") +dfb = pl.read_parquet("fs-data/FS-3/08172025/08172025_28autox3&4_45C_40C_~29Cambient_0fans.parquet") +print(dfa) +print(dfb) +print(dfa.columns) +print(dfb.columns) + diff --git a/Data/BatteryThermalModel/FS4_FanTherm.py b/Data/BatteryThermalModel/FS4_FanTherm.py index 9a4beae..770d948 100644 --- a/Data/BatteryThermalModel/FS4_FanTherm.py +++ b/Data/BatteryThermalModel/FS4_FanTherm.py @@ -2,6 +2,10 @@ import matplotlib.pyplot as plt import numpy as np from scipy.optimize import curve_fit +import sys +sys.path.append(".") +sys.path.append("..") +sys.path.append("./Data") from Data.FSLib.IntegralsAndDerivatives import * # from Data.integralsAndDerivatives import in_place_derive diff --git a/Data/BatteryThermalModel/FanBatteryThermalModeling.py b/Data/BatteryThermalModel/FanBatteryThermalModeling.py new file mode 100644 index 0000000..d9d53a5 --- /dev/null +++ b/Data/BatteryThermalModel/FanBatteryThermalModeling.py @@ -0,0 +1,91 @@ +#Fan&BatteryThermalModeling +# Understanding the Tractive Battrey Accumulator Model +## We have 5 segments, with each segment having six 2x10 packs/(cells according to the parquet). 120 cells per segment, 600 cells total. +# There are 3 holes in each segment which are cooling the batteries and the cells/packs are arranged 2x3 2 in the front and 3 wide, distance of the holes is 3 times the width/length +import polars as pl +import matplotlib.pyplot as plt +import numpy as np +import argparse +from scipy.optimize import curve_fit +import sys +sys.path.append(".") +sys.path.append("..") +sys.path.append("./Data") +from Data.FSLib.IntegralsAndDerivatives import * +from Data.FSLib.AnalysisFunctions import * +parser = argparse.ArgumentParser() +parser.add_argument( + "parquet_path", + type=str, + help="Path to the Parquet file" +) + +args = parser.parse_args() +df = pl.read_parquet(args.parquet_path) + +#df1= pl.read_parquet("fs-data/FS-3/08172025/08172025_27autox2&45C_35C_~28Cambient_100fans.parquet") +#df2= pl.read_parquet("fs-data/FS-3/08172025/08172025_28autox3&4_45C_40C_~29Cambient_0fans.parquet") +#df3= pl.read_parquet("fs-data/FS-3/08102025/08102025Endurance1_FirstHalf.parquet") +#time starts from 0 + +#df1 = df1.with_columns(simpleTimeCol(df1))[6788:] +#df2 = df2.with_columns(simpleTimeCol(df2))[69330+370+6788:] +#df3 = df3.with_columns(simpleTimeCol(df3))[69330+370+6788:] +ambientTemp = 28 +df = df.with_columns(simpleTimeCol(df))[6788:] +df = df.with_columns((pl.col("Time") - df["Time"][ambientTemp]).alias("Time")) + +#time starts from 0 +#df1 = df1.with_columns((pl.col("Time") - df1["Time"][0]).alias("Time")) +#df2 = df2.with_columns((pl.col("Time") - df2["Time"][0]).alias("Time")) +#df3 = df3.with_columns((pl.col("Time") - df3["Time"][0]).alias("Time")) + +#constants +mc = 1026 # J/K 1.14*900=1026 +Area = 0.02620083033 # m^2 A=n*l*c => n=20, l=0.0695, c=0.01884951822 +Ac=0.00691908680696 +sigma=5.67*(10**-8) # Stefan-Boltzmann constant +#AsACC= +e= 0.1 # Emissivity of Aluminium [TRAININGPARAMETER] +h = 50# W/m^2K (convection coefficient) [TRAININGPARAMETER] [-184.90636861] +k=0.205 #(Thermal conductivity of the cell) [calculate physically] +k1=0.205 + +I = df["SME_TEMP_BusCurrent"] ## Accordingly change the 'df1' to any data frame choosen] +Temprature=(((I**2)*(16*(10**-3)))/mc) +IntegratedTemp=in_place_integrate(Temprature, dt=60/5035) #integrating the temperature change to get the total temperature generated by the battery over time +q1=h*Area*(IntegratedTemp-ambientTemp) #Cooling +q1=q1/mc +q2=-k*IntegratedTemp*Ac #Pack to Pack conduction; k: Thermal conductivity, Ac: Cross-section area of the Pack [TRAININGPARAMETER] +q2=q2/mc +q3=k1*surfaceTemp*Ac2 #Cell to enclosure conduction; k1: Thermal conductivity, Ac2: Cross-section area of the Accumulator wall [TRAININGPARAMETER] +#q4=h*AsACC*(ACCst-ambientTemp) #enclosure to air convection; AsACC: Surface area of the accumulator [TRAININGPARAMETER] +#q5=e*sigma*AsACC*(ACCst^4-ambientTemp^4) #Radiation; e: emissivity of aluminium, sigma: Stefan-Boltzmann constant [TRAININGPARAMETER] +IntegratedTemp=in_place_integrate(Temprature, dt=60/5035) #integrating the temperature change to get the total temperature generated by the battery over time +# dT/dt = (I^2*R - h*A*(T-T_ambient)) / (m*c)) +temp_cols = [f"ACC_SEG{s}_TEMPS_CELL{c}" for s in range(5) for c in range(6)] #We are not using this anymore since out Ts is now measured by T/mc instead +#Q=(q2+q3)-(q1+q5+q4) + +temps = np.array((30, 19)) + +for i in range(len(Temprature)): + + + +df = df.with_columns( +df.select([pl.col(col).cast(pl.Float32) for col in temp_cols]) +.mean_horizontal() +.alias("TempMean") +) + +#plt.ylim(bottom=ambientTemp) +plt.plot(df["Time"], IntegratedTemp) +plt.xlabel("Time (s)") +plt.ylabel("Temperature (°C)") +plt.legend() +plt.show() + +#python Data/BatteryThermalModel/FanBatteryThermalModeling.py fs-data/FS-3/08102025/08102025Endurance1_FirstHalf.parquet +#python Data/BatteryThermalModel/FanBatteryThermalModeling.py fs-data/FS-3/08172025/08172025_27autox2&45C_35C_~28Cambient_100fans.parquet +#python Data/BatteryThermalModel/FanBatteryThermalModeling.py fs-data/FS-3/08102025/08102025Endurance1_SecondHalf.parquet +#python Data/BatteryThermalModel/FanBatteryThermalModeling.py fs-data/FS-3/08102025/08102025RollingResistanceTestP1.parquet \ No newline at end of file diff --git a/Data/BatteryThermalModel/Simultaneous_Plot_Viewer.py b/Data/BatteryThermalModel/Simultaneous_Plot_Viewer.py index 53c3d14..de26e9b 100644 --- a/Data/BatteryThermalModel/Simultaneous_Plot_Viewer.py +++ b/Data/BatteryThermalModel/Simultaneous_Plot_Viewer.py @@ -19,7 +19,7 @@ def load_avg_temp_from_parquet(path: str) -> NDArray[np.float64]: Returns ------- - NDArray[np.float64] + NDArray[np.float64] Array of average temperatures across all cells. """ columns_list = [ @@ -44,7 +44,7 @@ def run_thermal_model_from_parquet( path: str, current_column: str = "SME_TEMP_BusCurrent", t_span: Tuple[float, float] = (0, 10), - initial_temp: float = 30, + initial_temp: float = 33.5, t_eval: NDArray[np.float64] | None = None, ): """ @@ -72,7 +72,7 @@ def run_thermal_model_from_parquet( current_draw = df[current_column].to_numpy() if t_eval is None: - t_eval = np.linspace(t_span[0], t_span[1], 100, dtype=np.float64) + t_eval = np.linspace(t_span[0], t_span[1], 1000, dtype=np.float64) return thermal_ode_solve_ivp(current_draw, t_span, initial_temp, t_eval) @@ -152,3 +152,5 @@ def main() -> None: # run the main function: python Simultaneous_Plot_Viewer.py "/Users/gautham/Documents/fs-data/FS-3/08102025/08102025Endurance1_FirstHalf.parquet" if __name__ == "__main__": main() + +#python Data/BatteryThermalModel/Simultaneous_Plot_Viewer.py fs-data/FS-3/08102025/08102025Endurance1_FirstHalf.parquet diff --git a/Data/BatteryThermalModel/__init__.py b/Data/BatteryThermalModel/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Data/BatteryThermalModel/fs4BatteryThermalModelingEx.py b/Data/BatteryThermalModel/fs4BatteryThermalModelingEx.py index cab52a5..d5b74ce 100644 --- a/Data/BatteryThermalModel/fs4BatteryThermalModelingEx.py +++ b/Data/BatteryThermalModel/fs4BatteryThermalModelingEx.py @@ -5,6 +5,7 @@ THERMAL_COEFFICIENT: float = 12.6316 / (49.9 * 1000.0) +ambient_temp: float = 32.0 """ Effective thermal coefficient relating I² to temperature rate of change. @@ -50,10 +51,14 @@ def thermal_ode( Instantaneous temperature time derivative ``dT/dt`` in degrees Celsius per second. """ + mc = 967.6 # J/K + Area = 0.71 # m^2 idx = int(t * len(current_draw) / total_time) idx = min(max(idx, 0), len(current_draw) - 1) I_t = current_draw[idx] - return float(THERMAL_COEFFICIENT * (I_t ** 2)) + heatingCoeff = 5.0 + coolingCoeff = 20.0 + return float(THERMAL_COEFFICIENT * (I_t ** 2) * heatingCoeff + (T-ambient_temp) * -184.90636861 * Area/mc * coolingCoeff) def thermal_ode_solve_ivp( diff --git a/Data/BatteryThermalModel/pyproject.toml b/Data/BatteryThermalModel/pyproject.toml new file mode 100644 index 0000000..ba0cb5a --- /dev/null +++ b/Data/BatteryThermalModel/pyproject.toml @@ -0,0 +1,2 @@ +[project] +name = "BatteryThermalModel" \ No newline at end of file diff --git a/Data/BatteryThermalModel/teste b/Data/BatteryThermalModel/teste new file mode 100644 index 0000000..e69de29 diff --git a/Data/__init__.py b/Data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fs-data b/fs-data new file mode 160000 index 0000000..4a7e93f --- /dev/null +++ b/fs-data @@ -0,0 +1 @@ +Subproject commit 4a7e93f052d0b26e4c5abc25e0651af7320cc736 From f21f2cd92dcf02c7860d0d97ccecc726fa6a2df3 Mon Sep 17 00:00:00 2001 From: goob10000 <65315624+goob10000@users.noreply.github.com> Date: Mon, 27 Apr 2026 18:42:57 -0700 Subject: [PATCH 4/5] demo for complex thermal modeling --- .../DemoComplexThermalModel.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Data/BatteryThermalModel/DemoComplexThermalModel.py diff --git a/Data/BatteryThermalModel/DemoComplexThermalModel.py b/Data/BatteryThermalModel/DemoComplexThermalModel.py new file mode 100644 index 0000000..ad259f9 --- /dev/null +++ b/Data/BatteryThermalModel/DemoComplexThermalModel.py @@ -0,0 +1,43 @@ +import numpy as np + +air = np.ones((5, 6)) * 20.0 # Initialize air temperature to 20 C +pack = np.ones((5, 6)) * 20.0 # Initialize pack temperature to 20 C +case = 20.0 + +ambientTemp = 20.0 # Ambient temperature in C + +def conduction(a, b): + # Simple conduction model: heat transfer proportional to temperature difference + k = 0.1 # Thermal conductivity + return k * (a - b) + +def convection(a, b): + # Simple convection model: heat transfer proportional to temperature difference + h = 0.05 # Convective heat transfer coefficient + return h * (a - b) + +def heat_generation(): + # Simulate heat generation in the battery pack + return np.random.rand(5, 6) * 5.0 # Random heat generation between 0 and 5 W + +for i in range(100): + conductionRow = conduction(pack[:-1, :], pack[1, :]) + conductionCol = conduction(pack[:, :-1], pack[:, 1:]) + + convectionTotal = convection(pack, air) + + heat = heat_generation() + + pack[:-1, :] -= conductionRow + pack[1:, :] += conductionRow + pack[:, :-1] -= conductionCol + pack[:, 1:] += conductionCol + + pack -= convectionTotal + air += convectionTotal + + pack += heat + + if i % 5 == 0: + air[1:, :] = air [:-1 , :] + air[0, :] = np.ones_like(air[0, :]) * ambientTemp \ No newline at end of file From e9afa9463fa41d7fd2f6b280e6cbff1ffd2029da Mon Sep 17 00:00:00 2001 From: goob10000 <65315624+goob10000@users.noreply.github.com> Date: Mon, 27 Apr 2026 18:48:42 -0700 Subject: [PATCH 5/5] bit of changes and corrections --- .../DemoComplexThermalModel.py | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/Data/BatteryThermalModel/DemoComplexThermalModel.py b/Data/BatteryThermalModel/DemoComplexThermalModel.py index ad259f9..2334520 100644 --- a/Data/BatteryThermalModel/DemoComplexThermalModel.py +++ b/Data/BatteryThermalModel/DemoComplexThermalModel.py @@ -6,9 +6,8 @@ ambientTemp = 20.0 # Ambient temperature in C -def conduction(a, b): +def conduction(a, b, k): # Simple conduction model: heat transfer proportional to temperature difference - k = 0.1 # Thermal conductivity return k * (a - b) def convection(a, b): @@ -18,14 +17,25 @@ def convection(a, b): def heat_generation(): # Simulate heat generation in the battery pack - return np.random.rand(5, 6) * 5.0 # Random heat generation between 0 and 5 W + return np.random.rand(5, 6) * 1.0 # Random heat generation between 0 and 5 W + +airS = np.zeros((100, 5, 6)) +packS = np.zeros((100, 5, 6)) +caseTemps = np.zeros(100) for i in range(100): - conductionRow = conduction(pack[:-1, :], pack[1, :]) - conductionCol = conduction(pack[:, :-1], pack[:, 1:]) + conductionRow = conduction(pack[:-1, :], pack[1, :], 0.1) + conductionCol = conduction(pack[:, :-1], pack[:, 1:], 0.1) + + topCaseConduction = conduction(pack[0, :], case, 0.01) + bottomCaseConduction = conduction(pack[-1, :], case, 0.01) + leftConduction = conduction(pack[1:-1, 0], case, 0.01) + rightConduction = conduction(pack[1:-1, -1], case, 0.01) convectionTotal = convection(pack, air) + case += np.sum([np.sum(topCaseConduction), np.sum(bottomCaseConduction), np.sum(leftConduction), np.sum(rightConduction)]) + heat = heat_generation() pack[:-1, :] -= conductionRow @@ -33,6 +43,11 @@ def heat_generation(): pack[:, :-1] -= conductionCol pack[:, 1:] += conductionCol + pack[0, :] -= topCaseConduction + pack[-1, :] -= bottomCaseConduction + pack[1:-1, 0] -= leftConduction + pack[1:-1, -1] -= rightConduction + pack -= convectionTotal air += convectionTotal