md-exafs provides the core scientific engine and command-line interface for calculating ensemble-averaged Extended X-ray Absorption Fine Structure (EXAFS) spectra from molecular dynamics trajectories.
The package sits at the base of the MD-EXAFS software ecosystem:
- MD-EXAFS Core and CLI (
md-exafs): Trajectory analysis, Debye–Waller extraction, FEFF card and input generation, path reconstruction, and streaming batch execution. - AiiDA-FEFF: AiiDA plugin for provenance tracking, multi-node HPC daemon scheduling and batch orchestration.
- AiiDAlab-EXAFS: Interactive browser interface inside Jupyter and AiiDAlab.
The package implements the physical and numerical routines required to bridge thermal configurations and experimental absorption spectra:
-
Exact EXAFS reconstruction: Evaluates the EXAFS equation for individual scattering paths using the complex electron momentum
$p = \mathrm{rep} + i/\lambda$ , matching Larch and FEFF conventions. -
Shard-and-stream batch execution: Runs FEFF in bounded chunks across available CPU cores, streams calculated spectra and per-path parameters directly into HDF5 shards (
batch_shard.h5), and prunes scratch directories immediately to keep disk usage within fixed bounds. -
Hybrid path storage: Stores raw 6-column FEFF scattering parameters on native grids for per-frame paths to permit post-hoc adjustment of
$S_0^2$ ,$\sigma^2$ , or$\Delta E_0$ , while pre-evaluating fine-grid$\chi(k)$ and$|\tilde{\chi}(R)|$ for the dominant composite ensemble paths. -
Debye–Waller and ADP extraction: Extracts path variances (
$\sigma^2$ ), third and fourth cumulants ($C_3, C_4$ ), autocorrelation sample corrections ($N_\text{eff}$ ), and atomic displacement parameter (ADP) CIF models directly from trajectories, with support for non-orthogonal periodic cells. -
Per-frame absorber resolution: Supports element labels, explicit index lists, and element-relative indexing (
"Cu:0,2"), validating chemical species consistency across static and permuted topologies. -
Experimental alignment: Imports beamline ASCII columns and Athena projects (
.prj), applying$\Delta E_0$ threshold shifts and$S_0^2$ scaling on common wavenumber grids. - Headless visualisation: Generates standard Altair spectral charts and 3D scattering vectors for WEAS-widget without framework dependencies.
git clone https://github.com/stfc/alc-dls-exafs.git
cd alc-dls-exafs
# Core engine (zero CLI or plotting dependencies)
pip install -e .
# With command-line interface (typer, rich, matplotlib)
pip install -e ".[cli]"
# Full installation including interactive notebooks (marimo, weas-widget, altair)
pip install -e ".[cli,notebooks]"Using uv:
uv pip install -e ".[cli,notebooks]"The CLI is available as md-exafs. The legacy alias larch-cli remains available for transition.
The pipeline command coordinates input generation, parallel FEFF execution, HDF5 archiving, and ensemble spectral averaging in a single run.
# Single structure (K-edge, first absorber site)
md-exafs pipeline structure.cif Cu
# Trajectory run: process all Cu sites across all frames into HDF5
md-exafs pipeline trajectory.xyz Cu --all-sites --all-frames --hdf5 --keep-paths
# Precompute potentials on the representative structure to accelerate trajectory runs
md-exafs pipeline trajectory.xyz Cu --all-sites --all-frames --hdf5 --reuse-potentials --parallel --workers 8
# Sub-sample trajectory frames (e.g. every 5th frame)
md-exafs pipeline trajectory.xyz Cu --ase-kwargs '{"index": "::5"}'The analyze command evaluates and plots simulated spectra and path contributions from an existing HDF5 archive (results.h5 or ensemble_results.h5):
# Plot ensemble average and path decomposition
md-exafs analyze results.h5 --plot-include average,paths --show
# Prune negligible paths (e.g. keep paths with at least 5% of peak amplitude)
md-exafs analyze results.h5 --plot-include average,paths --min-cw-ratio 5.0 --max-paths 20
# Compare against experimental data with an energy shift and amplitude factor
md-exafs analyze results.h5 --experimental measurement.dat --e0-shift 2.5 --s02 0.85Extract path variances (
# Calculate MSRD up to 5.0 Å from Cu absorbing sites
md-exafs debye-waller trajectory.xyz --prefix cu_dw --site-spec Cu --cutoff 5.0
# Export an average CIF structure with anisotropic thermal ellipsoids
md-exafs debye-waller trajectory.xyz --prefix cu_dw --adp-cif# Step 1: Generate FEFF inputs only
md-exafs generate structure.cif Cu --output feff_inputs/ --radius 6.0 --edge K
# Step 2: Run FEFF across generated input directories
md-exafs run-feff feff_inputs/ --parallel --workers 4
# Step 3: Analyse results
md-exafs analyze feff_inputs/ --output plots/ --plot-include sites --showThe md_exafs package can be imported directly in Python scripts and computational notebooks.
from ase.build import bulk
from md_exafs.feff_input import FeffConfig, build_feff_inp
atoms = bulk("Cu", "fcc", a=3.61)
config = FeffConfig.from_preset("quick")
config.radius = 5.5
feff_inp = build_feff_inp(atoms, config=config, absorber_idx=0)import numpy as np
from md_exafs.paths import path_chi
# Calculate chi(k) from raw 6-column FEFF scattering factors
k_out = np.linspace(2.0, 16.0, 141)
chi = path_chi(
k_native=k_coarse,
feff_data=feff_data_6cols,
r_eff=2.55,
degeneracy=12.0,
k_out=k_out,
sigma2=0.005,
s02=0.9,
e0_shift=1.5,
)from pathlib import Path
from md_exafs.execution import BatchExecutor, FeffTask, merge_shards
from md_exafs.hdf5 import ArchiveReader
tasks = [
FeffTask(frame_idx=0, site_idx=0, input_dir=Path("run_0000"), absorber_element="Cu"),
FeffTask(frame_idx=0, site_idx=1, input_dir=Path("run_0001"), absorber_element="Cu"),
]
# Run chunked execution into a self-contained shard
executor = BatchExecutor(tasks, output_h5="batch_shard_0.h5", chunk_size=128, n_workers=4)
executor.run()
# Merge multiple shards into an ensemble archive
ensemble_file = merge_shards(
shard_paths=["batch_shard_0.h5", "batch_shard_1.h5"],
ensemble_path="ensemble_results.h5",
)
# Inspect results through the unified archive reader
reader = ArchiveReader(ensemble_file)
print(f"Grand average chi points: {len(reader.chi)}")from ase.io import read
from md_exafs.debye_waller import calculate_grouped_msrd
trajectory = read("trajectory.xyz", index=":")
msrd_results = calculate_grouped_msrd(trajectory, site_spec="Cu", max_reff=5.0)
for path in msrd_results["paths"][:5]:
print(f"{path['label']}: R_eff = {path['r_eff']:.3f} Å, σ² = {path['sigma2']:.5f} Ų")- System architecture: Description of the shard-and-stream and hybrid storage models.
- CLI reference: Command documentation and parameter descriptions.
- Python API reference: Function and class signatures for library modules.
- Ecosystem integration: Details on coupling with
AiiDA-FEFFandAiiDAlab-EXAFS.
If you use md-exafs in your research, please cite:
@software{md_exafs,
author = {Kane Shenton and Joshua Elliott and Alin M. Elena},
title = {MD-EXAFS: Ensemble-Averaged EXAFS Spectroscopy from Molecular Dynamics Trajectories},
year = {2026},
publisher = {GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/stfc/alc-dls-exafs}}
}See CITATION.cff for full citation metadata.
This project is licensed under the BSD-3-Clause License. See LICENSE for details.
- Built on top of the excellent Larch project
- FEFF calculations powered by the FEFF Project. Specifically, the Open Source version of FEFF8 (FEFF8L) is used by default.
- Structure handling via ASE and pymatgen
- Trajectory-based ensemble analysis inspired by EDACA, which pioneered the software methodology and served as an invaluable reference during pipeline validation.