09 - Adsorption#

This tutorial demonstrates calculating adsorption energies for atoms on metal surfaces, using H on Al(111) as an example.

Note: These calculations use reduced settings for demonstration purposes. Production calculations would use larger slabs, higher ENCUT, and relaxed surfaces.

import numpy as np
from ase.build import add_adsorbate, bulk, fcc111, molecule
from ase.constraints import FixAtoms

from vasp import Vasp

print("=" * 60)
print("Adsorption Energy Calculations")
print("=" * 60)
print()
print("Note: Using fast demo settings (small slab, frozen, low ENCUT)")
print()
============================================================
Adsorption Energy Calculations
============================================================

Note: Using fast demo settings (small slab, frozen, low ENCUT)

Part 1: Reference calculations#

print("Part 1: Reference calculations")
print("-" * 40)
print()

# Gas-phase H atom
print("1a. Gas-phase H atom")
from ase import Atoms
h_atom = Atoms('H', positions=[[0, 0, 0]])
h_atom.center(vacuum=6.0)

calc_h = Vasp(
    label='results/adsorption/h_gas',
    atoms=h_atom,
    xc='PBE',
    encut=300,
    kpts=(1, 1, 1),
    ismear=0,
    sigma=0.05,
    ispin=2,
)

e_h = calc_h.potential_energy
print(f"  H energy: {e_h:.6f} eV")
print()

# Al bulk reference
print("1b. Al bulk reference")
al_bulk = bulk('Al', 'fcc', a=4.05)

calc_al_bulk = Vasp(
    label='results/adsorption/al_bulk',
    atoms=al_bulk,
    xc='PBE',
    encut=300,
    kpts=(10, 10, 10),
    ismear=1,
    sigma=0.1,
)

e_al_bulk = calc_al_bulk.potential_energy
e_al_per_atom = e_al_bulk / len(al_bulk)
print(f"  Al bulk energy per atom: {e_al_per_atom:.6f} eV")
print()
Part 1: Reference calculations
----------------------------------------

1a. Gas-phase H atom
  H energy: -1.111562 eV

1b. Al bulk reference
  Al bulk energy per atom: -3.744454 eV

Part 2: Clean Al(111) surface#

We use a small 2x2 slab with 3 layers, all frozen (no relaxation). This is much faster but less accurate than a relaxed surface.

print("Part 2: Clean Al(111) surface")
print("-" * 40)
print()

# Create small Al(111) slab - 2x2, 3 layers, 10 A vacuum
slab = fcc111('Al', size=(2, 2, 3), vacuum=10.0, a=4.05)

# Freeze ALL atoms - no surface relaxation for speed
constraint = FixAtoms(indices=range(len(slab)))
slab.set_constraint(constraint)

print("  Slab: Al(111) 2x2, 3 layers (all frozen)")
print(f"  Atoms: {len(slab)}")
print()

# Static calculation only (no relaxation needed - all frozen)
calc_slab = Vasp(
    label='results/adsorption/al111_clean',
    atoms=slab,
    xc='PBE',
    encut=300,
    kpts=(4, 4, 1),
    ismear=1,
    sigma=0.1,
    ldipol=True,
    idipol=3,
)

e_slab = calc_slab.potential_energy
print(f"  Clean slab energy: {e_slab:.6f} eV")
print()
Part 2: Clean Al(111) surface
----------------------------------------

  Slab: Al(111) 2x2, 3 layers (all frozen)
  Atoms: 12

  Clean slab energy: -37.903796 eV

Part 3: H adsorption at different sites#

We test two sites (ontop and fcc hollow) to demonstrate the workflow. Only the H atom position is relaxed; the slab remains frozen.

print("Part 3: H adsorption at different sites")
print("-" * 40)
print()

# Test just two sites for speed
adsorption_sites = ['ontop', 'fcc']
adsorption_results = {}

