Skip to main content

Adsorption

Conda Version Conda Downloads Pypi version PyPI Downloads

A Python package for simulating molecular adsorption on clusters or surfaces, built on top of ASE (Atomic Simulation Environment).

Overview

The adsorption package provides tools for placing and optimizing adsorbates (molecules or atoms) on surface or cluster substrates. It automates the process of:

  • Generating adsorption sites on surfaces/clusters
  • Placing adsorbates at optimal positions and orientations
  • Performing geometry optimization using two-stage relaxation
  • Running transition state searches (dimer and NEB methods)
  • Running high-throughput adsorption studies with Ray Tune

Features

  • Multiple Adsorption Strategies:

    • RawAdsorption: Places adsorbates based on geometric site analysis (top, bridge, hollow sites)
    • DirectAdsorption: Uses Fibonacci lattice sampling for comprehensive orientation exploration
    • DirectAdsorptionAD: Extends DirectAdsorption with NequIP-based torch autodiff optimization for GPU-accelerated relaxation
  • Flexible Adsorbate Input:

    • ASE Atoms objects
    • Chemical symbols (e.g., "O", "C")
    • Molecule names recognized by ASE (e.g., "H2O", "CO")
    • SMILES strings (e.g., "C=C-C-C-C-C")
  • Two-Stage Optimization:

    1. First stage: Fixed substrate + bond length constraints on adsorbate
    2. Second stage: Full relaxation of the entire system
  • Transition State Search:

    • Dimer method (call_dimer) for saddle point searches
    • NEB / DyNEB method (call_neb) for minimum energy path searches
    • Built-in trajectory recording with per-step energy snapshots
  • High-Throughput Screening:

    • Integration with Ray Tune for parallel optimization
    • Automatic grid generation for systematic adsorption site exploration
    • Unified Helper interface for iterating over both raw and direct adsorption paths

Installation

pip install adsorption

Or using pixi:

pixi install

Quick Start

Basic Usage

from ase.cluster import Octahedron
from adsorption import RawAdsorption, DirectAdsorption

# Create a copper cluster
cluster = Octahedron("Cu", 10)

# Method 1: RawAdsorption (geometric site-based)
ads = RawAdsorption(calculator=None)  # Replace with actual calculator
result, stage = ads(
    atoms=cluster,
    adsorbate="CO",
    core=[303, 334, 464],  # FCC hollow site
)

# Method 2: DirectAdsorption (Fibonacci lattice sampling)
ads = DirectAdsorption(calculator=None, nfibonacci=100)
result, stage = ads(
    atoms=cluster,
    adsorbate="C6H6",  # Benzene
    core=[454],  # Top site
)

NequIP-Based Autodiff Optimization

from ase.cluster import Octahedron
from adsorption.interfaces import DirectAdsorptionAD
from nequip.ase import NequIPCalculator

cluster = Octahedron("Cu", 10)
calc = NequIPCalculator.from_compiled_model("path/to/model.pth")

ads = DirectAdsorptionAD(calculator=calc, nfibonacci=100)
result, stage = ads(atoms=cluster, adsorbate="CO", core=[454])

Unified Helper Interface

from ase.cluster import Octahedron
from adsorption.interfaces import Helper
from ase.calculators.emt import EMT

cluster = Octahedron("Cu", 10)

helper = Helper(
    calculator=EMT(),
    atoms=cluster,
    adsorbate="CO",
    core=[454],
    use_direct=True,
    use_raw=True,
)

# Run raw adsorption path
result = helper(irun=0, outdir="./results")

# Run direct adsorption path (iterates over orientation grids)
for i in range(1, helper.nrun):
    result = helper(irun=i, outdir="./results")
from ase.build import fcc100
from adsorption.common.optimize import call_dimer, call_neb
from ase.calculators.emt import EMT

# Create a slab
slab = fcc100("Cu", size=(3, 3, 3), vacuum=10)

# Dimer method for saddle point search
trajectory, converged = call_dimer(
    slab, EMT(), displacement=..., max_steps=1000, fmax=0.02
)

