24. STRAP adsorption column design#
The solvent targeted recovery and precipitation (STRAP) process leverages solvents for the targeted dissolution and precipitation of specific layers within multilayer plastic films. Additives and inks also dissolve in organic solvents and accumulate with each use, ultimately making its way to the product resin and lowering its aesthetic appeal. To ensure a high-quality product with a desirable color, an adsorption column with activated carbon bed can selectively remove inks. In this case study, we demonstrate how to design such an adsorption column using experimental data on adsorption isotherms and mass transfer kinetics.
Neglecting diffusion, the mass transfer equations are:
\(x\) - Length of bed [m].
\(t\) - Time [h].
\(k\) - Overall solid mass transfer coefficient [1/h].
\(\epsilon\) - Void fraction [-].
\(u\) - Superficial velocity of fluid [m/h].
\(q_e\) - Equilibrium adsorbate concentration in the adsorbent [g / kg].
\(\rho\) - Bulk density of the adsorbent [kg / m\(^3\)].
Assuming a bed of length \(L\), a feed concentration of \(C_0\), and a saturated adsorption capacity of \(q_0\), we can scale the equations as follows:
Assuming: \(z = \frac{x}{L}\), \(\tau = \frac{ut}{L}\), \(\kappa = \frac{q}{q_0}\), \(\kappa_e = \frac{q_e}{q_0}\), \(Da = \frac{kL}{u}\)
We begin by fitting an isother model (e.g., Langmuir, Freundlich) and the mass transfer coefficient using experimental data. Here are the key equations needed to fit the isotherms with experimental data:
Langmuir isotherm:
\(C_e\) - Equilibrium adsorbate concentration in the solution [g / L].
\(K\) - Equilibrium constant [L / g].
\(q_{max}\) - Maximum equilibrium loading [g / kg].
Freundlich isotherm:
\(K\) - Equilibrium constant [\((g / L)^{-1/n} g / kg\)].
\(n\) - Exponential factor [-].
In BioSTEAM, we can pass the experimental data and get the isotherm and kinectic parameters with the best fit. As expected from the low concentrations used in the experiments, the Freundlich isotherm provides the best fit:
[1]:
from biosteam import SingleComponentAdsorptionColumn
import matplotlib.pyplot as plt
import biosteam as bst
import numpy as np
import time
Ce = np.array([
0.06013, 0.07133, 0.07133, 0.31769, 0.31769, 0.31769,
0.63124, 0.64244, 0.63124, 2.03102, 2.03102, 2.03102,
4.16988, 4.16988, 4.16988, 5.00974, 4.99854, 4.99854
]) / 1000 # g / L
qe = np.array([
1.24, 1.24, 1.24, 1.87, 1.87, 1.87, 2.85, 2.84, 2.85,
4.48, 4.48, 4.48, 5.5, 5.5, 5.5, 6.53, 6.56, 6.56
]) # g / kg
t = np.array([0.0, 31.0, 60.0, 90.0, 120.0, 150.0]) / 60 # h
C = np.array([7.0, 2.45, 1.03, 0.43, 0.26, 0.13]) # g / L
volume = 0.075 # L
adsorbent = 1 # g
fig, axes, parameters = bst.SingleComponentAdsorptionColumn.plot_isotherm_and_mass_transfer_coefficient_fit(
Ce, qe, t, C, volume, adsorbent, t_units='h', C_units='g/L', q_units='g/kg'
)
plt.show()

Given the superficial velocity (between 4 to 14.4 m/h for liquids) and the feed flow rate, \(Q\), we can determine the diameter of the column:
Given the adsorbate concentration in the feed and the cycle time \(t_{cycle}\), we can determine the length of the bed in equilibrium (spent) \(LES\) in kg / m\(^3\):
The length of unused bed will be given by the mass transfer zone, MTZ, which is based on mass transfer modeling:
Finally, let’s simulate an adsorption column in BioSTEAM:
[2]:
import biosteam as bst
bst.nbtutorial()
chemicals = bst.Chemicals([
'Toluene',
bst.Chemical('Ink', search_db=False, default=True, phase='l'),
bst.Chemical('ActivatedCarbon', search_db=False, default=True, phase='l')
])
bst.settings.set_thermo(chemicals)
chemicals.compile()
feed = bst.Stream(ID='feed', phase='l', T=298, P=1.01e+06,
Toluene=1000, Ink=0.001, units='kg/hr')
# Adsorption equilibrium and rate data from Dr. Tianwei Yan from Prof. George Huber's group.
A1 = bst.AdsorptionColumn(
'A1', ins=feed, outs='effluent',
cycle_time=1000, # h
superficial_velocity=4, # m / h
isotherm_model='Freundlich',
isotherm_args = (48.4, 2.58), # K, n
k = 0.00931, # [1 / hr]
void_fraction=1 - 0.38 / 0.8,
C_final_scaled=0.05, # Breakthrough condition
adsorbate='Ink',
)
A1.simulate()
A1.show()
print()
A1.results(basis='SI')
SingleComponentAdsorptionColumn: A1
ins...
[0] feed
phase: 'l', T: 298 K, P: 1.01e+06 Pa
flow (kmol/hr): Toluene 10.9
Ink 0.001
[1] -
phase: 'l', T: 298.15 K, P: 101325 Pa
flow: 0
[2] -
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kmol/hr): ActivatedCarbon 0.213
outs...
[0] effluent
phase: 'l', T: 298 K, P: 101325 Pa
flow (kmol/hr): Toluene 10.9
[1] -
phase: 'l', T: 298.15 K, P: 101325 Pa
flow: 0
[2] -
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kmol/hr): ActivatedCarbon 0.213
[2]:
Single component adsorption column | Units | A1 | |
---|---|---|---|
Electricity | Power | kW | 0.278 |
Cost | USD/hr | 0.0217 | |
Design | Diameter | m | 0.607 |
Length | m | 1.94 | |
Vessel type | Vertical | ||
Weight | kg | 254 | |
Wall thickness | m | 0.00706 | |
Pressure drop | kg/m/s² | 15 | |
Vessel material | Stainless steel 316 | ||
Purchase cost | Vertical pressure vessel (x3) | USD | 6.33e+04 |
Platform and ladders (x3) | USD | 7.58e+03 | |
Pump - Pump (x3) | USD | 1.3e+04 | |
Pump - Motor (x3) | USD | 229 | |
Total purchase cost | USD | 8.42e+04 | |
Utility cost | USD/hr | 0.0217 |
[3]:
import matplotlib.pyplot as plt
from IPython.display import HTML
plt.ioff() # prevent both inline and widget plots
ani = A1.simulation_gif()
HTML(ani.to_jshtml(15))
[3]: