Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions BatteryThermalModel/Simulation_FanBatteryThermalModel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
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)
df = pl.read_parquet("fs-data/FS-3/08102025/08102025Endurance1_FirstHalf.parquet")

cols = [f"ACC_SEG{x}_TEMPS_CELL0" for x in range(5)]
#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

ambientTemp = 32
df = df.with_columns(simpleTimeCol(df))[6788:]
df = df.with_columns((pl.col("Time") - df["Time"][ambientTemp]).alias("Time"))
real = df.select(cols).to_numpy()
#constants

#Thermal mass
mc = 1026 # J/K 1.14*900=1026

#Surface Area Of Pack
Area = 0.02620083033 # m^2 A=n*l*c => n=20, l=0.0695, c=0.01884951822

#Cross section area of the Pack
Ac=0.00691908680696

#Cross-section area of the Accumulator wall
Ac2=7.528521392*(10**-3)

# Stefan-Boltzmann constant [Wm^-2K^-4]
sigma=5.67*(10**-8)

# Surface area of the accumulator
AsACC= 0.065

# Emissivity of Aluminium[606] [TRAININGPARAMETER]
e= 0.1 # 0.02 - 0.80 ; depends on the surface finish

# Convection coefficient
h = 3000# W/m^2K [TRAININGPARAMETER] [-184.90636861]

# Thermal conductivity of the cell
k=0.205 #[calculate physically?] W/mK

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
size = len(Temprature)
#python Data/BatteryThermalModel/Simulation_FanBatteryThermalModel.py fs-data/FS-3/08102025/08102025Endurance1_FirstHalf.parquet
#python Data/BatteryThermalModel/Simulation_FanBatteryThermalModel.py fs-data/FS-3/08172025/08172025_27autox2&45C_35C_~28Cambient_100fans.parquet
#python Data/BatteryThermalModel/Simulation_FanBatteryThermalModel.py fs-data/FS-3/08102025/08102025Endurance1_SecondHalf.parquet
#python Data/BatteryThermalModel/Simulation_FanBatteryThermalModel.py fs-data/FS-3/08102025/08102025RollingResistanceTestP1.parquet

## ------------------------ ## ------------------------- ## ------------------------- ## ------------------------- ##

simulationDuration = 50 # seconds
deltaTime = 60/5035 # seconds

air = np.ones((5, 6)) * ambientTemp # Initialize air temperature to ambient
pack = np.ones((5, 6)) * ambientTemp # Initialize pack temperature to ambient
packResistance = np.ones((5, 6)) * 0.016 # Initialize pack resistance to 16 mOhm
case = ambientTemp

def conductionRow (a, b, k, d=0.038600):
"""Conduction between row-adjacent packs [W]"""
return -k*((b-a)/d)*Ac

def conductionCol (a, b, k, d=0.069300):
"""Conduction between col-adjacent packs [W]"""
return -k*((b-a)/d)*Ac

def conductionCase (a, b, k):
"""Conduction between pack and case [W]"""
return -k*((b-a))*Ac2

def convection (a, b, h=h):
"""Convection between pack and air [W]"""
return h * Area * ((b - a))

def heat_generation (I):
"""Heat generated by the battery [W]"""
return (I**2)*packResistance

def radiation (case):
"""Radiation between case to air [W]"""
return e*sigma*AsACC*((case**4))

logCells = np.zeros((size + 1, 5, 6))
logAir = np.zeros((size + 1, 5, 6))
logCase = np.zeros(size + 1)
time = np.arange(0, size + 1) * deltaTime

logCells[0, :, :] = pack
logAir[0, :, :] = air
logCase[0] = case

for i in range(1, size + 1):
pack = logCells[i-1, :, :].copy()
air = logAir[i-1, :, :].copy()
case = logCase[i-1]
I_now = I[i-1]

#Heat Generation (W per cell)
heat = heat_generation(I_now)

#Pack to Pack Conduction (W)
conduction_R = conductionRow(pack[:-1, :], pack[1:, :], k)
conduction_C = conductionCol(pack[:, :-1], pack[:, 1:], k)

#Pack to Case Conduction (W)
topCaseConduction = conductionCase(pack[0, :], case, k)
bottomCaseConduction = conductionCase(pack[-1, :], case, k)
leftConduction = conductionCase(pack[1:-1, 0], case, k)
rightConduction = conductionCase(pack[1:-1, -1], case, k)

#Radiation (W)
Accradiation = radiation(case + 273.15) #tempratures in kelvin!

#Convection Fan to Pack
convectionTotal = convection(pack, air)

case_flux = np.sum(topCaseConduction) + np.sum(bottomCaseConduction) + np.sum(leftConduction) + np.sum(rightConduction) + np.sum(Accradiation)
case += case_flux * deltaTime / (mc * 5) #case thermal mass?

#Updating Tempratures
pack[:-1, :] -= conduction_R * deltaTime / mc
pack[1:, :] += conduction_R * deltaTime / mc
pack[:, :-1] -= conduction_C * deltaTime / mc
pack[:, 1:] += conduction_C * deltaTime / mc

pack[0, :] -= topCaseConduction * deltaTime / mc
pack[-1, :] -= bottomCaseConduction * deltaTime / mc
pack[1:-1, 0] -= leftConduction * deltaTime / mc
pack[1:-1, -1] -= rightConduction * deltaTime / mc

pack += convectionTotal * deltaTime / mc
air -= convectionTotal * deltaTime / mc
pack += heat * deltaTime / mc

#Air cycle
if i % 50 == 0:
air[1:, :] = air [:-1 , :]
air[0, :] = np.ones_like(air[0, :]) * ambientTemp

logCells[i, :, :] = pack
logAir[i, :, :] = air
logCase[i] = case


# PLOT

# plt.plot(logCells[i])
# plt.legend()
# plt.show()

row1 = logAir[:, :, 0] # time, x, y
row1 # time, x
# X -> time
# Y -> temp
# Z -> x, y coordinates
arr = logCells[:, :, 0]
X, Y = np.meshgrid(time, np.arange(5))

plt

realArr = np.zeros_like(arr.T)
realArr[:, 1:] = real.T
realArr[:, 0] = real.T[:, 0]

fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
ax.plot_surface(X, Y, arr.T, label='Sim Temperature')
ax.plot_surface(X, Y, realArr, label='Real Temperature')
ax.set_xlabel('Time')
ax.set_ylabel('Position')
ax.set_zlabel('Temperature')
ax.set_title('Battery Temperature Simulation')
plt.show()


64 changes: 64 additions & 0 deletions Data/BatteryThermalModel/BatteryThermal.md
Original file line number Diff line number Diff line change
@@ -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
8 changes: 4 additions & 4 deletions Data/BatteryThermalModel/DataColumns.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
16 changes: 16 additions & 0 deletions Data/BatteryThermalModel/DataFrames.py
Original file line number Diff line number Diff line change
@@ -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)

58 changes: 58 additions & 0 deletions Data/BatteryThermalModel/DemoComplexThermalModel.py
Original file line number Diff line number Diff line change
@@ -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
9 changes: 4 additions & 5 deletions Data/BatteryThermalModel/FS4_FanTherm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Loading