# NEB method for minimum energy path
initial = slab.copy()
final = slab.copy()
# ... set up initial and final states ...
trajectory, converged = call_neb(
    initial, EMT(), final, nimages=5, climb=True
)

Command Line Interface

The package provides a CLI tool for running high-throughput adsorption studies:

adsorption-tune -cn config.yaml

Example configuration (config.yaml):

output: ./outputs
calculator:
  _target_: ase.calculators.emt.EMT
adsorption:
  nfibonacci: 100
  max_steps_for_first_stage: 100
  max_steps_for_second_stage: 100
  max_force: 0.05
system:
  atoms:
    _target_: ase.build.fcc111
    symbol: Cu
    size: [10, 10, 5]
    vacuum: 15
  core: [454]
gas: C6H6

Architecture

Core Components

src/adsorption/
├── common/
│   ├── __init__.py       # Exports AdsorptionABC, Point, Vector, Site
│   ├── _interface.py     # Abstract base class (AdsorptionABC)
│   ├── _dataclass.py     # Data structures (Point, Vector, Site)
│   └── optimize.py       # optimize(), call_dimer(), call_neb()
├── interfaces/
│   ├── __init__.py       # Exports RawAdsorption, DirectAdsorption, DirectAdsorptionAD
│   ├── _raw.py           # RawAdsorption implementation
│   ├── _direct.py        # DirectAdsorption implementation
│   ├── _directAD.py      # DirectAdsorptionAD (NequIP + torch autodiff)
│   └── helper.py         # Helper class + plot() visualization
└── runner/
    ├── _cli.py           # Hydra-based CLI (adsorption-tune entry point)
    ├── _cli.yaml         # Default Hydra configuration
    └── _tune.py          # Ray Tune integration

Key Classes

AdsorptionABC (Abstract Base Class)

The abstract base class that defines the common interface and optimization workflow:

  • Two-stage optimization:

    • Stage 1: Fixes substrate atoms, applies bond length constraints to adsorbate
    • Stage 2: Full relaxation without constraints
  • Adsorbate parsing: Converts various input types (string, Atom, Atoms, Gas) to ASE Atoms

RawAdsorption

Places adsorbates based on geometric analysis of adsorption sites:

  • Automatically identifies neighbor atoms around the core site
  • Calculates optimal adsorption direction based on site geometry
  • Supports top, bridge, and hollow sites

Parameters:

  • adsorbate_index: Index of anchoring atom in adsorbate (or "com" for center of mass)
  • nbr1hop: First-neighbor shell indices (auto-detected if not provided)
  • core: Core atom indices defining the adsorption site

DirectAdsorption

Uses Fibonacci lattice sampling for comprehensive orientation exploration:

  • Generates uniform sampling points on a sphere
  • Supports custom grid definitions for both adsorbate and substrate
  • Ideal for high-throughput screening

Parameters:

  • nfibonacci: Number of Fibonacci lattice points (default: 1000)
  • grid_core: Custom grid for substrate orientations
  • grid_ads: Custom grid for adsorbate orientations
  • distance: Adsorbate-substrate distance

DirectAdsorptionAD

Extends DirectAdsorption with NequIP-based torch autodiff optimization:

  • Requires a NequIPCalculator instance
  • Uses torch.optim.LBFGS for GPU-accelerated relaxation
  • Optimizes distance, quaternion rotations via autodiff gradients
  • Early stopping with configurable patience and tolerance

Helper

Unified interface that wraps both RawAdsorption and DirectAdsorption:

  • Initializes grids and configurations for both adsorption paths
  • irun=0: Runs the raw adsorption path
  • irun>=1: Runs the direct adsorption path (iterates over orientation x distance grids)
  • Returns structured results with energy, convergence stage, and structure

Data Structures

All defined in common/_dataclass.py as Pydantic models:

  • Point: 3D point with arithmetic operations
  • Vector: 3D vector with length and normalize properties
  • Site: Adsorption site with neighbor, core, computed center and direction

