T = 150 + 273.15
TR = 25 + 273.15
hf_nh3 = -11020
hf_n2 = 0
hf_h2 = 0
# N2 + 3H2 -> 2 NH3
nu_n2 = -1
nu_h2 = -3
nu_nh3 = 2
# Getting the Cp values from Perry's handbook (@green2019)
cp_h2 = 6.992 # cal/mol H2 K
cp_n2 = 6.984 # cal/mol N2 K
cp_nh3 = 8.92 # cal/mol NH3 K
delta_h_tr = (nu_nh3 * hf_nh3) + (nu_n2 * hf_n2 + nu_h2 * hf_h2)
delta_cp = (nu_nh3 * cp_nh3) + (nu_n2 * cp_n2 + nu_h2 * cp_h2)
delta_h = delta_h_tr + delta_cp * (T - TR)
cal = 4.184 # J
delta_h_kj = cal * delta_h_tr + delta_cp * (T - TR)In class activity: Non-isothermal reactor design
Lecture notes for chemical reaction engineering
Heat of reaction
Calculate the heat of reaction for the synthesis of ammonia from hydrogen and nitrogen at 150 °C in kcal/mol of N2 reacted and also in kJ/mol of H2 reacted.
The enthalpy of formation of ammonia at 25 °C is
The enthalpy of formations at 25 °C
Heats of formation of pure components are 0.
Getting the values from Perry’s handbook (Green and Southard (2019))
= 6.992 ; = 6.984 ; = 8.92
= -22.04
at 423.15 K = -93.48
at 423.15 K = -31.16
Adiabatic Liquid-Phase Isomerization of Normal Butane
Normal butane, , is to be isomerized to isobutane in a plug-flow reactor. Isobutane is a valuable product that is used in the manufacture of gasoline additives. For example, isooctane can be further reacted to form iso-octane. The 2014 selling price of n-butane was $1.5/gal, while the trading price of isobutane was $1.75/gal.
This elementary reversible reaction is to be carried out adiabatically in the liquid phase under high pressure using essentially trace amounts of a liquid catalyst that gives a specific reaction rate of at . The feed enters at .
Calculate the PFR volume necessary to process () at conversion of a mixture -butane and -pentane, which is considered an inert.
Plot and analyze , , , and down the length of the reactor.
Calculate the CSTR volume for conversion.
Additional information:
-butane,
Activation energy =
at ,
Butane: ;
i-Pentane:
PFR algorithm
- Mole balance
- Rate law
- Stoichiometry
- Energy balance
From problem statement:
Adiabatic: = 0
No work: = 0
= 141 - 141 = 0
Substituting in Equation 7
- Solve
Solve equations 1 to 7 simultaneously.
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
# Constants
K0 = 31.1 # 1/h
T_K0 = 360 # K
KC0 = 3.3
T_KC0 = 60 + 273.15 # K
E = 65700 # J/mol
R = 8.314 # J/mol K
DELTAHR = -6900 # J/mol
def adiabatic_pfr( V, y, *args ):
X = y[0]
(fa0,ca0, T0, theta, cp) = args
sum_cp = np.sum(theta * cp)
T = T0 + (-DELTAHR) * X / sum_cp
k = K0 * np.exp((E/R) * (1/T_K0 - 1/T))
Kc = KC0 * np.exp((DELTAHR/R) * (1/T_KC0 - 1/T))
ca = ca0 * ( 1 - X )
cb = ca0 * X
rate = k * (ca - cb/Kc)
dXdV = rate/fa0
return dXdV
# Problem data
ya0 = 0.9
yi0 = 0.1
fa = 163.0 # kmol/h
fa0 = ya0 * fa
ca0 = 9.3 # kmol/m3
T0 = 330 # K
# n-butane = comp 1; i-butane = comp 2; i-Pentane = component 3
theta = np.array([1, 0, yi0/ya0])
cp = np.array([141, 141, 161])
# Initial condition
y0 = [0]
args = (fa0, ca0, T0, theta, cp)
v_final = 5
sol = solve_ivp(adiabatic_pfr, [0, v_final], y0, args=args, dense_output=True)
v = np.linspace(0,v_final, 1000)
x = sol.sol(v)[0]
pfr_07 = v[np.argmax(x > 0.7)]PFR volume required to obtain a conversion of 0.7 = 2.24 .
# recalculate other quantities for plotting
sum_cp = np.sum(theta * cp)
T = T0 + (-DELTAHR) * x / sum_cp
Kc = KC0 * np.exp((DELTAHR/R) * (1/T_KC0 - 1/T))
xe = Kc/(1 + Kc)Temperature profile
Conversion profile
CSTR volume
pfr_04 = v[np.argmax(x > 0.4)]
temp_04 = T[np.argmax(x > 0.4)]
kc_04 = Kc[np.argmax(x > 0.4)]
k_04 = K0 * np.exp((E/R) * (1/T_K0 - 1/temp_04))
ca = ca0 * ( 1 - 0.4 )
cb = ca0 * 0.4
rate = k_04 * (ca - cb/kc_04)
cstr_04 = fa0 * 0.4/ ratePFR volume required to obtain a conversion of 0.4 = 1.14 .
CSTR volume required to obtain a conversion of 0.4 = 0.97 .
Adiabatic Equilibrium Temperature
For the elementary liquid-phase reaction
Make a plot of equilibrium conversion as a function of temperature.
Combine the rate law and stoichiometric table to write as a function of , , , and .
Determine the adiabatic equilibrium temperature and conversion when pure A is fed to the reactor at a temperature of 300 K.
What is the CSTR volume to achieve 90% of the adiabatic equilibrium conversion for ?
Additional information:†
at 298 K, with
- Rate law
- Equilibrium
- Stoichiometry
Combining Equation 9, Equation 12, Equation 13, and Equation 14
- Energy balance
From problem statement:
Adiabatic: = 0
No work: = 0
= 141 - 141 = 0
Substituting in Equation 16
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
from scipy.optimize import fsolve
# Constants
HF_A = -40000 # cal/mol
HF_B = -60000 # cal/mol
CP_A = 50 # cal/mol K
CP_B = 50 # cal/mol K
# A <-> B
D_CP = CP_B - CP_A
KE0 = 100000
T_KE0 = 298 # K
K0 = 1e-3 # 1/h
T_K0 = 298 # K
E = 10000 # cal/mol
R = 1.987 # cal/mol K
DELTAHR = HF_B - HF_A
T0 = 300 # K
Ke = lambda T: KE0 * np.exp((DELTAHR/R) * (1/T_KE0 - 1/T))
Xe = lambda T: Ke(T)/(1 + Ke(T))
Xeb = lambda T, T0: CP_A * (T - T0)/(-DELTAHR)
intersect = lambda T: Xe(T) - Xeb(T, T0)
sol = fsolve(intersect, T0)
T_adiab = sol[0]
X_eadiab = Xe( T_adiab )Adiabatic equilibrium temperature = 460.40 K.
Adiabatic equilibrium conversion = 0.40.
T_final = T0 + 300
temperatures = np.linspace(T0, T_final , 100)
conv_eb = Xeb(temperatures, T0)
conv_eq = Xe(temperatures)
plt.plot(temperatures,conv_eb, label="$X_{eb}$")
plt.plot(temperatures,conv_eq, label="$X_{eq}$")
plt.xlabel('Temperature (K)')
plt.ylabel('Conversion')
plt.xlim(T0, T_final)
plt.ylim(0,1)
plt.legend()
plt.show()CSTR Volume
Calculate temperature corresponding to X = 0.9 Xe
Calculate rate at this temperature using Equation 9 and Equation 10.
k = lambda T: K0 * np.exp((E/R) * (1/T_K0 - 1/T))
v_0 = 5 # dm^3/min
x_cstr = 0.9 * X_eadiab
def v_cstr(x, xin, t0, ca0, v_0):
t_cstr = t0 + (x - xin)*(-DELTAHR)/CP_A
rate = k(t_cstr) * ca0 * (1 - (x/Xe(t_cstr)))
v_cstr = v_0 * ca0 * x/rate
return v_cstr,t_cstr
V_cstr,T_cstr = v_cstr(x_cstr,0,T0,1.0,v_0)For a CSTR to achieve a conversion of 90% of (= 0.36), volume required is 17.57 . The CSTR operates at 444.36 .
Interstage Cooling for Highly Exothermic Reactions
What conversion could be achieved in the previous example if two interstage coolers that had the capacity to cool the exit stream to 350 K were available? Also, determine the heat duty of each exchanger for a molar feed rate of A of 40 mol/s. Assume that 95% of the equilibrium conversion is achieved in each reactor. The feed temperature to the first reactor is 300 K.
# Stage 1
fa0 = 40.0 # mol/s
x1 = 0.95 * X_eadiab
v_cstr1, t_cstr1 = v_cstr(x1,0,T0,1.0,v_0)Stage 1: = 0.38. = 25.69 . = 452.38 .
Heat Load:
;
T_ic = 350
qdot1 = fa0 * CP_A * (T_ic - t_cstr1)Stage 1 cooling requirement : = -204.76 .
# Stage 2
# find adiabatic conversion and temperature for second stage
intersect = lambda T: Xe(T) - ( x1 + Xeb(T, T_ic))
sol = fsolve(intersect, T0)
T2_adiab = sol[0]
X2_eadiab = Xe( T2_adiab )
x2 = X2_eadiab * 0.95
v_cstr2, t_cstr2 = v_cstr(x2,x1,T_ic,1.0,v_0)
qdot2 = fa0 * CP_A * (T_ic - t_cstr2)Stage 2: = 0.58. = 442.93 . = 71.24 . = 430.66 .
Stage 2 cooling requirement : = -161.33 .
# Stage 3
# find adiabatic conversion and temperature for second stage
intersect = lambda T: Xe(T) - ( x2 + Xeb(T, T_ic))
sol = fsolve(intersect, T0)
T3_adiab = sol[0]
X3_eadiab = Xe( T3_adiab )
x3 = X3_eadiab * 0.95
v_cstr3, t_cstr3 = v_cstr(x3,x2,T_ic,1.0,v_0)
qdot3 = fa0 * CP_A * (T_ic - t_cstr3)Stage 3: = 0.74. = 428.03 . = 195.40 . = 412.47 .
Stage 3 cooling requirement : = -124.95 .
Production of Acetic Anhydride
Jeffreys, in a treatment of the design of an acetic anhydride manufacturing facility, states that one of the key steps is the endothermic vapor-phase cracking of acetone to ketene and methane.
The reaction is as follows:
He states further that this reaction is first-order with respect to acetone and that the specific reaction rate can be expressed by
where is in reciprocal seconds and is in Kelvin. In this design it is desired to feed 7850 kg of acetone per hour to a tubular reactor. The reactor consists of a bank of 1000 one-inch schedule 40 tubes. We shall consider four cases of heat exchanger operation. The inlet temperature and pressure are the same for all cases at 1035 K and 162 kPa (1.6 atm) and the entering heating-fluid temperature available is 1250 K.
A bank of 1000 one-inch, schedule 40 tubes 1.79 m in length corresponds to 1.0 m³ (0.001 m³/tube = 1.0 dm³/tube) and gives 20% conversion. Ketene is unstable and tends to explode, which is a good reason to keep the conversion low. However, the pipe material and schedule size should be checked to learn if they are suitable for these temperatures and pressures. The heat-exchange fluid has a flow rate, , of 0.111 mol/s, with a heat capacity of 34.5 J/mol·K.
The cases are as follows:
- Case 1: The reactor is operated adiabatically.
- Case 2: Constant heat-exchange fluid temperature K.
- Case 3: Co-current heat exchange with K.
- Case 4: Countercurrent heat exchange with K.
Additional information:
- For acetone (A): kJ/mol, J/mol·K
- For ketene (B): kJ/mol, J/mol·K
- For methane (C): kJ/mol, J/mol·K
The overall heat transfer coefficient .
Reaction:
- Mole balance
- Rate law
Stoichiometry
Energy balance
Reactor balance
Heat exchanger
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
# Constants
R = 8.314 # J/mol K
TR = 298 # K
# Components: 1: A(acetone), 2: B(ketene), 3: C(methane)
HF = np.array ([-216.67, -61.09, -74.81])*1000 # J/mol
CP = np.array ([163.0, 83.0, 71.0]) # J/mol K
MW = np.array ([58.0, 42.0, 20.0]) # g/mol
k = lambda t: np.exp( 34.34 - 34222/t )
# k = lambda t: 3.58 * np.exp( 34222 * (1/1035 - 1/t))
def pfr (v, x, *args):
X, T, Ta = x
(fa0, ca0, T0, epsilon, delta_hr_tr, delta_cp, theta, UA, mc, cpc) = args
ca = ca0 * (( 1 - X ) / ( 1 + epsilon * X )) * (T0/T)
rate = k(T) * ca # -r_A
delta_h = delta_hr_tr + delta_cp * (T - TR)
dxdv = rate/fa0
dtdv = (UA * ( Ta - T ) + (-rate * delta_h))/( fa0 * ( np.sum(theta * CP) + X * delta_cp) )
dtadv = UA * ( T - Ta )/ (mc * cpc)
return [dxdv, dtdv, dtadv]
# Data
nu = np.array ([-1.0, 1.0, 1.0]) # stoichiometric coefficients
f0 = 7850 # kg/h
T0 = 1035.0 # K
P0 = 162.0e3 # Pa
n_tubes = 1000
d_tube = 26.64e-3 # 1" schedule 40 pipe inner diameter mm
Ta0 = 1250 # K
a = 4/d_tube
U = 110.0 # J/s m^3 K
UA = U * a
mc = 0.111 # mol/s
cpc = 34.5 # J/mol K
# inlet mole fraction
y0 = np.array ([1.0, 0.0, 0.0])
theta = np.array([1.0, 0.0, 0.0])
fa0 = (f0 / ( y0[0] * MW[0])) / n_tubes # kmol/h
fa0 = fa0 * (1000/3600) # mol/s
pa0 = y0[0] * P0
ca0 = pa0/(R * T0)
v_0 = fa0/ca0
ya0 = y0[0]
# Heat of reaction at 298K
delta_hr_tr = np.sum(HF * nu)
delta_cp = np.sum(CP * nu)
epsilon = ya0 * np.sum(nu)| Parameter values | ||
|---|---|---|
| = 80770.00 | = -9.00 | = 1035.00 K |
| = 3.76e-02 mol/s | = 18.83 | = 298.00 K |
| = 163.00 | = 16516.52 | = 0.11 |
| = 34.50 | = 0.001 |
args = (fa0, ca0, T0, epsilon, delta_hr_tr, delta_cp, theta, 0, mc, cpc)
initial_conditions = np.array([0, T0, Ta0])
v_final = 0.005
sol = solve_ivp(pfr, [0, v_final], initial_conditions, args=args, dense_output=True)
v = np.linspace(0,v_final, 1000)
x = sol.sol(v)[0]
T = sol.sol(v)[1]
Ta = sol.sol(v)[2]
fig, ax1 = plt.subplots()
# Plot x on the original y-axis
plt_x = ax1.plot(v, x, color='#105e5d', label='X')
ax1.set_xlabel('volume ($m^3$)')
ax1.set_ylabel('Conversion')
ax1.set_xlim(0,v[-1])
ax1.set_ylim(0,)
# Create a second y-axis for T and Ta
ax2 = ax1.twinx()
plt_t = ax2.plot(v, T, label='T')
# plt_ta = ax2.plot(v, Ta, label='Ta')
ax2.set_ylabel('Temperature (K)')
ax2.set_xlim(0,v[-1])
ax2.set_ylim(top=T0)
arrow_properties = dict(arrowstyle="<-,head_length=0.7,head_width=0.25",
color="black")
ax1.annotate('', xy=(v[100],x[100]), xytext=(v[0], x[100]),arrowprops=dict(arrowstyle="<-"))
ax2.annotate('', xy=(v[100],T[100]), xytext=(v[200], T[100]),arrowprops=dict(arrowstyle="<-"))
# fig.tight_layout()
handles, labels = [], []
for ax in [ax1, ax2]:
for handle, label in zip(*ax.get_legend_handles_labels()):
handles.append(handle)
labels.append(label)
plt.legend(handles, labels)
plt.show()def pfr_const_heatex_fluid_T (v, x, *args):
X, T, Ta = x
(fa0, ca0, T0, epsilon, delta_hr_tr, delta_cp, theta, UA, mc, cpc) = args
ca = ca0 * (( 1 - X ) / ( 1 + epsilon * X )) * (T0/T)
rate = k(T) * ca # -r_A
delta_h = delta_hr_tr + delta_cp * (T - TR)
dxdv = rate/fa0
dtdv = (UA * ( Ta - T ) + (-rate * delta_h))/( fa0 * ( np.sum(theta * CP) + X * delta_cp) )
dtadv = 0.0
return [dxdv, dtdv, dtadv]
args = (fa0, ca0, T0, epsilon, delta_hr_tr, delta_cp, theta, UA, mc, cpc)
initial_conditions = np.array([0, T0, Ta0])
v_final = 0.001
sol = solve_ivp(pfr_const_heatex_fluid_T, [0, v_final], initial_conditions, args=args, dense_output=True)
v = np.linspace(0,v_final, 1000)
x = sol.sol(v)[0]
T = sol.sol(v)[1]
Ta = sol.sol(v)[2]
fig, ax1 = plt.subplots()
# Plot x on the original y-axis
plt_x = ax1.plot(v, x, color='#105e5d', label='X')
ax1.set_xlabel('volume ($m^3$)')
ax1.set_ylabel('Conversion')
ax1.set_xlim(0,v[-1])
ax1.set_ylim(0,)
# Create a second y-axis for T and Ta
ax2 = ax1.twinx()
plt_t = ax2.plot(v, T, label='T')
# plt_ta = ax2.plot(v, Ta, label='Ta')
ax2.set_ylabel('Temperature (K)')
ax2.set_xlim(0,v[-1])
arrow_properties = dict(arrowstyle="<-,head_length=0.7,head_width=0.25",
color="black")
ax1.annotate('', xy=(v[100],x[100]), xytext=(v[0], x[100]),arrowprops=dict(arrowstyle="<-"))
ax2.annotate('', xy=(v[100],T[100]), xytext=(v[200], T[100]),arrowprops=dict(arrowstyle="<-"))
# fig.tight_layout()
handles, labels = [], []
for ax in [ax1, ax2]:
for handle, label in zip(*ax.get_legend_handles_labels()):
handles.append(handle)
labels.append(label)
plt.legend(handles, labels)
plt.show()args = (fa0, ca0, T0, epsilon, delta_hr_tr, delta_cp, theta, UA, mc, cpc)
initial_conditions = np.array([0, T0, Ta0])
v_final = 0.001
sol = solve_ivp(pfr, [0, v_final], initial_conditions, args=args, dense_output=True)
v = np.linspace(0,v_final, 1000)
x = sol.sol(v)[0]
T = sol.sol(v)[1]
Ta = sol.sol(v)[2]
fig, ax1 = plt.subplots()
# Plot x on the original y-axis
plt_x = ax1.plot(v, x, color='#105e5d', label='X')
ax1.set_xlabel('volume ($m^3$)')
ax1.set_ylabel('Conversion')
ax1.set_xlim(0,v[-1])
ax1.set_ylim(0,)
# Create a second y-axis for T and Ta
ax2 = ax1.twinx()
plt_t = ax2.plot(v, T, label='T')
plt_ta = ax2.plot(v, Ta, label='Ta')
ax2.set_ylabel('Temperature (K)')
ax2.set_xlim(0,v[-1])
ax2.set_ylim(top=Ta0)
arrow_properties = dict(arrowstyle="<-,head_length=0.7,head_width=0.25",
color="black")
ax1.annotate('', xy=(v[100],x[100]), xytext=(v[0], x[100]),arrowprops=dict(arrowstyle="<-"))
ax2.annotate('', xy=(v[100],T[100]), xytext=(v[200], T[100]),arrowprops=dict(arrowstyle="<-"))
ax2.annotate('', xy=(v[100],Ta[100]), xytext=(v[200], Ta[100]),arrowprops=dict(arrowstyle="<-"))
# fig.tight_layout()
handles, labels = [], []
for ax in [ax1, ax2]:
for handle, label in zip(*ax.get_legend_handles_labels()):
handles.append(handle)
labels.append(label)
plt.legend(handles, labels)
plt.show()def pfr_countercurrent_exchange (v, x, *args):
X, T, Ta = x
(fa0, ca0, T0, epsilon, delta_hr_tr, delta_cp, theta, UA, mc, cpc) = args
ca = ca0 * (( 1 - X ) / ( 1 + epsilon * X )) * (T0/T)
rate = k(T) * ca # -r_A
delta_h = delta_hr_tr + delta_cp * (T - TR)
dxdv = rate/fa0
dtdv = (UA * ( Ta - T ) + (-rate * delta_h))/( fa0 * ( np.sum(theta * CP) + X * delta_cp) )
dtadv = - UA * ( T - Ta )/ (mc * cpc)
return [dxdv, dtdv, dtadv]
v_final = 0.001
# This temperature needs to be guessed for Ta to be 1250 at V = V_final
Ta0 = 995.15
args = (fa0, ca0, T0, epsilon, delta_hr_tr, delta_cp, theta, UA, mc, cpc)
initial_conditions = np.array([0, T0, Ta0])
sol = solve_ivp(pfr_countercurrent_exchange, [0, v_final], initial_conditions, args=args, dense_output=True)
v = np.linspace(0,v_final, 1000)
x = sol.sol(v)[0]
T = sol.sol(v)[1]
Ta = sol.sol(v)[2]
fig, ax1 = plt.subplots()
# Plot x on the original y-axis
plt_x = ax1.plot(v, x, color='#105e5d', label='X')
ax1.set_xlabel('volume ($m^3$)')
ax1.set_ylabel('Conversion')
ax1.set_xlim(0,v[-1])
ax1.set_ylim(0,)
# Create a second y-axis for T and Ta
ax2 = ax1.twinx()
plt_t = ax2.plot(v, T, label='T')
plt_ta = ax2.plot(v, Ta, label='Ta')
ax2.set_ylabel('Temperature (K)')
ax2.set_xlim(0,v[-1])
arrow_properties = dict(arrowstyle="<-,head_length=0.7,head_width=0.25",
color="black")
ax1.annotate('', xy=(v[200],x[200]), xytext=(v[100], x[200]),arrowprops=dict(arrowstyle="<-"))
ax2.annotate('', xy=(v[100],T[100]), xytext=(v[200], T[100]),arrowprops=dict(arrowstyle="<-"))
ax2.annotate('', xy=(v[400],Ta[400]), xytext=(v[500], Ta[400]),arrowprops=dict(arrowstyle="<-"))
# fig.tight_layout()
handles, labels = [], []
for ax in [ax1, ax2]:
for handle, label in zip(*ax.get_legend_handles_labels()):
handles.append(handle)
labels.append(label)
plt.legend(handles, labels)
plt.show()References
Citation
@online{utikar2024,
author = {Utikar, Ranjeet},
title = {In Class Activity: {Non-isothermal} Reactor Design},
date = {2024-04-01},
url = {https://cre.smilelab.dev/content/notes/07-non-isothermal-reactor-design/in-class-activities.html},
langid = {en}
}



