Learning Objectives
- Apply BET theory to measure catalyst surface area
- Identify active sites using chemisorption (CO, H₂, NH₃)
- Interpret XPS spectra for surface composition and oxidation states
- Use TEM and SEM for morphology and particle size analysis
- Understand operando spectroscopy for in situ catalyst studies
- Apply machine learning to spectral interpretation
- Diagnose catalyst deactivation mechanisms
4.1 Surface Area Measurement: BET Method
The BET Equation
The Brunauer-Emmett-Teller (BET) method is the standard technique for measuring specific surface area. It extends the Langmuir monolayer model to multilayer adsorption:
$$\frac{P/P_0}{n(1-P/P_0)} = \frac{1}{n_m C} + \frac{C-1}{n_m C} \cdot \frac{P}{P_0}$$Where:
- $n$ = amount adsorbed at pressure $P$
- $P_0$ = saturation pressure of adsorbate
- $n_m$ = monolayer capacity
- $C$ = BET constant (related to heat of adsorption)
BET Surface Area Calculation
$$S_{BET} = \frac{n_m \cdot N_A \cdot A_m}{m}$$Where $N_A$ is Avogadro's number and $A_m$ is the cross-sectional area of the adsorbate molecule (0.162 nm² for N₂).
Code Example 1: BET Analysis
"""
BET surface area analysis from N2 adsorption data
Demonstrates linearization and surface area calculation
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
# Simulated N2 adsorption data (P/P0, Volume adsorbed in cm³/g STP)
p_p0 = np.array([0.05, 0.10, 0.15, 0.20, 0.25, 0.30])
volume_ads = np.array([45.2, 52.1, 58.3, 64.8, 72.1, 80.5])
# BET linearization: y = (P/P0) / [V(1-P/P0)] vs x = P/P0
# Linear form: y = 1/(Vm*C) + [(C-1)/(Vm*C)] * x
y = p_p0 / (volume_ads * (1 - p_p0))
x = p_p0
# Linear regression
slope, intercept, r_value, p_value, std_err = stats.linregress(x, y)
# Calculate BET parameters
Vm = 1 / (slope + intercept) # Monolayer volume (cm³/g STP)
C = 1 + slope / intercept # BET constant
# Surface area calculation
# At STP: 1 mole of gas = 22,414 cm³
# N2 cross-sectional area: 0.162 nm² = 0.162e-18 m²
Na = 6.022e23
Am = 0.162e-18 # m²
V_molar = 22414 # cm³/mol at STP
S_BET = Vm * Na * Am / V_molar # m²/g
# Create figure
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# Left: Raw isotherm
ax1.scatter(p_p0, volume_ads, s=100, c='blue', edgecolors='black', zorder=5)
ax1.plot(p_p0, volume_ads, 'b-', linewidth=1.5, alpha=0.7)
ax1.set_xlabel('Relative Pressure P/P₀', fontsize=12)
ax1.set_ylabel('Volume Adsorbed (cm³/g STP)', fontsize=12)
ax1.set_title('N₂ Adsorption Isotherm', fontsize=13, fontweight='bold')
ax1.grid(alpha=0.3)
ax1.set_xlim(0, 0.35)
# Right: BET plot
ax2.scatter(x, y, s=100, c='red', edgecolors='black', zorder=5)
x_line = np.linspace(0, 0.35, 100)
y_line = slope * x_line + intercept
ax2.plot(x_line, y_line, 'r--', linewidth=2, label=f'y = {slope:.4f}x + {intercept:.5f}')
ax2.set_xlabel('P/P₀', fontsize=12)
ax2.set_ylabel('P/P₀ / [V(1-P/P₀)]', fontsize=12)
ax2.set_title('BET Plot (Linearized)', fontsize=13, fontweight='bold')
ax2.legend(fontsize=10)
ax2.grid(alpha=0.3)
ax2.set_xlim(0, 0.35)
# Add equation and results
textstr = f'$R^2$ = {r_value**2:.4f}\n$V_m$ = {Vm:.1f} cm³/g\nC = {C:.1f}\n$S_{{BET}}$ = {S_BET:.1f} m²/g'
ax2.text(0.05, 0.95, textstr, transform=ax2.transAxes, fontsize=11,
verticalalignment='top', bbox=dict(boxstyle='round', facecolor='wheat'))
plt.tight_layout()
plt.show()
print("\nBET Analysis Results:")
print(f"• Monolayer volume (Vm): {Vm:.2f} cm³/g STP")
print(f"• BET constant (C): {C:.1f}")
print(f"• BET Surface Area: {S_BET:.1f} m²/g")
print(f"• R² of linear fit: {r_value**2:.4f}")
4.2 Active Site Identification: Chemisorption
Probe Molecules for Different Metals
| Probe | Target Sites | Stoichiometry | Information |
|---|---|---|---|
| H₂ | Pt, Pd, Ni | H:M = 1:1 | Metal dispersion |
| CO | Pt, Pd, Rh, Cu | CO:M = 1:1 to 1:2 | Metal surface area |
| O₂ | Ag, Cu | O:M = 1:1 | Reactive oxygen |
| NH₃ | Acid sites | - | Total acidity (TPD) |
| Pyridine | Lewis/Brønsted acids | - | Acid type (FTIR) |
Metal Dispersion
Metal dispersion is the fraction of metal atoms exposed on the surface:
$$D = \frac{\text{Surface metal atoms}}{\text{Total metal atoms}} = \frac{n_{chem} \cdot S_f}{n_{total}}$$Where $S_f$ is the stoichiometry factor (e.g., 1 for H:Pt = 1:1).
Code Example 2: Dispersion and Particle Size
"""
Calculate metal dispersion and particle size from chemisorption data
"""
import numpy as np
import matplotlib.pyplot as plt
def calculate_dispersion(V_chem_cm3_g, metal_loading_wt_pct, metal_mw,
stoichiometry=1, VM=22414):
"""
Calculate metal dispersion from chemisorption volume
Args:
V_chem_cm3_g: Chemisorbed volume (cm³/g catalyst at STP)
metal_loading_wt_pct: Metal loading (wt%)
metal_mw: Metal molecular weight (g/mol)
stoichiometry: Chemisorption stoichiometry (probe:metal)
VM: Molar volume at STP (cm³/mol)
"""
# Moles of probe chemisorbed per g catalyst
n_probe = V_chem_cm3_g / VM
# Moles of surface metal atoms
n_surface_metal = n_probe / stoichiometry
# Total moles of metal in catalyst
n_total_metal = (metal_loading_wt_pct / 100) / metal_mw
# Dispersion
D = n_surface_metal / n_total_metal
return min(D, 1.0)
def dispersion_to_particle_size(D, metal='Pt'):
"""
Estimate particle size from dispersion (spherical particles)
d = 6 * V_atomic / (a * D)
"""
# Atomic parameters (approximate)
params = {
'Pt': {'V_atomic': 15.1e-24, 'a': 8.0e-20}, # cm³, cm²
'Pd': {'V_atomic': 14.7e-24, 'a': 7.9e-20},
'Rh': {'V_atomic': 13.8e-24, 'a': 7.6e-20},
}
p = params.get(metal, params['Pt'])
if D > 0:
d_cm = 6 * p['V_atomic'] / (p['a'] * D)
return d_cm * 1e7 # Convert to nm
return np.inf
# Example: Pt/Al2O3 catalyst with 1 wt% Pt
metal_loading = 1.0 # wt%
metal_mw = 195.08 # g/mol for Pt
# Different chemisorption volumes
V_chem_values = np.linspace(0.05, 0.5, 50)
dispersions = [calculate_dispersion(V, metal_loading, metal_mw) for V in V_chem_values]
particle_sizes = [dispersion_to_particle_size(D) for D in dispersions]
# Create figure
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# Left: Dispersion vs chemisorption volume
ax1.plot(V_chem_values, np.array(dispersions) * 100, 'b-', linewidth=2)
ax1.set_xlabel('H₂ Chemisorption (cm³/g STP)', fontsize=12)
ax1.set_ylabel('Dispersion (%)', fontsize=12)
ax1.set_title('Pt Dispersion vs Chemisorption', fontsize=13, fontweight='bold')
ax1.grid(alpha=0.3)
ax1.axhline(y=50, color='green', linestyle='--', alpha=0.5, label='50% dispersion')
ax1.legend()
# Right: Particle size vs dispersion
D_range = np.linspace(0.05, 1.0, 100)
sizes = [dispersion_to_particle_size(D) for D in D_range]
ax2.plot(np.array(D_range) * 100, sizes, 'r-', linewidth=2)
ax2.set_xlabel('Dispersion (%)', fontsize=12)
ax2.set_ylabel('Particle Size (nm)', fontsize=12)
ax2.set_title('Particle Size vs Dispersion', fontsize=13, fontweight='bold')
ax2.grid(alpha=0.3)
ax2.set_ylim(0, 20)
# Add size regions
ax2.axhspan(0, 2, alpha=0.2, color='green', label='Clusters (<2 nm)')
ax2.axhspan(2, 5, alpha=0.2, color='yellow', label='Small NPs (2-5 nm)')
ax2.axhspan(5, 20, alpha=0.2, color='orange', label='Large NPs (>5 nm)')
ax2.legend(loc='upper right', fontsize=9)
plt.tight_layout()
plt.show()
# Example calculation
V_example = 0.25
D_example = calculate_dispersion(V_example, metal_loading, metal_mw)
size_example = dispersion_to_particle_size(D_example)
print(f"\nExample: 1 wt% Pt/Al₂O₃ with V_chem = {V_example} cm³/g")
print(f"• Dispersion: {D_example*100:.1f}%")
print(f"• Estimated particle size: {size_example:.1f} nm")
4.3 Spectroscopic Techniques
X-ray Photoelectron Spectroscopy (XPS)
XPS provides information about surface composition and oxidation states:
- Principle: Photoemission of core electrons by X-rays
- Information: Elemental composition, oxidation states, chemical bonding
- Depth: ~1-10 nm (surface sensitive)
Code Example 3: XPS Peak Analysis
"""
Simulate and analyze XPS spectrum for Pt catalyst
Demonstrates peak fitting and oxidation state identification
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
def gaussian(x, amp, cen, wid):
"""Gaussian peak function"""
return amp * np.exp(-(x - cen)**2 / (2 * wid**2))
def multi_gaussian(x, *params):
"""Multiple Gaussian peaks"""
n_peaks = len(params) // 3
y = np.zeros_like(x)
for i in range(n_peaks):
amp = params[3*i]
cen = params[3*i + 1]
wid = params[3*i + 2]
y += gaussian(x, amp, cen, wid)
return y
# Binding energy range for Pt 4f
BE = np.linspace(68, 82, 200)
# Simulate Pt 4f spectrum with Pt(0) and Pt(II) components
# Pt(0) 4f7/2: 71.0 eV, 4f5/2: 74.5 eV (spin-orbit splitting ~3.5 eV)
# Pt(II) 4f7/2: 72.5 eV, 4f5/2: 76.0 eV (shifted +1.5 eV)
# True parameters (what we're trying to recover)
true_params = [
100, 71.0, 0.8, # Pt(0) 4f7/2
67, 74.5, 0.8, # Pt(0) 4f5/2 (ratio 0.67 from degeneracy)
40, 72.5, 1.0, # Pt(II) 4f7/2
27, 76.0, 1.0, # Pt(II) 4f5/2
]
# Generate synthetic spectrum with noise
np.random.seed(42)
spectrum = multi_gaussian(BE, *true_params)
noise = np.random.normal(0, 3, len(BE))
spectrum_noisy = spectrum + noise
background = 5 + 0.5 * (BE - 68) # Shirley-like background
spectrum_noisy += background
# Fit the spectrum (simplified - in practice, more sophisticated fitting is used)
initial_guess = [90, 71.2, 0.9, 60, 74.6, 0.9, 35, 72.8, 1.1, 23, 76.3, 1.1]
popt, pcov = curve_fit(lambda x, *p: multi_gaussian(x, *p) + 5 + 0.5*(x-68),
BE, spectrum_noisy, p0=initial_guess, maxfev=5000)
# Create figure
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# Left: Raw spectrum with fit
ax1.plot(BE, spectrum_noisy, 'k-', linewidth=1.5, label='Experimental')
ax1.plot(BE, multi_gaussian(BE, *popt) + 5 + 0.5*(BE-68), 'r-',
linewidth=2, label='Fitted')
# Individual components
colors = ['blue', 'blue', 'green', 'green']
labels = ['Pt(0) 4f₇/₂', 'Pt(0) 4f₅/₂', 'Pt(II) 4f₇/₂', 'Pt(II) 4f₅/₂']
for i in range(4):
peak = gaussian(BE, popt[3*i], popt[3*i+1], popt[3*i+2])
ax1.fill_between(BE, background, peak + background, alpha=0.3,
color=colors[i], label=labels[i])
ax1.set_xlabel('Binding Energy (eV)', fontsize=12)
ax1.set_ylabel('Intensity (a.u.)', fontsize=12)
ax1.set_title('Pt 4f XPS Spectrum', fontsize=13, fontweight='bold')
ax1.legend(fontsize=9, loc='upper left')
ax1.invert_xaxis() # XPS convention: higher BE on left
ax1.grid(alpha=0.3)
# Right: Oxidation state quantification
pt0_area = popt[0] * popt[2] + popt[3] * popt[5] # Pt(0) 4f7/2 + 4f5/2
pt2_area = popt[6] * popt[8] + popt[9] * popt[11] # Pt(II)
total_area = pt0_area + pt2_area
pt0_pct = pt0_area / total_area * 100
pt2_pct = pt2_area / total_area * 100
categories = ['Pt(0)\nMetallic', 'Pt(II)\nOxidized']
values = [pt0_pct, pt2_pct]
colors_bar = ['#2196f3', '#4caf50']
bars = ax2.bar(categories, values, color=colors_bar, edgecolor='black', width=0.5)
ax2.set_ylabel('Relative Amount (%)', fontsize=12)
ax2.set_title('Pt Oxidation State Distribution', fontsize=13, fontweight='bold')
ax2.set_ylim(0, 100)
for bar, val in zip(bars, values):
ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 2,
f'{val:.1f}%', ha='center', fontsize=12, fontweight='bold')
plt.tight_layout()
plt.show()
print("\nXPS Analysis Results:")
print(f"• Pt(0) metallic: {pt0_pct:.1f}%")
print(f"• Pt(II) oxidized: {pt2_pct:.1f}%")
print(f"• Pt(0) 4f₇/₂ binding energy: {popt[1]:.2f} eV")
print(f"• Pt(II) 4f₇/₂ binding energy: {popt[7]:.2f} eV")
4.4 Microscopy Techniques
TEM and SEM
| Technique | Resolution | Information | Sample Prep |
|---|---|---|---|
| TEM | ~0.1 nm | Particle size, crystallinity, lattice fringes | Thin samples (~100 nm) |
| STEM-HAADF | ~0.1 nm | Z-contrast imaging, single atoms | Thin samples |
| SEM | ~1-10 nm | Surface morphology, porosity | Conductive coating |
| EDX | ~100 nm | Elemental mapping | Combined with TEM/SEM |
Code Example 4: Particle Size Distribution Analysis
"""
Analyze particle size distribution from TEM data
Demonstrates histogram fitting and statistics
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
# Simulated TEM particle size measurements (nm)
np.random.seed(123)
# Lognormal distribution (common for nanoparticles)
particle_sizes = np.random.lognormal(mean=1.0, sigma=0.4, size=200)
particle_sizes = particle_sizes[particle_sizes < 10] # Remove outliers
# Calculate statistics
mean_size = np.mean(particle_sizes)
std_size = np.std(particle_sizes)
median_size = np.median(particle_sizes)
d10 = np.percentile(particle_sizes, 10)
d50 = np.percentile(particle_sizes, 50)
d90 = np.percentile(particle_sizes, 90)
# Create figure
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# Left: Histogram with fitted distribution
n, bins, patches = ax1.hist(particle_sizes, bins=25, density=True,
alpha=0.7, color='steelblue', edgecolor='black')
# Fit lognormal distribution
shape, loc, scale = stats.lognorm.fit(particle_sizes, floc=0)
x_fit = np.linspace(0, 10, 200)
pdf_fit = stats.lognorm.pdf(x_fit, shape, loc, scale)
ax1.plot(x_fit, pdf_fit, 'r-', linewidth=2, label='Lognormal fit')
ax1.axvline(x=mean_size, color='green', linestyle='--', linewidth=2,
label=f'Mean = {mean_size:.2f} nm')
ax1.axvline(x=median_size, color='orange', linestyle=':', linewidth=2,
label=f'Median = {median_size:.2f} nm')
ax1.set_xlabel('Particle Size (nm)', fontsize=12)
ax1.set_ylabel('Probability Density', fontsize=12)
ax1.set_title('TEM Particle Size Distribution', fontsize=13, fontweight='bold')
ax1.legend(fontsize=9)
ax1.set_xlim(0, 8)
ax1.grid(alpha=0.3)
# Right: Cumulative distribution
sorted_sizes = np.sort(particle_sizes)
cumulative = np.arange(1, len(sorted_sizes) + 1) / len(sorted_sizes) * 100
ax2.plot(sorted_sizes, cumulative, 'b-', linewidth=2)
ax2.axhline(y=10, color='gray', linestyle='--', alpha=0.5)
ax2.axhline(y=50, color='gray', linestyle='--', alpha=0.5)
ax2.axhline(y=90, color='gray', linestyle='--', alpha=0.5)
ax2.scatter([d10, d50, d90], [10, 50, 90], s=100, c='red', zorder=5)
ax2.text(d10, 15, f'd₁₀={d10:.2f}', fontsize=10)
ax2.text(d50, 55, f'd₅₀={d50:.2f}', fontsize=10)
ax2.text(d90, 85, f'd₉₀={d90:.2f}', fontsize=10)
ax2.set_xlabel('Particle Size (nm)', fontsize=12)
ax2.set_ylabel('Cumulative %', fontsize=12)
ax2.set_title('Cumulative Size Distribution', fontsize=13, fontweight='bold')
ax2.grid(alpha=0.3)
ax2.set_xlim(0, 8)
plt.tight_layout()
plt.show()
print("\nTEM Particle Size Analysis:")
print(f"• Number of particles measured: {len(particle_sizes)}")
print(f"• Mean diameter: {mean_size:.2f} ± {std_size:.2f} nm")
print(f"• d₁₀/d₅₀/d₉₀: {d10:.2f}/{d50:.2f}/{d90:.2f} nm")
print(f"• Polydispersity index (PDI): {(d90-d10)/d50:.2f}")
4.5 Operando Spectroscopy
In Situ vs Operando
In situ: Measurements under controlled atmosphere/temperature without actual reaction. Operando: Measurements during actual catalytic reaction with simultaneous activity measurement. Operando provides direct structure-activity correlations.
Common Operando Techniques
| Technique | Information | Time Resolution |
|---|---|---|
| Operando DRIFTS | Surface adsorbates, intermediates | Seconds |
| Operando XAS | Oxidation state, coordination | Seconds-minutes |
| Operando Raman | Catalyst structure, adsorbates | Seconds |
| Environmental TEM | Morphology changes, sintering | Sub-second |
4.6 ML-Assisted Spectral Analysis
2025-2026: AI Revolution in Catalyst Characterization
Machine learning is transforming catalyst analysis by enabling automated spectral interpretation, reducing analysis time by 30-50%, and discovering hidden patterns in complex data.
Code Example 5: ML-Based Spectral Classification
"""
Machine learning classification of catalyst states from spectra
Demonstrates training and prediction for catalyst health monitoring
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
# Generate synthetic spectral data for different catalyst states
np.random.seed(42)
n_samples = 200
n_features = 100 # Spectral points
def generate_spectrum(state, noise_level=0.1):
"""Generate synthetic IR spectrum for different catalyst states"""
x = np.linspace(1000, 2000, n_features)
if state == 'fresh':
# Strong metal carbonyl peak at 2050, weak hydroxyl
spectrum = 0.8 * np.exp(-(x - 2050)**2 / 100) + \
0.2 * np.exp(-(x - 1600)**2 / 200)
elif state == 'active':
# Medium carbonyl, reaction intermediates
spectrum = 0.5 * np.exp(-(x - 2050)**2 / 100) + \
0.4 * np.exp(-(x - 1800)**2 / 150) + \
0.3 * np.exp(-(x - 1500)**2 / 100)
elif state == 'deactivated':
# Weak carbonyl, strong coke peaks
spectrum = 0.2 * np.exp(-(x - 2050)**2 / 100) + \
0.6 * np.exp(-(x - 1400)**2 / 200) + \
0.5 * np.exp(-(x - 1300)**2 / 150)
spectrum += np.random.normal(0, noise_level, n_features)
return spectrum
# Generate dataset
states = ['fresh', 'active', 'deactivated']
X = []
y = []
for state in states:
for _ in range(n_samples // len(states)):
X.append(generate_spectrum(state))
y.append(state)
X = np.array(X)
y = np.array(y)
# Split data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3,
random_state=42)
# Train classifier
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X_train, y_train)
# Predict
y_pred = clf.predict(X_test)
# Create figure
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# Top-left: Example spectra
ax1 = axes[0, 0]
x = np.linspace(1000, 2000, n_features)
for state, color in zip(states, ['blue', 'green', 'red']):
spectrum = generate_spectrum(state, noise_level=0.02)
ax1.plot(x, spectrum, color=color, linewidth=2, label=state.capitalize())
ax1.set_xlabel('Wavenumber (cm⁻¹)', fontsize=11)
ax1.set_ylabel('Absorbance (a.u.)', fontsize=11)
ax1.set_title('Example FTIR Spectra', fontsize=12, fontweight='bold')
ax1.legend()
ax1.invert_xaxis()
ax1.grid(alpha=0.3)
# Top-right: Feature importance
ax2 = axes[0, 1]
importance = clf.feature_importances_
ax2.plot(x, importance, 'purple', linewidth=1.5)
ax2.fill_between(x, importance, alpha=0.3, color='purple')
ax2.set_xlabel('Wavenumber (cm⁻¹)', fontsize=11)
ax2.set_ylabel('Feature Importance', fontsize=11)
ax2.set_title('ML Feature Importance', fontsize=12, fontweight='bold')
ax2.invert_xaxis()
ax2.grid(alpha=0.3)
# Bottom-left: Confusion matrix
ax3 = axes[1, 0]
cm = confusion_matrix(y_test, y_pred, labels=states)
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=states,
yticklabels=states, ax=ax3)
ax3.set_xlabel('Predicted', fontsize=11)
ax3.set_ylabel('True', fontsize=11)
ax3.set_title('Confusion Matrix', fontsize=12, fontweight='bold')
# Bottom-right: Accuracy by class
ax4 = axes[1, 1]
accuracy = cm.diagonal() / cm.sum(axis=1) * 100
bars = ax4.bar(states, accuracy, color=['blue', 'green', 'red'], edgecolor='black')
ax4.set_ylabel('Classification Accuracy (%)', fontsize=11)
ax4.set_title('Accuracy by Catalyst State', fontsize=12, fontweight='bold')
ax4.set_ylim(0, 105)
for bar, acc in zip(bars, accuracy):
ax4.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 2,
f'{acc:.1f}%', ha='center', fontsize=11)
plt.tight_layout()
plt.show()
# Classification report
print("\nML Classification Results:")
print(classification_report(y_test, y_pred, target_names=states))
4.7 Catalyst Deactivation
Deactivation Mechanisms
| Mechanism | Cause | Detection | Regeneration |
|---|---|---|---|
| Sintering | High temperature, particle migration | TEM, chemisorption loss | Often irreversible |
| Coking | Carbon deposition | TPO, TGA, Raman | Oxidative regeneration |
| Poisoning | Strong adsorbate binding | Chemisorption, XPS | Depends on poison |
| Fouling | Physical blocking | BET, pore analysis | Washing, calcination |
4.8 Chapter Summary
Key Takeaways
- BET provides total surface area from N₂ adsorption
- Chemisorption quantifies active sites and metal dispersion
- XPS reveals surface composition and oxidation states
- TEM provides atomic-resolution imaging of nanoparticles
- Operando techniques enable real-time structure-activity correlations
- ML accelerates spectral analysis and enables automated catalyst monitoring
- Deactivation characterization guides regeneration strategies
Exercises
Exercise 1: BET Calculation
From BET data with slope = 0.015 and intercept = 0.001, calculate Vm, C, and surface area.
Exercise 2: Dispersion Analysis
A 2 wt% Pt/SiO₂ catalyst shows H₂ chemisorption of 0.35 cm³/g. Calculate the dispersion and estimate particle size.
Exercise 3: XPS Interpretation
An XPS spectrum shows Pt 4f₇/₂ peaks at 71.2 eV and 73.5 eV. What oxidation states are present? What might cause the higher BE component?
Exercise 4: Deactivation Diagnosis
A hydrogenation catalyst loses 50% activity after 100 hours. BET area is unchanged, but CO chemisorption dropped 60%. What is the likely deactivation mechanism?