Amine-Based CO2 Capture: Fundamentals and Simulation#
This notebook introduces amine-based CO2 capture, the most mature and widely deployed technology for post-combustion carbon capture. We’ll use difflow_cc to simulate a complete capture loop and understand the key process parameters.
Learning Objectives#
Understand how amine absorption works for CO2 capture
Set up absorber and stripper units in difflow_cc
Analyze key performance metrics (capture efficiency, regeneration energy)
Compare different amine solvents
1. Background: Amine Scrubbing#
The Chemistry#
Primary and secondary amines react with CO2 to form carbamates:
This reaction is:
Fast (rate constant k₂ ~ 5000-50000 L/(mol·s) at 25°C)
Exothermic (ΔH ~ -80 to -85 kJ/mol for MEA)
Reversible (at elevated temperature, CO2 is released)
The Process#
A typical amine capture plant consists of:
Absorber: Flue gas contacts lean amine solution counter-currently. CO2 is absorbed.
Rich/Lean Heat Exchanger: Rich solvent is preheated before regeneration.
Stripper (Regenerator): Heated rich solvent releases CO2. Steam provides stripping.
Reboiler: Supplies heat for regeneration (the main energy consumer).
Key Metrics#
Capture Efficiency: Fraction of CO2 removed from flue gas (typically 90%+)
Specific Regeneration Energy: GJ of heat per tonne CO2 captured (3-4 GJ/t for MEA)
Solvent Circulation Rate: L/G ratio (liquid to gas molar ratio)
2. Setup#
import jax
import jax.numpy as jnp
# Enable 64-bit precision for numerical accuracy
jax.config.update("jax_enable_x64", True)
# Import difflow components
from difflow.streams import make_stream, get_flows, total_flow
# Import carbon capture components
from difflow_cc import (
# Solvent database
get_solvent, list_solvents,
# Unit operations
AbsorberParams, AmineAbsorber,
StripperParams, AmineStripper,
)
print("Available solvents:", list_solvents())
Available solvents: ['MEA', 'DEA', 'MDEA', 'PZ', 'AMP', 'Glycine', 'Sarcosine']
3. Exploring the Solvent Database#
Let’s examine the properties of MEA (monoethanolamine), the benchmark solvent.
# Get MEA properties
mea = get_solvent("MEA")
print(f"Solvent: {mea.full_name}")
print(f"Formula: {mea.formula}")
print(f"Type: {mea.solvent_type}")
print()
print("Thermodynamic Properties:")
print(f" Molecular weight: {mea.MW:.2f} g/mol")
print(f" Heat of absorption: {mea.heat_of_absorption:.1f} kJ/mol CO2")
print(f" Max loading capacity: {mea.loading_capacity:.2f} mol CO2/mol amine")
print()
print("Kinetic Properties:")
print(f" Reaction mechanism: {mea.kinetics['mechanism']}")
print(f" Rate constant k2 (25°C): {mea.kinetics['k2_25C']:.0f} L/(mol·s)")
print()
print("Operating Conditions:")
print(f" Typical concentration: {mea.typical_concentration:.0f} wt%")
print(f" Regeneration temperature: {mea.regen_temperature - 273.15:.0f} °C")
print(f" Expected regen energy: {mea.regen_energy:.1f} GJ/tonne CO2")
Solvent: Monoethanolamine
Formula: C2H7NO
Type: primary
Thermodynamic Properties:
Molecular weight: 61.08 g/mol
Heat of absorption: 82.0 kJ/mol CO2
Max loading capacity: 0.50 mol CO2/mol amine
Kinetic Properties:
Reaction mechanism: zwitterion
Rate constant k2 (25°C): 5900 L/(mol·s)
Operating Conditions:
Typical concentration: 30 wt%
Regeneration temperature: 120 °C
Expected regen energy: 3.8 GJ/tonne CO2
4. Setting Up the Absorber#
The absorber is modeled using the Kremser equation with Murphree stage efficiency:
where:
\(\phi\) = fraction of CO2 remaining in gas
\(A\) = absorption factor = L/(mG), the ratio of liquid capacity to gas loading
\(N\) = number of theoretical stages
# Define flue gas feed (typical coal power plant composition)
# 100 mol/s total, 15% CO2
flue_gas = make_stream(
flows={"CO2": 15.0, "N2": 85.0},
T=313.15, # 40°C
P=101325.0, # 1 atm
)
print("Flue Gas Feed:")
print(f" Total flow: {total_flow(flue_gas):.1f} mol/s")
print(f" CO2 flow: {flue_gas['F_CO2']:.1f} mol/s")
print(f" CO2 fraction: {float(flue_gas['F_CO2'] / total_flow(flue_gas)):.1%}")
print(f" Temperature: {float(flue_gas['T']) - 273.15:.1f} °C")
Flue Gas Feed:
Total flow: 100.0 mol/s
CO2 flow: 15.0 mol/s
CO2 fraction: 15.0%
Temperature: 40.0 °C
# Configure absorber
absorber_params = AbsorberParams(
solvent="MEA",
n_stages=10, # Number of theoretical stages
solvent_conc=30.0, # 30 wt% MEA solution
L_G_ratio=3.0, # Liquid/gas molar ratio
T_liquid_in=313.15, # Solvent inlet at 40°C
lean_loading=0.2, # mol CO2 / mol MEA (from stripper)
stage_efficiency=0.25, # Murphree efficiency
)
# Create absorber unit
absorber = AmineAbsorber(absorber_params)
print("Absorber Configuration:")
for key, value in absorber_params.items():
if key in ['solvent', 'n_stages', 'solvent_conc', 'L_G_ratio', 'lean_loading']:
print(f" {key}: {value}")
Absorber Configuration:
solvent: MEA
n_stages: 10
solvent_conc: 30.0
L_G_ratio: 3.0
lean_loading: 0.2
# Run absorber simulation
treated_gas, rich_solvent, absorber_info = absorber(flue_gas)
print("Absorber Results:")
print(f" Capture efficiency: {float(absorber_info['capture_efficiency']):.1%}")
print(f" CO2 captured: {float(absorber_info['CO2_captured']):.2f} mol/s")
print(f" Rich loading: {float(absorber_info['rich_loading']):.3f} mol CO2/mol amine")
print(f" Absorption factor A: {float(absorber_info['absorption_factor']):.2f}")
print()
print(f"Treated Gas:")
print(f" CO2 remaining: {float(treated_gas['F_CO2']):.2f} mol/s")
print(f" CO2 fraction: {float(treated_gas['F_CO2'] / (treated_gas['F_CO2'] + treated_gas['F_N2'])):.2%}")
Absorber Results:
Capture efficiency: 67.3%
CO2 captured: 10.09 mol/s
Rich loading: 0.500 mol CO2/mol amine
Absorption factor A: 51.43
Treated Gas:
CO2 remaining: 4.91 mol/s
CO2 fraction: 5.46%
5. Setting Up the Stripper#
The stripper regenerates the rich solvent by heating. The reboiler duty consists of:
Sensible heat: Heating the solvent to reboiler temperature
Heat of reaction: Reversing the CO2-amine reaction (endothermic)
Heat of vaporization: Generating stripping steam
# Configure stripper
stripper_params = StripperParams(
solvent="MEA",
n_stages=8,
T_reboiler=393.15, # 120°C
P_stripper=200000.0, # 2 bar
target_lean_loading=0.2, # Target lean loading
reflux_ratio=0.3,
cross_exchanger_approach=10.0, # 10 K approach in heat exchanger
)
stripper = AmineStripper(stripper_params)
# Run stripper
lean_solvent, co2_product, stripper_info = stripper(rich_solvent)
print("Stripper Results:")
print(f" Lean loading: {float(stripper_info['lean_loading']):.3f} mol CO2/mol amine")
print(f" CO2 stripped: {float(stripper_info['CO2_stripped']):.2f} mol/s")
print(f" CO2 purity: {float(stripper_info['CO2_purity']):.1%}")
print()
print("Energy Breakdown:")
print(f" Reboiler duty: {float(stripper_info['reboiler_duty'])/1000:.1f} kW")
print(f" - Sensible heat: {float(stripper_info['Q_sensible'])/1000:.1f} kW")
print(f" - Heat of reaction: {float(stripper_info['Q_reaction'])/1000:.1f} kW")
print(f" - Vaporization: {float(stripper_info['Q_vaporization'])/1000:.1f} kW")
print()
print(f" Specific energy: {float(stripper_info['specific_energy']):.2f} GJ/tonne CO2")
Stripper Results:
Lean loading: 0.227 mol CO2/mol amine
CO2 stripped: 9.18 mol/s
CO2 purity: 41.7%
Energy Breakdown:
Reboiler duty: 1772.5 kW
- Sensible heat: 274.0 kW
- Heat of reaction: 752.5 kW
- Vaporization: 746.1 kW
Specific energy: 4.39 GJ/tonne CO2
6. Comparing Amine Solvents#
Different amines offer trade-offs:
Primary amines (MEA): Fast kinetics, high absorption, but high regen energy
Tertiary amines (MDEA): Slower, but lower regen energy
Sterically hindered (AMP): Balance of properties
Cyclic diamines (PZ): Very fast, high capacity
def simulate_capture_loop(solvent_name, flue_gas, l_g_ratio=3.0):
"""Simulate complete capture loop for a given solvent."""
solvent = get_solvent(solvent_name)
# Absorber
abs_params = AbsorberParams(
solvent=solvent_name,
n_stages=10,
solvent_conc=solvent.typical_concentration,
L_G_ratio=l_g_ratio,
lean_loading=0.2,
)
absorber = AmineAbsorber(abs_params)
treated_gas, rich_solvent, abs_info = absorber(flue_gas)
# Stripper
strip_params = StripperParams(
solvent=solvent_name,
T_reboiler=solvent.regen_temperature,
target_lean_loading=0.2,
)
stripper = AmineStripper(strip_params)
lean_solvent, co2_product, strip_info = stripper(rich_solvent)
return {
'capture_efficiency': float(abs_info['capture_efficiency']),
'rich_loading': float(abs_info['rich_loading']),
'specific_energy': float(strip_info['specific_energy']),
'heat_of_absorption': solvent.heat_of_absorption,
}
# Compare solvents
solvents = ['MEA', 'DEA', 'MDEA', 'PZ', 'AMP']
print(f"{'Solvent':<8} {'Type':<12} {'Capture':<10} {'Rich Load':<12} {'ΔH_abs':<10} {'Energy':<10}")
print(f"{'':8} {'':12} {'(%)':10} {'(mol/mol)':12} {'(kJ/mol)':10} {'(GJ/t)':10}")
print("-" * 62)
for s in solvents:
solvent_data = get_solvent(s)
result = simulate_capture_loop(s, flue_gas)
print(f"{s:<8} {solvent_data.solvent_type:<12} {result['capture_efficiency']:.1%} "
f"{result['rich_loading']:.3f} "
f"{result['heat_of_absorption']:.1f} "
f"{result['specific_energy']:.2f}")
Solvent Type Capture Rich Load ΔH_abs Energy
(%) (mol/mol) (kJ/mol) (GJ/t)
--------------------------------------------------------------
MEA primary 67.3% 0.500 82.0 4.39
DEA secondary 50.6% 0.500 72.0 4.49
MDEA tertiary 0.4% 0.201 55.0 179.29
PZ cyclic_diamine 99.9% 0.609 75.0 4.08
AMP sterically_hindered 9.9% 0.262 65.0 8.12
7. Sensitivity Analysis: L/G Ratio#
The liquid-to-gas ratio (L/G) is a key operating parameter:
Higher L/G → Higher capture, but more solvent to regenerate
Lower L/G → Lower capture, but lower energy consumption
l_g_ratios = [2.0, 2.5, 3.0, 3.5, 4.0, 5.0]
print(f"{'L/G Ratio':<12} {'Capture (%)':<14} {'Rich Loading':<14}")
print("-" * 40)
for lg in l_g_ratios:
result = simulate_capture_loop('MEA', flue_gas, l_g_ratio=lg)
print(f"{lg:<12.1f} {result['capture_efficiency']:.1%} {result['rich_loading']:.3f}")
L/G Ratio Capture (%) Rich Loading
----------------------------------------
2.0 44.9% 0.500
2.5 56.1% 0.500
3.0 67.3% 0.500
3.5 78.5% 0.500
4.0 89.7% 0.500
5.0 99.9% 0.467
8. Key Takeaways#
MEA is the benchmark but has high regeneration energy (~3.5-4 GJ/t)
Trade-offs exist between:
Capture efficiency vs energy consumption
Reaction kinetics vs regeneration energy
Solvent capacity vs corrosivity
Operating parameters matter:
L/G ratio directly affects capture and circulation costs
Lean loading affects both capture and regen energy
Reboiler temperature must balance stripping vs degradation
difflow_cc enables:
Rapid screening of solvents and conditions
Sensitivity analysis
Gradient-based optimization (in next notebooks)
Next Steps#
02_membrane_separation.ipynb: Membrane-based CO2 capture
03_adsorption_processes.ipynb: PSA, TSA, VSA cycles
04_optimization.ipynb: Gradient-based optimization