diff --git a/Data/BatteryThermalModel/BatteryThermal.md b/Data/BatteryThermalModel/BatteryThermal.md new file mode 100644 index 0000000..2db4690 --- /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 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/DemoComplexThermalModel.py b/Data/BatteryThermalModel/DemoComplexThermalModel.py new file mode 100644 index 0000000..2334520 --- /dev/null +++ b/Data/BatteryThermalModel/DemoComplexThermalModel.py @@ -0,0 +1,58 @@ +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, k): + # Simple conduction model: heat transfer proportional to temperature difference + 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) * 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, :], 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 + pack[1:, :] += conductionRow + 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 + + pack += heat + + if i % 5 == 0: + air[1:, :] = air [:-1 , :] + air[0, :] = np.ones_like(air[0, :]) * ambientTemp \ No newline at end of file diff --git a/Data/BatteryThermalModel/FS4_FanTherm.py b/Data/BatteryThermalModel/FS4_FanTherm.py index d560129..770d948 100644 --- a/Data/BatteryThermalModel/FS4_FanTherm.py +++ b/Data/BatteryThermalModel/FS4_FanTherm.py @@ -3,11 +3,10 @@ import numpy as np from scipy.optimize import curve_fit import sys -from pathlib import Path - -sys.path.append(str(Path(__file__).resolve().parent.parent)) - -from FSLib.IntegralsAndDerivatives import * +sys.path.append(".") +sys.path.append("..") +sys.path.append("./Data") +from Data.FSLib.IntegralsAndDerivatives import * # from Data.integralsAndDerivatives import in_place_derive def simpleTimeCol (dfa, dt=60/5035): 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/fs-data b/fs-data new file mode 160000 index 0000000..4a7e93f --- /dev/null +++ b/fs-data @@ -0,0 +1 @@ +Subproject commit 4a7e93f052d0b26e4c5abc25e0651af7320cc736