formatting Battery.py, added 2022-12-23 TMS data

This commit is contained in:
Yeongdo Park 2023-01-04 20:03:26 +09:00
parent c377de82ce
commit 6f51e2f479
4 changed files with 14006 additions and 162 deletions

View file

@ -10,8 +10,9 @@ import pde
import cantera as ct import cantera as ct
from scipy import optimize from scipy import optimize
class CombustionChamber: class CombustionChamber:
def __init__ (self, mdot, ct_object, burned_state, hA=700): def __init__(self, mdot, ct_object, burned_state, hA=700):
self.mdot = mdot # kg/s self.mdot = mdot # kg/s
self.gas = ct_object # gas object self.gas = ct_object # gas object
self.eq_state = burned_state # HP equilibrium state self.eq_state = burned_state # HP equilibrium state
@ -27,59 +28,66 @@ class CombustionChamber:
self.Twall1 = 1100 + 273.15 self.Twall1 = 1100 + 273.15
self.Area = 6.7 * 16.7 self.Area = 6.7 * 16.7
def update_mdot (self, mdot_new): def update_mdot(self, mdot_new):
if mdot_new : self.mdot = mdot_new if mdot_new:
self.mdot = mdot_new
def update_Twall (self, Twall0=None, Twall1=None): def update_Twall(self, Twall0=None, Twall1=None):
if Twall0: self.Twall0 = Twall0 if Twall0:
if Twall1: self.Twall1 = Twall1 self.Twall0 = Twall0
if Twall1:
self.Twall1 = Twall1
def energy_balance_equation (self, Tout): def energy_balance_equation(self, Tout):
self.gas.TP = Tout, None self.gas.TP = Tout, None
h1 = self.gas.enthalpy_mass h1 = self.gas.enthalpy_mass
q1, q2 = self.heat(Tout) q1, q2 = self.heat(Tout)
return (self.mdot * (self.h0 - h1) - q1 - q2) return (self.mdot * (self.h0 - h1) - q1 - q2)
def solve (self, ): def solve(self, ):
""" Iteratively solve for outlet temperature that balance with heat loss to walls """ """ Iteratively solve for outlet temperature that balance with heat loss to walls """
meanTwall = (self.Twall0 + self.Twall1) / 2 meanTwall = (self.Twall0 + self.Twall1) / 2
T_low = meanTwall - (self.T0 - meanTwall) T_low = meanTwall - (self.T0 - meanTwall)
try: try:
f_found = optimize.root_scalar(self.energy_balance_equation, f_found = optimize.root_scalar(self.energy_balance_equation,
bracket=[T_low, self.T0]) bracket=[T_low, self.T0])
self.T1 = f_found.root self.T1 = f_found.root
except ValueError: except ValueError:
self.T1 = meanTwall self.T1 = meanTwall
return f_found.root return f_found.root
def heat (self, Tout=None): def heat(self, Tout=None):
''' Heat(W) to walls ''' ''' Heat(W) to walls '''
if Tout is None: Tout = self.T1 if Tout is None:
Tout = self.T1
Tgas = (self.T0 + Tout) / 2 Tgas = (self.T0 + Tout) / 2
return self.hA * (Tgas - self.Twall0), self.hA * (Tgas - self.Twall1) return self.hA * (Tgas - self.Twall0), self.hA * (Tgas - self.Twall1)
class CokeCharge: class CokeCharge:
def __init__ (self, t_charge, idx_oven): def __init__(self, t_charge, idx_oven):
self.t_charge = t_charge self.t_charge = t_charge
self.t_push = None self.t_push = None
self.idx_oven = idx_oven self.idx_oven = idx_oven
self.Q = 0 self.Q = 0
def bake (self, dQ): def bake(self, dQ):
self.Q += dQ self.Q += dQ
def end_baking (self, t): def end_baking(self, t):
self.t_push = t self.t_push = t
brick_thickness = 0.14 # m,
brick_thickness = 0.14 # m,
n_grid_brick = 16 # Number of Grid points inside n_grid_brick = 16 # Number of Grid points inside
wall_grid = pde.CartesianGrid([[0, brick_thickness]], n_grid_brick, periodic=False) wall_grid = pde.CartesianGrid(
wall_area = 6.7 * 16.7 # m^2 , Oven cross section area [[0, brick_thickness]], n_grid_brick, periodic=False)
wall_area = 6.7 * 16.7 # m^2 , Oven cross section area
# op_grad2 = wall_grid.make_operator_no_bc('gradient_squared', backend='scipy') # op_grad2 = wall_grid.make_operator_no_bc('gradient_squared', backend='scipy')
# op_grad = wall_grid.make_operator_no_bc('gradient', backend='scipy') # op_grad = wall_grid.make_operator_no_bc('gradient', backend='scipy')
@ -89,10 +97,11 @@ wall_area = 6.7 * 16.7 # m^2 , Oven cross section area
# op_info_grad = wall_grid._get_operator_info('gradient') # op_info_grad = wall_grid._get_operator_info('gradient')
# op_info_lap = wall_grid._get_operator_info('laplace') # op_info_lap = wall_grid._get_operator_info('laplace')
class CokeOvenBrickHeatEqn(pde.PDEBase): class CokeOvenBrickHeatEqn(pde.PDEBase):
"""Implementation of the Heat equation""" """Implementation of the Heat equation"""
def __init__ (self, bc="auto_periodic_neumann"): def __init__(self, bc="auto_periodic_neumann"):
self.bc = bc self.bc = bc
self.rho = 1900 # kg / m3 self.rho = 1900 # kg / m3
self.kCoef0 = 0.93 # W / m / K self.kCoef0 = 0.93 # W / m / K
@ -100,24 +109,24 @@ class CokeOvenBrickHeatEqn(pde.PDEBase):
self.cpCoef0 = 837.2 # J / kg / K self.cpCoef0 = 837.2 # J / kg / K
self.cpCoef1 = 251.2e-3 # J / kg / K2 self.cpCoef1 = 251.2e-3 # J / kg / K2
def k (self, T): def k(self, T):
return T * self.kCoef1 + self.kCoef0 return T * self.kCoef1 + self.kCoef0
def cp (self, T): def cp(self, T):
return T * self.cpCoef1 + self.cpCoef0 return T * self.cpCoef1 + self.cpCoef0
def update_bc (self, gradT_chamber=None, T_oven=None): def update_bc(self, gradT_chamber=None, T_oven=None):
bc0, bc1 = self.bc bc0, bc1 = self.bc
if gradT_chamber: if gradT_chamber:
self.bc[0] = {"derivative": gradT_chamber} self.bc[0] = {"derivative": gradT_chamber}
if T_oven: if T_oven:
self.bc[1] = {"value": T_oven} self.bc[1] = {"value": T_oven}
def evolution_rate(self, state, t=0): def evolution_rate(self, state, t=0):
"""implement the python version of the evolution equation""" """implement the python version of the evolution equation"""
state_lap = state.laplace(bc=self.bc) # , backend="auto") state_lap = state.laplace(bc=self.bc) # , backend="auto")
# state_grad = state.gradient(bc=self.bc, backend="scipy") # state_grad = state.gradient(bc=self.bc, backend="scipy")
state_grad2 = state.gradient_squared(bc=self.bc) # , backend="auto") state_grad2 = state.gradient_squared(bc=self.bc) # , backend="auto")
''' '''
# out_cls_grad2 = state.get_class_by_rank(op_info_grad2.rank_out) # out_cls_grad2 = state.get_class_by_rank(op_info_grad2.rank_out)
@ -131,11 +140,12 @@ class CokeOvenBrickHeatEqn(pde.PDEBase):
op_grad(state._data_full, state_grad.data) op_grad(state._data_full, state_grad.data)
op_lap(state._data_full, state_lap.data) op_lap(state._data_full, state_lap.data)
''' '''
k = self.kCoef1 * state + self.kCoef0 k = self.kCoef1 * state + self.kCoef0
cp = self.cpCoef1 * state + self.cpCoef0 cp = self.cpCoef1 * state + self.cpCoef0
state_grad_k_grad = self.kCoef1 * state_grad2 # state_grad.dot(state_grad) state_grad_k_grad = self.kCoef1 * \
state_grad2 # state_grad.dot(state_grad)
return (state_grad_k_grad + k * state_lap) / cp / self.rho return (state_grad_k_grad + k * state_lap) / cp / self.rho
''' '''
@ -156,15 +166,17 @@ class CokeOvenBrickHeatEqn(pde.PDEBase):
return pde_rhs return pde_rhs
''' '''
class RefractoryWall: class RefractoryWall:
def __init__ (self, T0): def __init__(self, T0):
self.T_oven = T0 self.T_oven = T0
self.T_chamber = T0 self.T_chamber = T0
self.q_chamber = 0. self.q_chamber = 0.
self.T_internal = pde.ScalarField(wall_grid, T0) self.T_internal = pde.ScalarField(wall_grid, T0)
self.eqn = CokeOvenBrickHeatEqn(bc=[{"derivative": 0}, {"value": self.T_oven}]) self.eqn = CokeOvenBrickHeatEqn(
bc=[{"derivative": 0}, {"value": self.T_oven}])
def update_bc (self, Q=None, T_oven=None): def update_bc(self, Q=None, T_oven=None):
# Q = - k(T) gradT # Q = - k(T) gradT
# T_chamber = self.T_internal.get_boundary_values(axis=0, upper=False, bc=self.eqn.bc) # T_chamber = self.T_internal.get_boundary_values(axis=0, upper=False, bc=self.eqn.bc)
k0 = self.eqn.k(self.T_chamber) k0 = self.eqn.k(self.T_chamber)
@ -174,13 +186,14 @@ class RefractoryWall:
gradT = None gradT = None
self.eqn.update_bc(gradT, T_oven) self.eqn.update_bc(gradT, T_oven)
def solve (self, dt): def solve(self, dt):
# solution = self.eqn.solve (eqn, bc) # solution = self.eqn.solve (eqn, bc)
self.T_internal = self.eqn.solve( self.T_internal = self.eqn.solve(
self.T_internal, t_range=dt, dt=30., tracker='consistency', backend="numpy") self.T_internal, t_range=dt, dt=30., tracker='consistency', backend="numpy")
self.T_chamber = self.T_internal.get_boundary_values(axis=0, upper=False, bc=self.eqn.bc) self.T_chamber = self.T_internal.get_boundary_values(
axis=0, upper=False, bc=self.eqn.bc)
def heat_to_oven (self): def heat_to_oven(self):
""" NOT YET IMPLEMENTED """ """ NOT YET IMPLEMENTED """
return 0.0 return 0.0
@ -197,11 +210,12 @@ def Twall_model(x):
''' '''
return np.interp(x, Twall_table[0], Twall_table[1]) return np.interp(x, Twall_table[0], Twall_table[1])
class OvenChamber: class OvenChamber:
def __init__ (self): def __init__(self):
self.content = None self.content = None
def get_charge_temperature (self, t): def get_charge_temperature(self, t):
""" Return temperature of coal charge content at oven wall """ """ Return temperature of coal charge content at oven wall """
if self.content: if self.content:
elapsed_time = t - self.content.t_charge elapsed_time = t - self.content.t_charge
@ -209,21 +223,23 @@ class OvenChamber:
elapsed_time = 0. elapsed_time = 0.
return Twall_model(elapsed_time) return Twall_model(elapsed_time)
def bake (self, q): def bake(self, q):
""" Add transferred heat to coal charge content """ """ Add transferred heat to coal charge content """
if self.content: if self.content:
self.content.bake(q) self.content.bake(q)
def charge (self, coal_charge): def charge(self, coal_charge):
""" Update content with fresh coal is charged """ """ Update content with fresh coal is charged """
self.content = coal_charge self.content = coal_charge
def wall_solve_wrapper(t_range, wall): def wall_solve_wrapper(t_range, wall):
wall.solve(t_range) wall.solve(t_range)
return wall.T_internal, wall.T_chamber return wall.T_internal, wall.T_chamber
class Battery: class Battery:
def load_state(self): def load_state(self):
with open('gas.history', 'rb') as gas_history_file: with open('gas.history', 'rb') as gas_history_file:
self.gas_t_history = pickle.load(gas_history_file) self.gas_t_history = pickle.load(gas_history_file)
@ -237,34 +253,37 @@ class Battery:
with open('oven.state', 'rb') as coke_state_file: with open('oven.state', 'rb') as coke_state_file:
self.processing = pickle.load(coke_state_file) self.processing = pickle.load(coke_state_file)
def __init__ (self, name, size, heat_program, charge_program, burned_gas_state, hv, init_from_file=False): def __init__(self, name, size, heat_program, charge_program, burned_gas_state, hv, init_from_file=False):
self.name = name # Battery name self.name = name # Battery name
self.size = size # Size of battery, number of ovens self.size = size # Size of battery, number of ovens
self.heat_program = heat_program # Heat program or schedule object self.heat_program = heat_program # Heat program or schedule object
self.charge_program = charge_program # Charge program of schedule object self.charge_program = charge_program # Charge program of schedule object
self.t = 0 # Battery time self.t = 0 # Battery time
self.t_last = 0 # Time of last Push/Charge self.t_last = 0 # Time of last Push/Charge
self.processing = [] # List of Coke charges under processing(drying) # List of Coke charges under processing(drying)
self.product = [] # List of Coke charges done(completed) self.processing = []
# List of Coke charges done(completed)
self.product = []
self.gas = ct.Solution('gri30.xml') self.gas = ct.Solution('gri30.xml')
self.gas.TPX = burned_gas_state # Burned gas T, P, X self.gas.TPX = burned_gas_state # Burned gas T, P, X
T0, P0, X0 = self.gas.TPX T0, P0, X0 = self.gas.TPX
self.T0 = T0 self.T0 = T0
self.P0 = P0 self.P0 = P0
self.X0 = X0 self.X0 = X0
self.sequence_idx = 0 # Integer, 0 ~ (size-1), progress index for oven sequence array # Integer, 0 ~ (size-1), progress index for oven sequence array
self.sequence_idx = 0
self.wall_t_history = [] self.wall_t_history = []
self.gas_t_history = [] self.gas_t_history = []
self.hv = hv # Base unit heat J/kg self.hv = hv # Base unit heat J/kg
self.normal_heat = self.heat_program.f(-1) # GJ / rev self.normal_heat = self.heat_program.f(-1) # GJ / rev
# Energy input to battery # Energy input to battery
Q0 = self.normal_heat * 1e9 * 3 / 3600 # GJ/rev => J/s (W) Q0 = self.normal_heat * 1e9 * 3 / 3600 # GJ/rev => J/s (W)
# Equivalent Fuel+Air mass flow # Equivalent Fuel+Air mass flow
mdot0 = Q0 / hv # (J/s) / (J/kg) => kg/s mdot0 = Q0 / hv # (J/s) / (J/kg) => kg/s
self.mdot0 = mdot0 # kg / s self.mdot0 = mdot0 # kg / s
# chambers[0] - walls_0[0] - ovens[0] - walls_1[0] - chambers[1] - walls_0[1] - ... # chambers[0] - walls_0[0] - ovens[0] - walls_1[0] - chambers[1] - walls_0[1] - ...
# ... walls_1[i-1] - chambers[i] - walls_0[i] - ovens[i] - walls_1[i] - chambers[i+1] - walls_0[i+1] - ... # ... walls_1[i-1] - chambers[i] - walls_0[i] - ovens[i] - walls_1[i] - chambers[i+1] - walls_0[i+1] - ...
@ -287,10 +306,11 @@ class Battery:
RefractoryWall(Twall_model(0)) RefractoryWall(Twall_model(0))
for ioven in range(self.size) for ioven in range(self.size)
] ]
# For 1~4 Coke Ovens with n+5 P/C sequence # For 1~4 Coke Ovens with n+5 P/C sequence
start_indices = [1, 3, 5, 2, 4] start_indices = [1, 3, 5, 2, 4]
self.oven_idx_order = np.concatenate([np.array(range(i0 - 1, self.size, 5)) for i0 in start_indices]) self.oven_idx_order = np.concatenate(
[np.array(range(i0 - 1, self.size, 5)) for i0 in start_indices])
if init_from_file: if init_from_file:
print("Initializaton from file") print("Initializaton from file")
@ -314,40 +334,41 @@ class Battery:
# Recover Oven State # Recover Oven State
for coal in self.processing: for coal in self.processing:
self.ovens[coal.idx_oven].content = coal self.ovens[coal.idx_oven].content = coal
else: else:
print("Initializaton Start") print("Initializaton Start")
# 정상 상태 만들기: 모든 문에 n_cycle 회 장입 # 정상 상태 만들기: 모든 문에 n_cycle 회 장입
n_cycle = 3 # 모든 문 장입 반복 횟수 n_cycle = 3 # 모든 문 장입 반복 횟수
period_over_dt = 6. # period/dt, 장입 간격 / 초기화 time step 크기 period_over_dt = 6. # period/dt, 장입 간격 / 초기화 time step 크기
normal_period = self.charge_program.period(-1) # 감산 전 장입 간격 (주기) normal_period = self.charge_program.period(-1) # 감산 전 장입 간격 (주기)
dt = normal_period / period_over_dt # Simulation Time Step dt = normal_period / period_over_dt # Simulation Time Step
self.t = - normal_period * self.size * n_cycle # 정상상태 생성 모사 시간 = 장입 간격 * 총 장입 횟수 self.t = - normal_period * self.size * \
n_cycle # 정상상태 생성 모사 시간 = 장입 간격 * 총 장입 횟수
self.t_last = self.t # 마지막 장입을 정상상태 시뮬레이션 시작 시각으로 설정 self.t_last = self.t # 마지막 장입을 정상상태 시뮬레이션 시작 시각으로 설정
# initialization time loop # initialization time loop
for i in range(int(np.ceil(self.size * period_over_dt * n_cycle))): for i in range(int(np.ceil(self.size * period_over_dt * n_cycle))):
# for i in range(3): # for i in range(3):
""" Fill battety with normal charge rate """ """ Fill battety with normal charge rate """
self.update(dt) # Time adavancement self.update(dt) # Time adavancement
def mdot (self, t): def mdot(self, t):
return self.mdot0 * self.heat_program.f(t) / self.normal_heat return self.mdot0 * self.heat_program.f(t) / self.normal_heat
def next_oven (self): def next_oven(self):
''' Index of the oven to which apply push and charge ''' ''' Index of the oven to which apply push and charge '''
next_oven_id = self.oven_idx_order[self.sequence_idx % self.size] next_oven_id = self.oven_idx_order[self.sequence_idx % self.size]
self.sequence_idx += 1 self.sequence_idx += 1
return next_oven_id return next_oven_id
def bake (self, dt): def bake(self, dt):
# update combustion chamber equilibrium temperature # update combustion chamber equilibrium temperature
# Tad = 연료 조성과 공연비로 결정 # Tad = 연료 조성과 공연비로 결정
# m_dot = 연료 발열량과 공급열량 공연비로 결정 # m_dot = 연료 발열량과 공급열량 공연비로 결정
# m(h1 - h0) = hA(Tgas - Twall) => solve with initial T0 = Tad # m(h1 - h0) = hA(Tgas - Twall) => solve with initial T0 = Tad
# Loop all combustion chambers # Loop all combustion chambers
# update chamber wall temperatures and mass flow rates # update chamber wall temperatures and mass flow rates
# solve for equilibrium heat to walls # solve for equilibrium heat to walls
for i_chamber, chmbr in enumerate(self.chambers): for i_chamber, chmbr in enumerate(self.chambers):
@ -363,15 +384,20 @@ class Battery:
chmbr.update_mdot(self.mdot(self.t)/self.size) chmbr.update_mdot(self.mdot(self.t)/self.size)
chmbr.update_Twall( chmbr.update_Twall(
Twall0=(wall_lower.T_chamber if wall_lower else wall_upper.T_chamber), Twall0=(
Twall1=(wall_upper.T_chamber if wall_upper else wall_lower.T_chamber), wall_lower.T_chamber if wall_lower else wall_upper.T_chamber),
Twall1=(
wall_upper.T_chamber if wall_upper else wall_lower.T_chamber),
) )
print(f"t={self.t:6.2} : {chmbr.Twall0} K | Chamber {i_chamber} | {chmbr.Twall1} K ") print(
f"t={self.t:6.2} : {chmbr.Twall0} K | Chamber {i_chamber} | {chmbr.Twall1} K ")
chmbr.solve() chmbr.solve()
Q1, Q2 = chmbr.heat() # W (J/s) Q1, Q2 = chmbr.heat() # W (J/s)
if wall_lower: wall_lower.update_bc(Q=Q1) if wall_lower:
if wall_upper: wall_upper.update_bc(Q=Q2) wall_lower.update_bc(Q=Q1)
if wall_upper:
wall_upper.update_bc(Q=Q2)
# Loop all ovens # Loop all ovens
# update oven wall temperatures using coke charge age # update oven wall temperatures using coke charge age
# solve heat equations of all walls # solve heat equations of all walls
@ -383,7 +409,8 @@ class Battery:
wall_upper.update_bc(T_oven=T_oven) wall_upper.update_bc(T_oven=T_oven)
with Pool(12) as pool: with Pool(12) as pool:
wall_sln = pool.starmap(wall_solve_wrapper, [((dt*60*60), w) for w in self.walls_0+self.walls_1]) wall_sln = pool.starmap(wall_solve_wrapper, [(
(dt*60*60), w) for w in self.walls_0+self.walls_1])
# wall_lower.solve(dt * 60 * 60) # convert hours to seconds # wall_lower.solve(dt * 60 * 60) # convert hours to seconds
# wall_upper.solve(dt * 60 * 60) # convert hours to seconds # wall_upper.solve(dt * 60 * 60) # convert hours to seconds
@ -400,48 +427,49 @@ class Battery:
''' '''
# advance time oven brick # advance time oven brick
# from chamber heat flux boundary condition # from chamber heat flux boundary condition
# to oven fixed temperature boundary condition # to oven fixed temperature boundary condition
# integrate heat to oven # 오븐 벽면 온도 우선 시간 함수로 # integrate heat to oven # 오븐 벽면 온도 우선 시간 함수로
def push_and_charge (self, coke_charge): def push_and_charge(self, coke_charge):
if len(self.processing) >= self.size: if len(self.processing) >= self.size:
self.push(coke_charge.t_charge) self.push(coke_charge.t_charge)
self.charge(coke_charge) self.charge(coke_charge)
def push (self, t): def push(self, t):
""" Push complete coke out of oven """ """ Push complete coke out of oven """
coke = self.processing.pop(0) coke = self.processing.pop(0)
coke.end_baking(t) coke.end_baking(t)
self.product.append(coke) self.product.append(coke)
def charge (self, coke_charge): def charge(self, coke_charge):
self.ovens[coke_charge.idx_oven].charge(coke_charge) self.ovens[coke_charge.idx_oven].charge(coke_charge)
self.processing.append(coke_charge) self.processing.append(coke_charge)
def dQ (self, dt): def dQ(self, dt):
return self.heat_program.dQ(self.t, self.t+dt) return self.heat_program.dQ(self.t, self.t+dt)
def is_pc_time (self, dt): def is_pc_time(self, dt):
''' Whether P/C should be done in this time step ''' ''' Whether P/C should be done in this time step '''
period = self.charge_program.period(self.t) period = self.charge_program.period(self.t)
return self.t + dt >= period + self.t_last return self.t + dt >= period + self.t_last
def update (self, dt): def update(self, dt):
# dQ = self.heat_program.dQ(self.t, self.t+dt) # t, t+dt 사이 공급하는 열량, array 로 대체 필요 # dQ = self.heat_program.dQ(self.t, self.t+dt) # t, t+dt 사이 공급하는 열량, array 로 대체 필요
# t 에서 t+dt 까지 탄화실 가열 # t 에서 t+dt 까지 탄화실 가열
self.bake(dt) self.bake(dt)
period = self.charge_program.period(self.t) # 현재 장입 시간 간격 period = self.charge_program.period(self.t) # 현재 장입 시간 간격
# 마지막 장입탄 장입 시각 # 마지막 장입탄 장입 시각
latest_coke_charge = self.processing[-1].t_charge if len(self.processing) > 0 else self.t_last latest_coke_charge = self.processing[-1].t_charge if len(
self.processing) > 0 else self.t_last
# t_last + period 가 t, t + dt 사이에 들어오는 것 검사 # t_last + period 가 t, t + dt 사이에 들어오는 것 검사
# t + dt 가 다음 추출/장입 시각 이후일 때 => 이번 time step 에 추출/장입을 실행해야함 # t + dt 가 다음 추출/장입 시각 이후일 때 => 이번 time step 에 추출/장입을 실행해야함
if self.t + dt >= period + self.t_last : if self.t + dt >= period + self.t_last:
# 마지막 장입 시각 + 장입 시간 간격 이 이번 time step 에 포함됨 # 마지막 장입 시각 + 장입 시간 간격 이 이번 time step 에 포함됨
# 일정한 간격으로 장입 진행 중, 마지막 장입 시간 += 장입 간격 # 일정한 간격으로 장입 진행 중, 마지막 장입 시간 += 장입 간격
if self.t < self.t_last + period: if self.t < self.t_last + period:
@ -450,25 +478,27 @@ class Battery:
# 이번 time step 끝을 마지막 장입 시각으로 업데이트 # 이번 time step 끝을 마지막 장입 시각으로 업데이트
else: else:
self.t_last = self.t + dt self.t_last = self.t + dt
# 추출/장입 실행 # 추출/장입 실행
i_oven = self.next_oven() i_oven = self.next_oven()
# oven = self.ovens[i_oven] # oven = self.ovens[i_oven]
fresh_coal = CokeCharge(self.t + dt, i_oven) fresh_coal = CokeCharge(self.t + dt, i_oven)
self.push_and_charge(fresh_coal) self.push_and_charge(fresh_coal)
print(f"On {i_oven} P/C within [ {self.t:7.3} , {self.t + dt:7.3} ].", print(f"On {i_oven} P/C within [ {self.t:7.3} , {self.t + dt:7.3} ].",
f"{self.t + dt - latest_coke_charge:7.3} since last P/C. ", f"{self.t + dt - latest_coke_charge:7.3} since last P/C. ",
f"period = {self.charge_program.period(self.t):7.3}",) f"period = {self.charge_program.period(self.t):7.3}",)
# 시뮬레이션 시간 업데이트 # 시뮬레이션 시간 업데이트
self.t += dt self.t += dt
self.gas_t_history.append((self.t, [chmbr.T1 for chmbr in self.chambers])) self.gas_t_history.append(
self.wall_t_history.append((self.t, [(wl.T_chamber, wl.T_internal.data, wl.T_oven, wu.T_oven, wu.T_internal.data, wu.T_chamber) for wl, wu in zip(self.walls_0, self.walls_1)])) (self.t, [chmbr.T1 for chmbr in self.chambers]))
self.wall_t_history.append((self.t, [(wl.T_chamber, wl.T_internal.data, wl.T_oven, wu.T_oven,
wu.T_internal.data, wu.T_chamber) for wl, wu in zip(self.walls_0, self.walls_1)]))
def coke_oven_exhaust_stoichiometry (phi=1.0, return_unburned=False):
def coke_oven_exhaust_stoichiometry(phi=1.0, return_unburned=False):
# Define the oxidizer composition, here air with 21 mol-% O2 and 79 mol-% N2 # Define the oxidizer composition, here air with 21 mol-% O2 and 79 mol-% N2
air = "O2:1,N2:3.762" air = "O2:1,N2:3.762"
@ -479,8 +509,9 @@ def coke_oven_exhaust_stoichiometry (phi=1.0, return_unburned=False):
mix.TP = 25+273.15, ct.one_atm mix.TP = 25+273.15, ct.one_atm
mix.set_equivalence_ratio(phi=phi, fuel=coke_oven_fuel, oxidizer=air) mix.set_equivalence_ratio(phi=phi, fuel=coke_oven_fuel, oxidizer=air)
element_X = {ename: mix.elemental_mole_fraction(ename) for ename in mix.element_names} element_X = {ename: mix.elemental_mole_fraction(
ename) for ename in mix.element_names}
exhaust = ct.Solution('gri30.xml') exhaust = ct.Solution('gri30.xml')
exhaust.TPX = (25+273.15, ct.one_atm, exhaust.TPX = (25+273.15, ct.one_atm,
{ {
@ -496,39 +527,42 @@ def coke_oven_exhaust_stoichiometry (phi=1.0, return_unburned=False):
else: else:
return exhaust.mole_fraction_dict(threshold=-1) return exhaust.mole_fraction_dict(threshold=-1)
class HeatSchedule: class HeatSchedule:
def __init__ (self, xp, fp): def __init__(self, xp, fp):
self.xp = xp self.xp = xp
self.fp = fp self.fp = fp
self.f = lambda x: np.interp(x, self.xp, self.fp) self.f = lambda x: np.interp(x, self.xp, self.fp)
def dQ(self, t0, t1): def dQ(self, t0, t1):
x = np.linspace(t0, t1, 31) x = np.linspace(t0, t1, 31)
return np.trapz(self.f(x), x) return np.trapz(self.f(x), x)
class ChargeSchedule: class ChargeSchedule:
def __init__ (self, normal_load, service_start, service_time, service_load, aux_start, aux_time, aux_load): def __init__(self, normal_load, service_start, service_time, service_load, aux_start, aux_time, aux_load):
self.xp = np.array([service_start, service_start, service_start+service_time, service_start+service_time, self.xp = np.array([service_start, service_start, service_start+service_time, service_start+service_time,
aux_start, aux_start, aux_start+aux_time, aux_start+aux_time, ]) aux_start, aux_start, aux_start+aux_time, aux_start+aux_time, ])
self.fp = np.array([normal_load, service_load, service_load, normal_load, self.fp = np.array([normal_load, service_load, service_load, normal_load,
normal_load, aux_load, aux_load, normal_load]) normal_load, aux_load, aux_load, normal_load])
self.f = lambda x: np.interp(x, self.xp, self.fp) self.f = lambda x: np.interp(x, self.xp, self.fp)
def to_charge (self, t0, t1): def to_charge(self, t0, t1):
self.f(t0) self.f(t0)
return np.trapz(self.f(x), x) return np.trapz(self.f(x), x)
def period (self, t): def period(self, t):
return 24 / self.f(t) return 24 / self.f(t)
if __name__ == "__main__": if __name__ == "__main__":
# Define the oxidizer composition, here air with 21 mol-% O2 and 79 mol-% N2 # Define the oxidizer composition, here air with 21 mol-% O2 and 79 mol-% N2
air = "O2:0.21,N2:0.79" air = "O2:0.21,N2:0.79"
coke_oven_fuel = "H2:6.42, O2:0.39, N2:47.28, CH4:1.79, CO:24.25, CO2:19.72, C2H4:0.13, C2H6:0.04" coke_oven_fuel = "H2:6.42, O2:0.39, N2:47.28, CH4:1.79, CO:24.25, CO2:19.72, C2H4:0.13, C2H6:0.04"
f_found = optimize.root_scalar(lambda x: coke_oven_exhaust_stoichiometry(x)["O2"] - 0.045, f_found = optimize.root_scalar(lambda x: coke_oven_exhaust_stoichiometry(x)["O2"] - 0.045,
bracket=[1e-300, 1]) bracket=[1e-300, 1])
# equivalence ratio for O2 4.5 % in exhaust gas (stoichiometric) # equivalence ratio for O2 4.5 % in exhaust gas (stoichiometric)
phi_O2_045 = f_found.root phi_O2_045 = f_found.root
@ -553,14 +587,16 @@ if __name__ == "__main__":
gas_in_state = gas.TPX gas_in_state = gas.TPX
# Time(Hours) - GJ/rev # Time(Hours) - GJ/rev
sample_program = np.array(open('sample_heat_221128_3A.txt').read().split(), dtype=np.double).reshape((-1,2)) sample_program = np.array(open(
'sample_heat_221128_3A-Plan2.txt').read().split(), dtype=np.double).reshape((-1, 2))
heating_plan = HeatSchedule(*sample_program.T) heating_plan = HeatSchedule(*sample_program.T)
charging_plan = ChargeSchedule( 82, 9, 13, 1e-12, 9+13+18, 4, 1e-12 ) charging_plan = ChargeSchedule(82, 9, 12, 1e-12, 9+13+18, 4, 1e-12)
n_doors = 66 n_doors = 66
load_state = False load_state = True
bat3A = Battery("3A", n_doors, heating_plan, charging_plan, gas_in_state, hv, init_from_file=load_state) bat3A = Battery("3A", n_doors, heating_plan, charging_plan,
gas_in_state, hv, init_from_file=load_state)
if not load_state: if not load_state:
with open('gas.history', 'wb') as gas_history_file: with open('gas.history', 'wb') as gas_history_file:
@ -575,8 +611,8 @@ if __name__ == "__main__":
with open('oven.state', 'wb') as wall_history_file: with open('oven.state', 'wb') as wall_history_file:
pickle.dump(bat3A.processing, wall_history_file) pickle.dump(bat3A.processing, wall_history_file)
dt = 5. * 1./60. # 5 min dt = 5. * 1./60. # 5 min
for it in range (int(60/dt)): # 시뮬레이션 시간 도메인 = 60시간 for it in range(int(60/dt)): # 시뮬레이션 시간 도메인 = 60시간
bat3A.update(dt) bat3A.update(dt)
with open('gas.history2', 'wb') as gas_history_file: with open('gas.history2', 'wb') as gas_history_file:

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long