| jupytext |
|
||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| kernelspec |
|
By the end of this lecture, you should be able to
- Describe the concept of adsorption and its importance in surface science.
- Implement a Monte Carlo simulation to study adsorption on surfaces.
- Analyze the results of a Monte Carlo simulation to determine the adsorption isotherm.
Adsorption is the process by which molecules or atoms adhere to the surface of a solid or liquid. It plays a crucial role in various applications, such as catalysis, gas separation, and drug delivery. In this project, we will explore how to simulate adsorption on solid surfaces using Monte Carlo methods.
Adsorption on surfaces can be classified into three main types:
-
Physisorption: In physisorption, molecules are held to the surface by weak van der Waals forces. This type of adsorption is reversible and does not involve the formation of chemical bonds between the adsorbate and the surface.
-
Chemisorption: Chemisorption involves the formation of chemical bonds between the adsorbate and the surface. This type of adsorption is stronger than physisorption and can lead to irreversible changes in the adsorbate.
-
Dissociative Adsorption: In dissociative adsorption, the adsorbate molecule dissociates into atoms or radicals upon adsorption. This process is common for molecules like hydrogen and oxygen on metal surfaces.
The adsorption isotherm is a plot of the amount of adsorbate adsorbed on the surface as a function of pressure or concentration at constant temperature. The shape of the isotherm provides information about the adsorption mechanism and the surface properties.
In the lattice gas model, we represent the surface as a 2D lattice where each lattice site can be occupied by an adsorbate molecule. The adsorbate molecules interact with each other and with the surface through pairwise interactions. The energy of the system is given by the sum of these pairwise interactions.
In this model system,
:tags: [hide-input]
import matplotlib.pyplot as plt
import numpy as np
# Define grid size
gridsize = 4
# Create figure and axis
fig, ax = plt.subplots()
# Create gridlines
ax.set_xticks(np.arange(0, gridsize + 1, 1))
ax.set_yticks(np.arange(0, gridsize + 1, 1))
ax.grid(True)
# Set the limits of the plot
ax.set_xlim(0, gridsize)
ax.set_ylim(0, gridsize)
# Label adsorption sites
for x in range(gridsize):
for y in range(gridsize):
ax.text(x + 0.5, y + 0.5, 'A', ha='center', va='center')
# Define sorbent particles' positions
sorbent_positions = [(0.5, 0.5), (2.5, 0.5), (1.5, 1.5), (3.5, 1.5), (0.5, 2.5)]
# Plot sorbent particles
for (x, y) in sorbent_positions:
ax.plot(x, y, 'o', color='blue', markersize=15, zorder=10)
# Add labels for N_s and N_a
ax.text(2, -0.5, r'$N_a = 4 \times 4 = 16$ adsorption sites', ha='center')
ax.text(2, 4.5, r'$N_s = 5$ sorbent particles', color='blue', ha='center')
# Hide ticks
ax.set_xticklabels([])
ax.set_yticklabels([])
# Show plot
plt.gca().set_aspect('equal', adjustable='box')
plt.show()
```{note}
A square lattice can be used to represent the (100) surface of face-centered cubic solids like the alkali metals Ca and Sr, group nine transition metals Rh and Ir, group ten transition metals (Ni, Pd, and Pt), group eleven transition metals (Cu, Ag, and Au), Al, and Pb.
```
The figure above shows an example of an ideal sorbent system with
In Lecture 11, we showed that the coverage of the surface by the sorbent particles can then be calculated as
where
:class: tip
The chemical potential $\mu$ can be interpreted as the energy change when a sorbent particle is added to the system from a reservoir at constant temperature. A positive chemical potential indicates that the sorbent particles are more likely to adsorb on the surface, while a negative chemical potential indicates that the sorbent particles are more likely to desorb from the surface.
In a more realistic model, we can introduce mean-field interactions between the sorbent particles. The mean-field interaction energy between one sorbent particle and all other sorbent particles can be set to a constant value. This interaction energy can be used to model the attractive or repulsive interactions between the sorbent particles.
:tags: [hide-input]
import matplotlib.pyplot as plt
import numpy as np
# Define grid size
gridsize = 4
# Create figure and axis
fig, ax = plt.subplots()
# Create gridlines
ax.set_xticks(np.arange(0, gridsize + 1, 1))
ax.set_yticks(np.arange(0, gridsize + 1, 1))
ax.grid(True)
# Set the limits of the plot
ax.set_xlim(0, gridsize)
ax.set_ylim(0, gridsize)
# Label adsorption sites
for x in range(gridsize):
for y in range(gridsize):
ax.text(x + 0.5, y + 0.5, 'A', ha='center', va='center')
# Define sorbent particles' positions
sorbent_positions = [(0.5, 0.5), (2.5, 0.5), (1.5, 1.5), (3.5, 1.5), (0.5, 2.5)]
# Plot sorbent particles
for (x, y) in sorbent_positions:
ax.plot(x, y, 'o', color='blue', markersize=15, zorder=10)
# Mean-field interaction arrows (red lines)
for (x1, y1) in sorbent_positions:
for (x2, y2) in sorbent_positions:
ax.arrow(x1, y1, x2 - x1, y2 - y1, head_width=0.1, head_length=0.1, fc='red', ec='red', length_includes_head=True)
# Add labels for N_s and N_a
ax.text(2, -0.5, r'$N_a = 4 \times 4 = 16$ adsorption sites', ha='center')
ax.text(2, 4.5, r'$N_s = 5$ sorbent particles', color='blue', ha='center')
# Hide ticks
ax.set_xticklabels([])
ax.set_yticklabels([])
# Show plot
plt.gca().set_aspect('equal', adjustable='box')
plt.show()
The figure above shows the same sorbent system as before, but now with interactions between the sorbent particles represented by red arrows. In a mean-field model, these interactions are replaced by a constant mean-field interaction energy.
In Lecture 14, we implemented a basic Metropolis algorithm for the canonical ensemble. In this lecture and the associated project, we will extend the Metropolis algorithm to the grand canonical ensemble to study adsorption on surfaces.
In the grand canonical ensemble, the system is in contact with a reservoir of particles at constant chemical potential
where
To simulate the grand canonical ensemble using the Metropolis algorithm, we need to modify the acceptance criterion to account for changes in the number of particles. The acceptance probability for adding a particle to the system is given by
where
To implement a grand canonical Monte Carlo simulation for adsorption on surfaces, we can follow these steps:
- Initialize the system with a random distribution of sorbent particles on the surface.
- Choose a random move (addition or removal of a sorbent particle) and calculate the change in energy.
- Accept or reject the move based on the acceptance probability.
By repeating these steps for a large number of iterations, we can sample the grand canonical ensemble and calculate the adsorption isotherm.
Let's consider the adsorption of ideal sorbent particles on a
The energy of the system is given by
where
def energy_ideal_sorbent(N, epsilon=-1.0):
"""Calculate the energy of the ideal sorbent system."""
return N * epsilon
Now, let's implement the grand canonical Monte Carlo simulation for the ideal sorbent system.
import numpy as np
np.random.seed(42)
def grand_canonical_mc_ideal_sorbent(N_a, mu, beta, epsilon, n_steps=10000):
"""Perform grand canonical Monte Carlo simulation for ideal sorbent particles."""
# Initialize the system
N = 0
coverage = []
for _ in range(n_steps):
# Choose a random move
if np.random.rand() < 0.5:
# Attempt to add a particle
energy_diff = energy_ideal_sorbent(N + 1, epsilon) - energy_ideal_sorbent(N, epsilon)
# acc_prob = min(1, (N_a - N) / (N + 1) * np.exp(-beta * (epsilon - mu)))
acc_prob = min(1, (N_a - N) / (N + 1) * np.exp(-beta * (energy_diff - mu)))
if np.random.rand() < acc_prob:
N += 1
else:
# Attempt to remove a particle
energy_diff = energy_ideal_sorbent(N - 1, epsilon) - energy_ideal_sorbent(N, epsilon)
# acc_prob = min(1, N / (N_a - N + 1) * np.exp(-beta * (epsilon + mu)))
acc_prob = min(1, N / (N_a - N + 1) * np.exp(-beta * (energy_diff + mu)))
if np.random.rand() < acc_prob:
N -= 1
# Calculate coverage
theta = N / N_a
coverage.append(theta)
return coverage
Let's run the grand canonical Monte Carlo simulation for the ideal sorbent system and plot the adsorption isotherm.
import matplotlib.pyplot as plt
# Define system parameters
N_a = 16
mus = np.linspace(-2, 0, 21)
beta = 10.0
epsilon = -1.0
# Perform grand canonical Monte Carlo simulation for different chemical potentials
coverages = [np.mean(grand_canonical_mc_ideal_sorbent(N_a, mu, beta, epsilon)) for mu in mus]
# Calculate the analytical solution
mus_analytical = np.linspace(-2, 0, 100)
coverages_analytical = [np.exp(beta * (mu - epsilon)) / (1 + np.exp(beta * (mu - epsilon))) for mu in mus_analytical]
# Plot the adsorption isotherm
plt.plot(mus, coverages, 'o-', label='Simulation')
plt.plot(mus_analytical, coverages_analytical, '--', label='Analytical')
plt.xlabel(r'Chemical potential $\mu$')
plt.ylabel(r'Coverage $\theta$')
plt.legend()
plt.show()
The plot above shows the adsorption isotherm for the ideal sorbent system obtained from the grand canonical Monte Carlo simulation (solid line) and the analytical solution (dashed line). The coverage of the surface increases with the chemical potential, as expected because the sorbent particles are more likely to adsorb on the surface at higher chemical potentials.
Next, let's consider the adsorption of sorbent particles with mean-field interactions on a
The energy of the system with mean-field interactions is given by
where
def energy_mean_field_sorbent(N, N_a, coverage, z=4, epsilon=-1.0, epsilon_int=0.1):
"""Calculate the energy of the system with mean-field interactions."""
return N * epsilon + N_a * coverage * z * epsilon_int
:class: dropdown
The term $N_a \theta z$ in the energy expression accounts for the mean-field interactions between the sorbent particles. The factor $N_a$ represents the total number of adsorption sites on the surface, $\theta$ is the coverage of the surface by the sorbent particles, and $z$ is the coordination number of the lattice. The product $N_a \theta z$ gives the number of interactions between each sorbent particle and its $z$ nearest neighbors on the lattice.
Now, let's implement the grand canonical Monte Carlo simulation for the system with mean-field interactions.
np.random.seed(42)
def grand_canonical_mc_mean_field_sorbent(N_a, mu, beta, epsilon, epsilon_int, n_steps=10000):
"""Perform grand canonical Monte Carlo simulation for sorbent particles with mean-field interactions."""
# Initialize the system
N = 0
coverage = []
for _ in range(n_steps):
# Current coverage
theta = N / N_a
# Choose a random move
if np.random.rand() < 0.5:
# Attempt to add a particle
theta_after = (N + 1) / N_a
energy_diff = (energy_mean_field_sorbent(N + 1, N_a, theta_after, epsilon=epsilon, epsilon_int=epsilon_int) -
energy_mean_field_sorbent(N, N_a, theta, epsilon=epsilon, epsilon_int=epsilon_int))
acc_prob = min(1, (N_a - N) / (N + 1) * np.exp(-beta * (energy_diff - mu)))
if np.random.rand() < acc_prob:
N += 1
else:
# Attempt to remove a particle
theta_after = (N - 1) / N_a
energy_diff = (energy_mean_field_sorbent(N - 1, N_a, theta_after, epsilon=epsilon, epsilon_int=epsilon_int) -
energy_mean_field_sorbent(N, N_a, theta, epsilon=epsilon, epsilon_int=epsilon_int))
acc_prob = min(1, N / (N_a - N + 1) * np.exp(-beta * (energy_diff + mu)))
if N > 0 and np.random.rand() < acc_prob:
N -= 1
# Update coverage
theta = N / N_a
coverage.append(theta)
return coverage
Let's run the grand canonical Monte Carlo simulation for the system with mean-field interactions and plot the adsorption isotherm.
# Define system parameters
N_a = 16
mus = np.linspace(-2, 1, 31)
beta = 10.0
epsilon = -1.0
epsilon_int = 0.1
# Perform grand canonical Monte Carlo simulation for different chemical potentials
coverages_ideal_sorbent = [np.mean(grand_canonical_mc_ideal_sorbent(N_a, mu, beta, epsilon)) for mu in mus]
coverages_mean_field_sorbent = [np.mean(grand_canonical_mc_mean_field_sorbent(N_a, mu, beta, epsilon, epsilon_int)) for mu in mus]
# Plot the adsorption isotherm
plt.plot(mus, coverages_ideal_sorbent, 'o-', label='Ideal Sorbent')
plt.plot(mus, coverages_mean_field_sorbent, 'o-', label='Mean-Field Sorbent')
plt.axhline(y=0.5, color='gray', linestyle='--', label=r'$\theta = 0.5$')
plt.axvline(x=epsilon, color='gray', linestyle='--', label=r'$\mu = \epsilon$')
plt.axvline(x=epsilon + 4 * epsilon_int, color='gray', linestyle='--', label=r'$\mu = \epsilon + 4 \epsilon_{\rm int}$')
plt.xlabel(r'Chemical potential $\mu$')
plt.ylabel(r'Coverage $\theta$')
plt.legend()
plt.show()
The plot above shows the adsorption isotherm for the ideal sorbent system and the system with mean-field interactions obtained from the grand canonical Monte Carlo simulation. The dashed lines indicate the critical chemical potentials where the coverage of the surface is equal to 0.5.
In this lecture, we introduced the concept of adsorption on surfaces and discussed the importance of Monte Carlo simulations in studying adsorption phenomena. We implemented grand canonical Monte Carlo simulations for ideal sorbent particles and sorbent particles with mean-field interactions on a square lattice. The simulations allowed us to calculate the adsorption isotherms of the systems and analyze the effects of chemical potential and interaction energies on the adsorption behavior.
In the associated project, you will have the opportunity to explore more complex adsorption models and study the effects of different parameters on the adsorption isotherm. You will also learn how to analyze the simulation results and extract valuable information about the adsorption process.