for site in adsorption_sites:
    print(f"  Calculating {site} site...")

    # Create fresh slab with H
    slab_h = fcc111('Al', size=(2, 2, 3), vacuum=10.0, a=4.05)

    # Add H atom at specified site
    add_adsorbate(slab_h, 'H', height=1.5, position=site)

    # Freeze slab atoms only (indices 0 to n_slab-1)
    # Let H (last atom) relax
    n_slab = 12  # 2x2x3 = 12 Al atoms
    constraint = FixAtoms(indices=range(n_slab))
    slab_h.set_constraint(constraint)

    calc_ads = Vasp(
        label=f'results/adsorption/al111_h_{site}',
        atoms=slab_h,
        xc='PBE',
        encut=300,
        kpts=(4, 4, 1),
        ismear=1,
        sigma=0.1,
        ldipol=True,
        idipol=3,
        ibrion=2,
        nsw=20,
        ediffg=-0.05,
    )

    e_ads = calc_ads.potential_energy

    # Calculate adsorption energy
    e_adsorption = e_ads - e_slab - e_h

    adsorption_results[site] = {
        'energy': e_ads,
        'e_ads': e_adsorption,
    }

    print(f"    Total energy: {e_ads:.6f} eV")
    print(f"    Adsorption energy: {e_adsorption:.3f} eV")
    print()
Part 3: H adsorption at different sites
----------------------------------------

  Calculating ontop site...
    Total energy: -44.989159 eV
    Adsorption energy: -5.974 eV

  Calculating fcc site...
    Total energy: -45.144757 eV
    Adsorption energy: -6.129 eV

Part 4: Analysis#

print("Part 4: Summary of adsorption energies")
print("-" * 40)
print()

print(f"{'Site':<10} {'E_ads (eV)':<12} {'Stability':<15}")
print("-" * 37)

# Sort by adsorption energy (more negative = more stable)
sorted_sites = sorted(adsorption_results.items(), key=lambda x: x[1]['e_ads'])

for i, (site, data) in enumerate(sorted_sites):
    stability = "Most stable" if i == 0 else f"dE = {data['e_ads'] - sorted_sites[0][1]['e_ads']:.3f} eV"
    print(f"{site:<10} {data['e_ads']:<12.3f} {stability:<15}")

print()
Part 4: Summary of adsorption energies
----------------------------------------

Site       E_ads (eV)   Stability      
-------------------------------------
fcc        -6.129       Most stable    
ontop      -5.974       dE = 0.156 eV  

Summary#

print("=" * 60)
print("Summary: H/Al(111) Adsorption")
print("=" * 60)
print()

most_stable = sorted_sites[0]
print(f"Most stable site: {most_stable[0]}")
print(f"Adsorption energy: {most_stable[1]['e_ads']:.3f} eV")
print()
print("Demo settings used (for speed):")
print("  - Small slab: 2x2, 3 layers")
print("  - Frozen substrate (no relaxation)")
print("  - Lower ENCUT (300 eV)")
print("  - Two sites only")
print()
print("For production calculations:")
print("  - Use 3x3 or 4x4 slab, 4+ layers")
print("  - Relax top 2 layers")
print("  - ENCUT 400+ eV")
print("  - Test all sites (ontop, bridge, fcc, hcp)")
print()
print("Key formula:")
print("  E_ads = E(slab+adsorbate) - E(slab) - E(adsorbate)")
print()
print("Next: Try 10_reactions/ for reaction energetics.")
============================================================
Summary: H/Al(111) Adsorption
============================================================

Most stable site: fcc
Adsorption energy: -6.129 eV

Demo settings used (for speed):
  - Small slab: 2x2, 3 layers
  - Frozen substrate (no relaxation)
  - Lower ENCUT (300 eV)
  - Two sites only

For production calculations:
  - Use 3x3 or 4x4 slab, 4+ layers
  - Relax top 2 layers
  - ENCUT 400+ eV
  - Test all sites (ontop, bridge, fcc, hcp)

Key formula:
  E_ads = E(slab+adsorbate) - E(slab) - E(adsorbate)

Next: Try 10_reactions/ for reaction energetics.