Optimization Utilities

common/optimize.py provides:

  • optimize(atoms, calc, method, max_steps, fmax): General ASE structure optimization with trajectory recording. Returns (trajectory, converged).
  • call_dimer(atoms, calc, displacement, mask, max_steps, fmax): Dimer method for transition state search. Returns (trajectory, converged).
  • call_neb(atoms, calc, final_atoms, nimages, climb, ...): DyNEB method for minimum energy path search. Returns (trajectory, converged).

Examples

Example 1: Benzene on Cu(111) Surface

from ase.build import fcc111
from adsorption import DirectAdsorption
from ase.calculators.emt import EMT

# Create Cu(111) surface
surface = fcc111("Cu", size=(10, 10, 5), vacuum=15, orthogonal=True)

# Initialize adsorption calculator
ads = DirectAdsorption(
    calculator=EMT(),
    nfibonacci=100,
    max_steps_for_first_stage=100,
    max_steps_for_second_stage=100,
)

# Place benzene on surface
result, stage = ads(
    atoms=surface,
    adsorbate="C6H6",
    core=[454],  # Surface site index
)

Example 2: CO on Copper Cluster

from ase.cluster import Octahedron
from adsorption import RawAdsorption

# Create octahedral Cu cluster
cluster = Octahedron("Cu", 10)

# Place CO on FCC hollow site
ads = RawAdsorption(calculator=None)
result, stage = ads(
    atoms=cluster,
    adsorbate="CO",
    core=[303, 334, 464],  # FCC hollow site
    adsorbate_index=0,  # Anchor on carbon atom
)

High-Throughput Screening

The tune_adsorption function enables parallel exploration of multiple adsorption configurations:

from pathlib import Path
from adsorption.runner._tune import tune_adsorption
from ase.build import fcc111

surface = fcc111("Cu", size=(10, 10, 5), vacuum=15)

results = tune_adsorption(
    cfg=config,  # DictConfig with calculator and adsorption settings
    atoms=surface,
    adsorbate="C6H6",
    core=[454],
    nsamples=100,  # Number of random samples
    output=Path("./results"),
)

Results are saved as:

  • .xyz files with energy and optimization stage in filename
  • .png visualization files with multiple viewing angles

Dependencies

  • ASE: Atomic Simulation Environment for atom manipulation
  • graphatoms: Graph-based atom system utilities
  • Ray[default, tune]: Distributed hyperparameter optimization
  • Hydra-core: Configuration management
  • typing-extensions: Type hints support
  • NequIP (optional): Neural network interatomic potential for DirectAdsorptionAD

Development

Running Tests

pixi run test
# or
pytest -s -vv

Code Style

The project uses:

  • Ruff for linting and formatting
  • Google-style docstrings
  • Line length: 80 characters

License

GPL-3.0-or-later

Author

LiuGaoyong (liugaoyong_88@163.com)

Metadata

Release files for adsorption 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for adsorption 0.1.2
File Size Uploaded
adsorption-0.1.2.tar.gz 21.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for adsorption 0.1.2
File Interpreter ABI Platform
adsorption-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 49.0 kB

Release files / adsorption-0.1.2.tar.gz

Download URL adsorption-0.1.2.tar.gz
Size 21.4 kB
Tags Source
SHA-256 checksum
How to use checksums
fef43f2a06cbe4d4ba2ec60f055fb499915cd0907c748617221c81f957f3d29a
BLAKE2b-256 checksum
How to use checksums
792679fc2b516406efa5902e16953912939e1a0a8fa3512c9d600dc75a8935ba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 11, 2026.

Transparency log

Release files / adsorption-0.1.2-py3-none-any.whl

Download URL adsorption-0.1.2-py3-none-any.whl
Size 27.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f1b34d314fd10a17e0e558fa6f430910a965d72b131cd90f2bb575ba2f70ace5
BLAKE2b-256 checksum
How to use checksums
16cc7f6cce9a6162e4d7732d3c9fb9e61c17c20305a72820a0f6865b66e405d0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 11, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page