From 0f7e6bec973570c4c7976d5da4054145a5c9f91e Mon Sep 17 00:00:00 2001 From: Yeongdo Park Date: Thu, 8 Dec 2022 19:52:49 +0900 Subject: [PATCH] Implementing simulator --- .gitignore | 2 + Battery.py | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 .gitignore create mode 100644 Battery.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bad9f5f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.ipynb_checkpoints +*~ diff --git a/Battery.py b/Battery.py new file mode 100644 index 0000000..c6bc905 --- /dev/null +++ b/Battery.py @@ -0,0 +1,184 @@ +from functools import reduce + +import numpy as np + +import cantera as ct + +class CokeCharge: + + def __init__ (self, t_charge, idx_oven): + self.t_charge = t_charge + self.t_push = None + self.idx_oven = idx_oven + self.Q = 0 + + def bake (self, dQ): + self.Q += dQ + + def end_baking (self, t): + self.t_push = t + + +class Battery: + + def __init__ (self, name, size, heat_program, charge_program, T_combustion_0): + self.name = name # Battery name + self.size = size # Size of battery, number of ovens + self.heat_program = heat_program # Heat program or schedule object + self.charge_program = charge_program # Charge program of schedule object + self.t = 0 # Battery time + self.t_last = 0 # Time of last Push/Charge + self.processing = [] # List of Coke charges under processing(drying) + self.product = [] # List of Coke charges done(completed) + self.T0 = T_combustion_0 # Burned gas temperature + self.sequence_idx = 0 # Integer, 0 ~ (size-1), progress index for oven sequence array + + # 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[size-2] - chambers[size-1] - walls_0[size-1] - ovens[size-1] - walls_1[size-1] - chambers[size] + + self.chambers = np.zeros(self.size+1) + self.ovens = np.zeros(self.size) + self.walls_0 = np.zeros(self.size) + self.walls_1 = np.zeros(self.size) + + # For 1~4 Coke Ovens with n+5 P/C sequence + 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]) + + # 정상 상태 만들기: 모든 문에 n_cycle 회 장입 + n_cycle = 3 # 모든 문 장입 반복 횟수 + period_over_dt = 11. # period/dt, 장입 간격 / 초기화 time step 크기 + normal_period = self.charge_program.period(-1) # 감산 전 장입 간격 (주기) + dt = normal_period / period_over_dt # Simulation Time Step + + self.t = - normal_period * self.size * n_cycle # 정상상태 생성 모사 시간 = 장입 간격 * 총 장입 횟수 + self.t_last = self.t # 마지막 장입을 정상상태 시뮬레이션 시작 시각으로 설정 + + # initialization time loop + for i in range(int(np.ceil(self.size * period_over_dt * n_cycle))): + """ Fill battety with normal charge rate """ + self.update(dt) # Time adavancement + + def next_oven (self): + ''' Index of the oven to which apply push and charge ''' + next_oven_id = self.oven_idx_order[self.sequence_idx % self.size] + self.sequence_idx += 1 + return next_oven_id + + def bake (self, dt): + # update combustion chamber equilibrium temperature + # Tad = 연료 조성과 공연비로 결정 + # m_dot = 연료 발열량과 공급열량 공연비로 결정 + # m(h1 - h0) = hA(Tgas - Twall) => solve with initial T0 = Tad + + # Loop all combustion chambers + # update chamber wall temperatures and mass flow rates + # solve for equilibrium heat to walls + for i_chamber, chmbr in enumerate(self.chambers): + try: + wall_lower = self.walls_1[i_chamber-1] + except IndexError: + wall_lower = None + + try: + wall_upper = self.walls_0[i_chamber] + except IndexError: + wall_upper = None + + chmbr.update_mdot() + chmbr.update_Twall( + wall_lower.T_chamber if wall_lower else wall_upper.T_chamber, + wall_upper.T_chamber if wall_upper else wall_lower.T_chamber, + ) + chmbr.solve() + Q1, Q2 = chmbr.heat() + if wall_lower: wall_lower.update_bc(Q=Q1) + if wall_upper: wall_upper.update_bc(Q=Q2) + + # Loop all ovens + # update oven wall temperatures using coke charge age + # solve heat equations of all walls + # bake charge in oven + for i_oven, (oven, wall_lower, wall_upper) in enumerate(zip(self.ovens, self.walls_0, self.walls_1)): + oven.get_charge_temperature() + + wall_lower.update_bc(Toven=Q1) + wall_upper.update_bc(Toven=Q2) + + wall_lower.solve(dt) + wall_upper.solve(dt) + + ql = wall_lower.heat_to_oven() + qu = wall_upper.heat_to_oven() + + oven.bake(ql+qu) + + # advance time oven brick + # from chamber heat flux boundary condition + # to oven fixed temperature boundary condition + + # integrate heat to oven # 오븐 벽면 온도 우선 시간 함수로 + + dQ = self.dQ(dt) # array, dQ pairs of all oven taking from both walls + + for cc in self.processing: + cc.bake(dQ) # bake.(dQ[cc.idx_oven]) + + def push_and_charge (self, coke_charge): + if len(self.processing) >= self.size: + self.push(coke_charge.t_charge) + self.charge(coke_charge) + + def push (self, t): + """ Push complete coke out of oven """ + coke = self.processing.pop(0) + coke.end_baking(t) + self.product.append(coke) + + def charge (self, coke_charge): + self.ovens[coke_charge.i_oven].charge(coke_charge) + self.processing.append(coke_charge) + + def dQ (self, dt): + return self.heat_program.dQ(self.t, self.t+dt) + + def is_pc_time (self, dt): + ''' P/C should be done in this time step ''' + return self.t + dt >= period + self.t_last + + def update (self, dt): + # dQ = self.heat_program.dQ(self.t, self.t+dt) # t, t+dt 사이 공급하는 열량, array 로 대체 필요 + + # t 에서 t+dt 까지 탄화실 가열 + self.bake(dt) + + period = self.charge_program.period(self.t) # 현재 장입 시간 간격 + + # 마지막 장입탄 장입 시각 + latest_coke_charge = self.processing[-1].t_charge if len(self.processing) > 0 else self.t_last + + # t_last + period 가 t, t + dt 사이에 들어오는 것 검사 + # t + dt 가 다음 추출/장입 시각 이후일 때 => 이번 time step 에 추출/장입을 실행해야함 + if self.t + dt >= period + self.t_last : + print(f"Push timing within [ {self.t} , {self.t + dt} ].", + f"{self.t + dt - latest_coke_charge} since last P/C. ", + f"P/C period = {self.charge_program.period(self.t)}",) + + # 마지막 장입 시각 + 장입 시간 간격 이 이번 time step 에 포함됨 + # 일정한 간격으로 장입 진행 중, 마지막 장입 시간 += 장입 간격 + if self.t < self.t_last + period: + self.t_last += period + # 마지막 장입 이후 현재 장입 간격보다 긴 시간이 경과함 (장입 간격이 짧아짐; 감산 끝남 등) + # 이번 time step 끝을 마지막 장입 시각으로 업데이트 + else: + self.t_last = self.t + dt + + # 추출/장입 실행 + i_oven = self.next_oven() + # oven = self.ovens[i_oven] + fresh_coal = CokeCharge(self.t + dt, i_oven) + self.push_and_charge(fresh_coal) + + # 시뮬레이션 시간 업데이트 + self.t += dt \ No newline at end of file