import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
# Data
# Constants
R = 8.314 # J/mol·K
T0 = 571 # K, reactor inlet temp
T_c0 = 320 # K, coolant inlet temp
# Kinetic parameters at 571 K
# k1 = 10.0 /60 # m^3/mol s
# k2 = 5.0 /60 # m^3/mol^0.2 s
# k3 = 2.0 /60 # 1/s
# Define Arrhenius function to convert to k(T) in consistent SI units (per s)
def k_arrhenius(T, k0, Ea):
R = 8.314 # J/mol·K
return (k0 / 60) * np.exp(-Ea / (R * T)) # convert k0 from per min to per s
# Parameters
k0_1, Ea1 = 1.1516e+08, 77.22e3 # m^3/mol·min, J/mol
k0_2, Ea2 = 1.2133e+09, 91.59e3 # mol^(-0.2)·m^(0.6)/min, J/mol
k0_3, Ea3 = 1.2106e+06, 63.32e3 # 1/min, J/mol
# Heat of reactions (J/mol)
dH1 = -50000
dH2 = -30000
dHD = 25000
# Thermophysical properties
Cp = 150 # J/mol·K (reacting fluid)
rho_c = 1000 # kg/m³
Cp_c = 4180 # J/kg·K (coolant)
U = 100 # W/m²·K
# Reactor parameters
v0_total = 100/60 # m^3/s
ca0 = 2 # mol/m^3
n_tubes = 25
d_tube = 20 / 100.0 # m
l_tube = 10 # m
v0 = v0_total / n_tubes # per tube
# Tube geometry
A_tube = np.pi * (d_tube / 2)**2 # m²
V_tube = A_tube * l_tube # m³
A_surf = np.pi * d_tube * l_tube # heat transfer area per tube (m²)
# Coolant flow rate (kg/s)
m_dot_c = 3.2 /60
m_dot_c = m_dot_c / n_tubes # per tube
# Initial conditions: [C_A, C_D, C_U1, C_U2, T, T_coolant]
y0 = [ca0, 0, 0, 0, T0, T_c0]
# System of ODEs
def odes(V, y):
C_A, C_D, C_U1, C_U2, T, T_c = y
k1 = k_arrhenius(T, k0_1, Ea1)
k2 = k_arrhenius(T, k0_2, Ea2)
k3 = k_arrhenius(T, k0_3, Ea3)
r1 = k1 * C_A**2
r2 = k2 * C_A**1.2
r3 = k3 * C_D
# TODO: Write correct expressions for
# rates of individual species
r_A = -r1 -r2
r_D = 0.5*r1 -r3
r_U1 = r2
r_U2 = r3
# TODO: Write correct expressions for
# Mole balance
dC_A_dV = r_A/v0
dC_D_dV = r_D/v0
dC_U1_dV = r_U1/v0
dC_U2_dV = r_U2/v0
# TODO: Write correct expressions for
# Energy balance
Q_gen = (-r1*dH1) + (-r2*dH2) + (r3*dHD) # J/s
Q_removal = U * A_surf * (T-T_c) # W
C_total = C_A + C_D + C_U1 + C_U2 + 1e-6 # avoid zero division
F_total = C_total * v0 # mol/s
# TODO: Write correct expressions for
# Energy balance (reactor side) in terms of Q_gen, Q_removal, and F_total
dT_dV = (Q_gen - Q_removal) / (m_dot_c * Cp_c) # K/m³
# TODO: Write correct expressions for
# Energy balance (coolant side) in terms of Q_removal, m_dot_c, Cp_c
dT_c_dV = Q_removal / (m_dot_c * Cp_c) # K/m³
return [dC_A_dV, dC_D_dV, dC_U1_dV, dC_U2_dV,dT_dV,dT_c_dV]
# Solve ODEs
V_span = (0, V_tube)
V_eval = np.linspace(V_span[0], V_span[1], 100)
solution = solve_ivp(odes, V_span, y0, t_eval=V_eval, method='BDF')
C_A, C_D, C_U1, C_U2, T, T_c = solution.y
aW1wb3J0IG51bXB5IGFzIG5wCmZyb20gc2NpcHkuaW50ZWdyYXRlIGltcG9ydCBzb2x2ZV9pdnAKaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdAoKIyBEYXRhCgojIENvbnN0YW50cwpSID0gOC4zMTQgICMgSi9tb2zCt0sKVDAgPSA1NzEgICMgSywgcmVhY3RvciBpbmxldCB0ZW1wClRfYzAgPSAzMjAgICMgSywgY29vbGFudCBpbmxldCB0ZW1wCgojIEtpbmV0aWMgcGFyYW1ldGVycyBhdCA1NzEgSwojIGsxID0gMTAuMCAvNjAgIyBtXjMvbW9sIHMKIyBrMiA9IDUuMCAgLzYwICMgbV4zL21vbF4wLjIgcwojIGszID0gMi4wICAvNjAgIyAxL3MKCiMgRGVmaW5lIEFycmhlbml1cyBmdW5jdGlvbiB0byBjb252ZXJ0IHRvIGsoVCkgaW4gY29uc2lzdGVudCBTSSB1bml0cyAocGVyIHMpCmRlZiBrX2Fycmhlbml1cyhULCBrMCwgRWEpOgogICAgUiA9IDguMzE0ICAjIEovbW9swrdLCiAgICByZXR1cm4gKGswIC8gNjApICogbnAuZXhwKC1FYSAvIChSICogVCkpICAjIGNvbnZlcnQgazAgZnJvbSBwZXIgbWluIHRvIHBlciBzCgojIFBhcmFtZXRlcnMKazBfMSwgRWExID0gMS4xNTE2ZSswOCwgNzcuMjJlMyAgIyBtXjMvbW9swrdtaW4sIEovbW9sCmswXzIsIEVhMiA9IDEuMjEzM2UrMDksIDkxLjU5ZTMgICMgbW9sXigtMC4yKcK3bV4oMC42KS9taW4sIEovbW9sCmswXzMsIEVhMyA9IDEuMjEwNmUrMDYsIDYzLjMyZTMgICMgMS9taW4sIEovbW9sCgojIEhlYXQgb2YgcmVhY3Rpb25zIChKL21vbCkKZEgxID0gLTUwMDAwCmRIMiA9IC0zMDAwMApkSEQgPSAyNTAwMAoKIyBUaGVybW9waHlzaWNhbCBwcm9wZXJ0aWVzCkNwID0gMTUwICAgICAgIyBKL21vbMK3SyAocmVhY3RpbmcgZmx1aWQpCnJob19jID0gMTAwMCAgIyBrZy9twrMKQ3BfYyA9IDQxODAgICAjIEova2fCt0sgKGNvb2xhbnQpClUgPSAxMDAgICAgICAgIyBXL23CssK3SwoKIyBSZWFjdG9yIHBhcmFtZXRlcnMKdjBfdG90YWwgPSAxMDAvNjAgICAgIyBtXjMvcwpjYTAgPSAyICAgICAgICAgICAgICAjIG1vbC9tXjMKbl90dWJlcyA9IDI1CmRfdHViZSA9IDIwIC8gMTAwLjAgICMgbQpsX3R1YmUgPSAxMCAgICAgICAgICAjIG0KdjAgPSB2MF90b3RhbCAvIG5fdHViZXMgICMgcGVyIHR1YmUKCiMgVHViZSBnZW9tZXRyeQpBX3R1YmUgPSBucC5waSAqIChkX3R1YmUgLyAyKSoqMiAgIyBtwrIKVl90dWJlID0gQV90dWJlICogbF90dWJlICAjIG3CswpBX3N1cmYgPSBucC5waSAqIGRfdHViZSAqIGxfdHViZSAgIyBoZWF0IHRyYW5zZmVyIGFyZWEgcGVyIHR1YmUgKG3CsikKCiMgQ29vbGFudCBmbG93IHJhdGUgKGtnL3MpCm1fZG90X2MgPSAzLjIgLzYwCm1fZG90X2MgPSBtX2RvdF9jIC8gbl90dWJlcyAjIHBlciB0dWJlCgojIEluaXRpYWwgY29uZGl0aW9uczogW0NfQSwgQ19ELCBDX1UxLCBDX1UyLCBULCBUX2Nvb2xhbnRdCnkwID0gW2NhMCwgMCwgMCwgMCwgVDAsIFRfYzBdCgojIFN5c3RlbSBvZiBPREVzCmRlZiBvZGVzKFYsIHkpOgoKICAgIENfQSwgQ19ELCBDX1UxLCBDX1UyLCBULCBUX2MgPSB5CgogICAgazEgPSBrX2Fycmhlbml1cyhULCBrMF8xLCBFYTEpCiAgICBrMiA9IGtfYXJyaGVuaXVzKFQsIGswXzIsIEVhMikKICAgIGszID0ga19hcnJoZW5pdXMoVCwgazBfMywgRWEzKQoKICAgIHIxID0gazEgKiBDX0EqKjIKICAgIHIyID0gazIgKiBDX0EqKjEuMgogICAgcjMgPSBrMyAqIENfRAoKICAgICMgVE9ETzogV3JpdGUgY29ycmVjdCBleHByZXNzaW9ucyBmb3IgCiAgICAjIHJhdGVzIG9mIGluZGl2aWR1YWwgc3BlY2llcwogICAgcl9BID0gLXIxIC1yMgogICAgcl9EID0gMC41KnIxIC1yMwogICAgcl9VMSA9IHIyCiAgICByX1UyID0gcjMKCiAgICAjIFRPRE86IFdyaXRlIGNvcnJlY3QgZXhwcmVzc2lvbnMgZm9yIAogICAgIyBNb2xlIGJhbGFuY2UKICAgIGRDX0FfZFYgPSByX0EvdjAKICAgIGRDX0RfZFYgPSByX0QvdjAKICAgIGRDX1UxX2RWID0gcl9VMS92MAogICAgZENfVTJfZFYgPSByX1UyL3YwCgogICAgIyBUT0RPOiBXcml0ZSBjb3JyZWN0IGV4cHJlc3Npb25zIGZvciAKICAgICMgRW5lcmd5IGJhbGFuY2UKICAgIFFfZ2VuID0gKC1yMSpkSDEpICsgKC1yMipkSDIpICsgKHIzKmRIRCkgICMgSi9zCiAgICBRX3JlbW92YWwgPSBVICogQV9zdXJmICogKFQtVF9jKSAgIyBXCgogICAgQ190b3RhbCA9IENfQSArIENfRCArIENfVTEgKyBDX1UyICsgMWUtNiAgIyBhdm9pZCB6ZXJvIGRpdmlzaW9uCiAgICBGX3RvdGFsID0gQ190b3RhbCAqIHYwICAjIG1vbC9zCgogICAgIyBUT0RPOiBXcml0ZSBjb3JyZWN0IGV4cHJlc3Npb25zIGZvciAKICAgICMgRW5lcmd5IGJhbGFuY2UgKHJlYWN0b3Igc2lkZSkgaW4gdGVybXMgb2YgUV9nZW4sIFFfcmVtb3ZhbCwgYW5kIEZfdG90YWwKICAgIGRUX2RWID0gKFFfZ2VuIC0gUV9yZW1vdmFsKSAvIChtX2RvdF9jICogQ3BfYykgIyBLL23CswoKICAgICMgVE9ETzogV3JpdGUgY29ycmVjdCBleHByZXNzaW9ucyBmb3IgCiAgICAjIEVuZXJneSBiYWxhbmNlIChjb29sYW50IHNpZGUpIGluIHRlcm1zIG9mIFFfcmVtb3ZhbCwgbV9kb3RfYywgQ3BfYwogICAgZFRfY19kViA9IFFfcmVtb3ZhbCAvIChtX2RvdF9jICogQ3BfYykgICMgSy9twrMKCiAgICByZXR1cm4gW2RDX0FfZFYsIGRDX0RfZFYsIGRDX1UxX2RWLCBkQ19VMl9kVixkVF9kVixkVF9jX2RWXQoKIyBTb2x2ZSBPREVzClZfc3BhbiA9ICgwLCBWX3R1YmUpClZfZXZhbCA9IG5wLmxpbnNwYWNlKFZfc3BhblswXSwgVl9zcGFuWzFdLCAxMDApCgpzb2x1dGlvbiA9IHNvbHZlX2l2cChvZGVzLCBWX3NwYW4sIHkwLCB0X2V2YWw9Vl9ldmFsLCBtZXRob2Q9J0JERicpCkNfQSwgQ19ELCBDX1UxLCBDX1UyLCBULCBUX2MgPSBzb2x1dGlvbi55Cg==