import numpy as np
from scipy.stats import linregress
import matplotlib.pyplot as plt
# Data
= 5 # cm
delta_Z = 2.33e-6 # mol/cm^3
C_A0 = 19.6 # cm^3/s
v_0 = 0.158 # cm
diameter
= np.pi * diameter**2/4
Ac = C_A0 * v_0
F_A0
= np.array([1, 2, 3, 4, 5, 6, 7])
electrode_positions = np.array([0, 1.93, 3.82, 5.68, 7.48, 9.25, 11.00])
percent_decomposition
= (electrode_positions - 1) *delta_Z
Z = percent_decomposition/100
X
# fit polynomial to the data
= np.polyfit(Z, X, deg=2)
coefficients = np.poly1d(coefficients)
polynomial
= np.linspace(Z.min(), Z.max(), 100)
z_fit = polynomial(z_fit)
x_fit
'o', label='Experimental data')
plt.plot(Z, X, '-', label='Polynomial fit')
plt.plot(z_fit, x_fit , 'Length (cm)')
plt.xlabel('Decomposition')
plt.ylabel('Decomposition of $HbO_2$ vs. Length')
plt.title(
plt.legend()True)
plt.grid(
plt.show()
Solutions to workshop 05: Collection and analysis of rate data
Lecture notes for chemical reaction engineering
Try following problems from Fogler 5e (Fogler 2016). P 7-4, P 7-5, P 7-6, P 7-7, P 7-10
We will go through some of these problems in the workshop.
P 7-4
When arterial blood enters a tissue capillary, it exchanges oxygen and carbon dioxide with its environment, as shown in this diagram.
The kinetics of this deoxygenation of hemoglobin in blood was studied with the aid of a tubular reactor by Nakamura and Staub (J. Physiol., 173, 161).
\ce{HbO2 <=>[ $k_1$ ][ $k_{-1}$ ] Hb + O2}
Although this is a reversible reaction, measurements were made in the initial phases of the decomposition so that the reverse reaction could be neglected. Consider a system similar to the one used by Nakamura and Staub: the solution enters a tubular reactor (0.158 cm in diameter) that has oxygen electrodes placed at 5-cm intervals down the tube. The solution flow rate into the reactor is 19.6 cm3/s with CA0 = 2.33 × 10–6 mol/cm3.
Electrode position | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Percent decomposition of \ce{HbO2} | 0.00 | 1.93 | 3.82 | 5.68 | 7.48 | 9.25 | 11.00 |
Using the method of differential analysis of rate data, determine the reaction order and the forward specific reaction-rate constant k1 for the deoxygenation of hemoglobin.
Repeat using regression.
- Reaction is reversible, but the measurements were taken in the initial phases where the reverse reaction can be neglected
\ce{HbO2 ->[k] Hb + O2}
- % decomposition (conversion) along the length of PFR is given
Rate law: -r_A = k C_A^\alpha = k C_{A0}^\alpha (1 - X)^\alpha
For PFR: F_{A0}\frac{dX}{dV} = k C_{A0}^\alpha (1 - X)^\alpha
V = A_c Z; \Rightarrow dV = A_c dZ
\frac{dX}{dZ} = \underbrace{ \frac{k C_{A0}^\alpha A_c}{F_{A0}} }_a (1 - X)^\alpha
\frac{dX}{dZ} = a (1 - X)^\alpha
Taking log
\ln \frac{dX}{dZ} = \ln a + \alpha \ln (1 - X)
A plot of \ln \frac{dX}{dZ} vs \ln (1 - X) has a slope of \alpha
# calculate the derivative of the polynomial
= np.polyder(polynomial)
dxdz
# calculate ln(1-X) and ln(dxdz) --> ideally should do this with fitted data
# ln_1_minus_x = np.log(1-x_fit)
# ln_dxdz = np.log(dxdz(z_fit))
= np.log(1-X)
ln_1_minus_x = np.log(dxdz(Z))
ln_dxdz
# fit line
= linregress(ln_1_minus_x, ln_dxdz)
res = res.slope * ln_1_minus_x + res.intercept
line
'o', label='Differential analysis')
plt.plot(ln_1_minus_x, ln_dxdz, '-', label='fitted line')
plt.plot(ln_1_minus_x, line,
plt.annotate(f'Slope = {res.slope:.2e}\nIntercept = {res.intercept:.2f}\n$R^2$ = {res.rvalue**2:.3f}',
=(0.5, 0.15),
xy='axes fraction',
xycoords=12
fontsize
)
'$\ln (1-X)$')
plt.xlabel('$\ln(\\frac{dX}{dZ})$')
plt.ylabel(
plt.legend()True)
plt.grid(
plt.show()
# pick the closest round order
= round(res.slope)
order = np.exp(res.intercept)
a = a * F_A0 / (C_A0**order * Ac) k
Reaction order is = 1, and k is 3.899 1/s.
P 7-5
The liquid-phase irreversible reaction
\ce{A -> B + C} is carried out in a CSTR. To learn the rate law, the volumetric flow rate, v_0 , (hence \tau = V / v_0 ) is varied and the effluent concentrations of species A are recorded as a function of the space time t. Pure A enters the reactor at a concentration of 2 mol/ dm3. Steady-state conditions exist when the measurements are recorded.
Run | 1 | 2 | 3 | 4 | 5 |
\tau (min) | 15 | 38 | 100 | 300 | 1200 |
CA (mol/dm3) | 1.5 | 1.25 | 1.0 | 0.75 | 0.5 |
Determine the reaction order and specific reaction-rate constant.
If you were to repeat this experiment to determine the kinetics, what would you do differently? Would you run at a higher, lower, or the same temperature? If you were to take more data, where would you place the measurements (e.g., \tau )?
It is believed that the technician may have made a dilution factor-of-10 error in one of the concentration measurements. What do you think? How do your answers compare using regression (Polymath or other software) with those obtained by graphical methods?
Note: All measurements were taken at steady-state conditions.
- The liquid-phase irreversible reaction
\ce{A -> B + C}
carried out in a CSTR.
\tau vs. C_A data is given
Rate law: -r_A = k C_A^\alpha
For CSTR,
V = \frac{\upsilon_0 (C_{A0} - C_A)}{-r_A} \Rightarrow \tau = \frac{(C_{A0} - C_A)}{k C_A^\alpha}
\therefore k C_A^\alpha = \frac{(C_{A0} - C_A)}{\tau}
Taking log \ln k + \alpha \ln C_A = \ln \left(\frac{C_{A0} - C_A}{\tau} \right)
import numpy as np
from scipy.stats import linregress
import matplotlib.pyplot as plt
# Data
= 2.0 # mol/dm^3
C_A0
= np.array([15, 38, 100, 300, 1200]) # space time in minutes
tau = np.array([1.5, 1.25, 1.0, 0.75, 0.5]) # concentration in mol/dm^3
C_A
# plot the data
'o-', label='concentration')
plt.plot(tau, C_A, '$\\tau$')
plt.xlabel('$C_A$')
plt.ylabel(
plt.legend()True)
plt.grid(0,1200)
plt.xlim(
plt.show()
= np.log(C_A)
ln_ca = np.log((C_A0 - C_A)/ tau)
ln_ca_by_tau
# fit line
= linregress(ln_ca, ln_ca_by_tau)
res = res.slope * ln_ca + res.intercept
line
'o', label='Differential analysis')
plt.plot(ln_ca, ln_ca_by_tau, '-', label='fitted line')
plt.plot(ln_ca, line,
plt.annotate(f'Slope = {res.slope:.2e}\nIntercept = {res.intercept:.2f}\n$R^2$ = {res.rvalue**2:.3f}',
=(0.5, 0.15),
xy='axes fraction',
xycoords=12
fontsize
)
'$\ln (C_A)$')
plt.xlabel('$\ln(\\frac{C_{A0}-C_A}{\\tau})$')
plt.ylabel(
plt.legend()True)
plt.grid(
plt.show()
# pick the closest round order
= round(res.slope)
order = np.exp(res.intercept) k
Reaction order is = 3, and k is 9.971e-03 1/s.
P 7-6
The reaction
\ce{ A -> B + C }
was carried out in a constant-volume batch reactor where the following concentration measurements were recorded as a function of time.
t (min) | 0 | 5 | 9 | 15 | 22 | 30 | 40 | 60 |
CA (mol/dm3) | 2 | 1.6 | 1.35 | 1.1 | 0.87 | 0.70 | 0.53 | 0.35 |
Use nonlinear least squares (i.e., regression) and one other method to determine the reaction order, \alpha, and the specific reaction rate, k.
Nicolas Bellini wants to know, if you were to take more data, where would you place the points? Why?
Prof. Dr. Sven Köttlov from Jofostan University always asks his students, if you were to repeat this experiment to determine the kinetics, what would you do differently? Would you run at a higher, lower, or the same temperature? Take different data points? Explain.
It is believed that the technician made a dilution error in the concentration measured at 60 min.
What do you think? How do your answers compare using regression (Polymath or other software) with those obtained by graphical methods?
Reaction: \ce{ A -> B + C }
- carried out in a constant-volume batch reactor
-\frac{dC_A}{dt} = k C_A^\alpha
- concentration measurements were recorded as a function of time.
import numpy as np
from scipy.integrate import solve_ivp
from scipy.optimize import minimize
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
# Given data
= np.array([0, 5, 9, 15, 22, 30, 40, 60]) # min
t = np.array([2, 1.6, 1.35, 1.1, 0.87, 0.7, 0.53, 0.35]) # mol/dm^3
C_A
= C_A[0]
C_A0
# Define the system of ODEs
def batch_reactor(t, y, *args):
= y[0]
C_A = args
k, n
= -k*C_A**n
rate = rate
dca_dt
return [dca_dt]
= (t.min(), t.max())
t_span
# initial conditions
= [C_A0]
y0
# Initial guesses of k and n
= 1
k = 1
n
# Objective function to minimize: the difference between C_A (experimental) and C_A (model)
def objective(params):
= params
k,n
# Solve the ODE using solve_ivp
= solve_ivp(
solution # The ODE function to solve
batch_reactor, # Time interval
t_span, # Initial condition in a list
y0, =(k, n), # Additional arguments passed to the ODE function
args=True # Generate a continuous solution
dense_output
)
= solution.sol(t)
sol_specific = solution.sol(t)[0]
C_A_model
= np.sum((C_A - C_A_model)**2) # Sum of squared residuals
ssr
return ssr
# Minimize the objective function
= minimize(objective, [k,n], bounds=[(1e-4, 1e4), (0, 5)])
result
# Extract the results
= result.x
k_opt, n_opt = result.success
success
# Check if the solution was successful
if not success:
print("Optimization was not successful. Try different initial guesses or methods.")
= solve_ivp(
final_solution # The ODE function to solve
batch_reactor, # Time interval
t_span, # Initial condition in a list
y0, =(k_opt, n_opt), # Additional arguments passed to the ODE function
args=True # Generate a continuous solution
dense_output
)
= final_solution.sol(t)[0]
C_A_model
# plot the data
'o', label='Experiment')
plt.plot(t, C_A, '-', label='model')
plt.plot(t, C_A_model, '$t (min)$')
plt.xlabel('$C_A (mol/dm^3)$')
plt.ylabel(
plt.legend()True)
plt.grid(0,60)
plt.xlim(
plt.show()
Initial guess for Reaction order is = 1.00, and k is 1.000e+00.
Optimized value of Reaction order is = 1.52, and k is 3.305e-02.
P 7-7
The following data were reported [from C. N. Hinshelwood and P. J. Ackey, Proc. R. Soc. (Lond)., A115, 215] for a gas-phase constant-volume decomposition of dimethyl ether at 504^{\circ}C in a batch reactor. Initially, only \ce{(CH3)2O} was present.
Time (s) | 390 | 777 | 1195 | 3155 | \infty |
Total Pressure (mmHg) | 408 | 488 | 562 | 799 | 931 |
Why do you think the total pressure measurement at t = 0 is missing? Can you estimate it?
Assuming that the reaction
\ce{(CH3)2O -> CH4 + H2 + CO}
is irreversible and goes virtually to completion, determine the reaction order and specific reaction rate k.
What experimental conditions would you suggest if you were to obtain more data?
How would the data and your answers change if the reaction were run at a higher temperature? A lower temperature?
constant volume batch reactor
Data on pressure with time is given
\ce{(CH3)2O -> CH4 + H2 + CO}
y_{A0} = 1
\delta = 3 - 1 = 2
\epsilon = y_{A0} \delta = 2
V = V_0 = V_0 \frac{P_0}{P} (1 + \epsilon X)
P = P_0 (1 + \epsilon X)
X = \frac{P - P_0}{\epsilon P_0}
At t = \infty, X = 1
P_\infty = P_0 (1 + 2) 3 P_0
P_0 = P_\infty/3
Rate law: -r_A = k C_A^\alpha
As pressure data is given, we need an equation for dP/dt
dP/dt = d/dt (P_0 (1 + \epsilon X))
dP/dt = P_0 \epsilon dX/dt
- For constant volume batch reactor
\frac{dC_A}{dt} = -k C_A^\alpha
C_A = C_{A0}^\alpha (1-X)^\alpha
\frac{dX}{dt} = k (1-X)^\alpha
import numpy as np
from scipy.integrate import solve_ivp
from scipy.optimize import minimize
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
# Given data
= 2
delta = 1
y_A0 = y_A0 * delta
epsilon
= np.array([390, 777, 1195, 3155, 10000]) # s, Replace infinity with a large number
t = np.array([408, 488, 562, 799, 931]) # mmHg
P
= P[-1]/(1 + epsilon)
P0
= t[:-1]
t_eval = P[:-1]
P_eval
# Define the system of ODEs
def batch_reactor(t, y, *args):
= y[0]
p = args
k, n
= (p-P0)/(epsilon * P0)
X
= k * (1-X)**n
dxdt = P0 * epsilon* dxdt
dpdt
return [dpdt]
= (min(t_eval), max(t_eval))
t_span
# initial conditions
= [P0]
y0
# Initial guesses of k and n
= 6e-4
k = 1
n
# Objective function to minimize: the difference between C_A (experimental) and C_A (model)
def objective(params):
= params
k,n
# Solve the ODE using solve_ivp
= solve_ivp(
solution # The ODE function to solve
batch_reactor, # Time interval
t_span, # Initial condition in a list
y0, =(k, n), # Additional arguments passed to the ODE function
args=True # Generate a continuous solution
dense_output
)
= solution.sol(t_eval)[0]
P_model
= np.sum((P_eval - P_model)**2) # Sum of squared residuals
ssr
return ssr
# Minimize the objective function
= minimize(objective, [k,n], bounds=[(1e-4, 1e-3), (0, 2)])
result
# Extract the results
= result.x
k_opt, n_opt = result.success
success
# Check if the solution was successful
if not success:
print("Optimization was not successful. Try different initial guesses or methods.")
= (min(t), max(t))
t_span
= solve_ivp(
final_solution # The ODE function to solve
batch_reactor, # Time interval
t_span, # Initial condition in a list
y0, =(k_opt, n_opt), # Additional arguments passed to the ODE function
args=True # Generate a continuous solution
dense_output
)
= np.linspace(t.min(), t.max(),100)
t_plot = final_solution.sol(t_plot)[0]
P_model
# plot the data
'o', label='Experiment')
plt.plot(t, P, '-', label='model')
plt.plot(t_plot, P_model, '$t (s)$')
plt.xlabel('P (mmHg)')
plt.ylabel(
plt.legend()True)
plt.grid(
plt.show()
Initial guess for Reaction order is = 1.00, and k is 6.000e-04.
Optimized value of Reaction order is = 1.50, and k is 8.184e-04.
P 7-10
Tests were run on a small experimental reactor used for decomposing nitrogen oxides in an automobile exhaust stream. In one series of tests, a nitrogen stream containing various concentrations of \ce{NO2} was fed to a reactor, and the kinetic data obtained are shown in Figure P7-10. Each point represents one complete run. The reactor operates essentially as an isothermal backmix reactor (CSTR). What can you deduce about the apparent order of the reaction over the temperature range studied?
The plot gives the fractional decomposition of \ce{NO2} fed versus the ratio of reactor volume V (in cm3) to the NO2 feed rate, F_{NO_{2,0}} (g mol/h), at different feed concentrations of \ce{NO2} (in parts per million by weight). Determine as many rate law parameters as you can.
X = \frac{\text{\% decomposition}}{100}
Rate law: -r_A = kC_A^n
V/F_{A0} vs. X data is given
Reaction carried out in CSTR
V = \frac{F_{A0}X}{-r_A}
\frac{V}{F_{A0}} = \frac{X}{k C_A^n}
The linear nature of experiment at a given temperature in Figure 1 indicates that X \propto \frac{V}{F_{A0}}
Therefore, apparent order must be zero (C_A^n = 1).
k \frac{V}{F_{A0}} = X
References
Citation
@online{utikar2024,
author = {Utikar, Ranjeet},
title = {Solutions to Workshop 05: {Collection} and Analysis of Rate
Data},
date = {2024-03-23},
url = {https://cre.smilelab.dev//content/workshops/05-collection-and-analysis-of-rate-data/solutions.html},
langid = {en}
}