Python API reference#

The foundational library md_exafs provides pure domain functions, data structures, and mathematical routines for EXAFS analysis without framework dependencies.


1. md_exafs.constants#

Physical constants and unit conversion factors. Units throughout md_exafs are Ångström and electron-volt.

  • HBAR2_OVER_2M_EV_ANGSTROM2 (float): \(\hbar^2 / (2 m_e)\) in \(\text{eV}\cdot\text{\AA}^2 \approx 3.80998\). That is, the kinetic energy relation \(E = \frac{\hbar^2}{2m_e} k^2\).

  • ETOK (float): Reciprocal conversion factor, \(k^2 = \text{ETOK} \cdot E \approx 0.262468\text{ \AA}^{-2}\cdot\text{eV}^{-1}\).


2. md_exafs.selection#

Absorber selection and trajectory sampling algorithms (ADR 0008).

resolve_frame_absorbers#

def resolve_frame_absorbers(
    symbols: list[str],
    spec: str | int | list[int] | tuple[int, ...],
) -> list[int]:

Resolves absorber indices for a single configuration. Enforces single-species consistency, raising a ValueError if the selection encompasses more than one chemical element.

resolve_trajectory_absorbers#

def resolve_trajectory_absorbers(
    trajectory_symbols: list[list[str]],
    spec: str | int | list[int] | tuple[int, ...],
) -> list[list[int]]:

Resolves absorber indices per frame across a trajectory. Accommodates variable atom ordering and permuted topologies while asserting that the chemical species of the absorber remains invariant throughout the simulation.

parse_frame_slice#

def parse_frame_slice(
    slice_spec: str | int | slice | list[int],
    n_frames: int,
) -> list[int]:

Parses slice specifications (e.g. ":100:5", "all", or explicit lists) into 0-based integer frame indices bounded by n_frames.


3. md_exafs.debye_waller#

Trajectory-based extraction of vibrational disorder parameters.

calculate_grouped_msrd#

def calculate_grouped_msrd(
    structures: list[Any],
    site_spec: str = "0",
    max_reff: float = 6.0,
    r_bin: float = 0.15,
) -> dict[str, Any]:

Calculates Mean Squared Relative Displacements (MSRD, \(\sigma^2\)), third and fourth cumulants (\(C_3, C_4\)), and autocorrelation effective sample sizes (\(N_\text{eff}\)) for all scattering paths within max_reff.

compute_adp_results#

def compute_adp_results(structures: list[Any]) -> dict[str, Any]:

Calculates atomic displacement parameter (ADP) tensors \(U_{ij}\) and isotropic thermal \(B\)-factors for all sites in the structure.

save_cif_with_adp#

def save_cif_with_adp(results: dict[str, Any]) -> str:

Exports average atomic positions and anisotropic ADP tensors to a standardised crystallographic CIF string.


4. md_exafs.feff_input#

Card normalisation and feff.inp input generation (ADR 0004).

FeffConfig#

@dataclass
class FeffConfig:
    spectrum_type: str = "EXAFS"
    edge: str = "K"
    radius: float = 4.0
    exclude_hydrogen: bool = False
    control: Any | None = None
    print: Any | None = "1 0 0 0 0 3"
    s02: Any | None = 1.0
    scf: Any | None = None
    exchange: Any | None = 0
    nleg: Any | None = 6
    kmin: float = 2.0
    kmax: float = 12.0
    kweight: int = 2
    dk: float = 1.0

Dataclass holding all card parameters for FEFF8L and Fourier transform post-processing. Can be initialised from bundled presets (quick, nscf, publication) via FeffConfig.from_preset().

build_feff_inp#

def build_feff_inp(
    structure: Atoms | Structure,
    config: FeffConfig | None = None,
    absorber_idx: int = 0,
    output_path: Path | str | None = None,
) -> str:

Generates the text of feff.inp from an ASE Atoms or pymatgen Structure. Prunes COREHOLE cards to ensure compatibility with FEFF8L.


5. md_exafs.potentials#

Precomputation and distribution of self-consistent field potentials (ADR 0006).

PotentialsManager#

  • MANIFEST: Canonical tuple of FEFF8L potential files (phase.pad, pot.pad, and associated JSON tables).

  • precompute(structure, absorber_idx, config, output_dir, feff_executable="feff") -> Path: Computes potentials once on a representative structure (CONTROL 1 1 1 0 0 0).

  • distribute(potentials_dir, target_dirs, mode="symlink", n_workers=8) -> int: Links or copies precomputed potentials into worker task directories.


6. md_exafs.paths#

Scattering path extraction and EXAFS equation evaluation (ADR 0003).

path_chi#

def path_chi(
    k_native: np.ndarray,
    feff_data: np.ndarray,
    r_eff: float,
    degeneracy: float,
    k_out: np.ndarray,
    *,
    sigma2: float = 0.0,
    s02: float = 1.0,
    e0_shift: float = 0.0,
    deltar: float = 0.0,
) -> np.ndarray:

Evaluates a single path’s fine-structure oscillation \(\chi(k)\) on k_out using the complex electron momentum \(p = \mathrm{rep} + i/\lambda\) and cubic spline interpolation matching Larch.

read_paths_from_dir#

def read_paths_from_dir(
    feff_dir: Path | str,
    max_paths: int | None = None,
    threshold: float = 0.0,
    frame_idx: int = 0,
    site_idx: int = 0,
) -> list[PathResult]:

Parses files.dat and feffNNNN.dat in a calculation directory into a list of PathResult objects, filtering out paths below threshold.


7. md_exafs.spectra#

Fourier transformation and ensemble averaging.

xftf_arrays#

def xftf_arrays(
    k: np.ndarray,
    chi: np.ndarray,
    ft_params: dict[str, Any] | None = None,
) -> dict[str, Any]:

Transforms \(\chi(k) \to \chi(R)\) using Larch, returning a dictionary containing r, magnitude chir_mag, real component chir_re, imaginary component chir_im, and the resolved parameters ft_params.

average_chi_arrays#

def average_chi_arrays(
    k_list: list[np.ndarray],
    chi_list: list[np.ndarray],
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:

Averages multiple \(\chi(k)\) spectra onto a common reference grid with NaN-aware statistics. Returns (k_ref, mean_chi, std_chi) where std_chi is the sample standard deviation (\(N-1\) degrees of freedom).


8. md_exafs.experimental#

Experimental spectrum loading and alignment (ADR 0009).

  • shifted_k_mask(k: np.ndarray, e0_shift: float) -> np.ndarray: Mask identifying wavenumber points that remain real under a \(\Delta E_0\) threshold shift.

  • scaled_chi_arrays(k: np.ndarray, chi: np.ndarray, s02: float = 1.0, e0_shift: float = 0.0) -> tuple[np.ndarray, np.ndarray]: Applies \(S_0^2\) amplitude scaling and \(\Delta E_0\) threshold shifts, dropping points below the shifted threshold.

  • read_experimental_spectrum(path, group=None, autobk=True) -> tuple[np.ndarray, np.ndarray, dict]: Imports multi-column ASCII or Athena project (.prj) files.


9. md_exafs.hdf5#

HDF5 archive storage management (ADR 0002, ADR 0003, ADR 0004).

  • BatchShardWriter(output_path, k_grid, threshold=0.0): Writes single-batch calculation shards (batch_shard.h5).

  • EnsembleWriter(output_path, k_grid, fourier_params=None): Writes consolidated trajectory ensemble archives (ensemble_results.h5).

  • ArchiveReader(path): High-level reader providing direct property access to .k, .chi, .r, .chir_mag, .iter_paths(), .get_site_average(), and .get_frame_average() across both shard and ensemble archives.


10. md_exafs.execution#

Streaming execution engine.

  • FeffTask(frame_idx, site_idx, input_dir, absorber_element): Atomic execution task descriptor.

  • BatchExecutor(tasks, output_h5, chunk_size=256, n_workers=None, ...): Bounded-memory streaming batch runner.

  • merge_shards(shard_paths, ensemble_path, fourier_params=None, top_n_paths=25) -> Path: Consolidates \(K\) shard files into an ensemble archive.


11. md_exafs.viz#

Headless visualisation builders (ADR 0007).

  • group_path_results(paths, r_bin_width=0.1) -> list[dict]: Groups scattering paths by \((scatterer, nlegs, r_{bin})\) and averages their properties.

  • build_chi_chart(df, title="χ(k)") -> alt.Chart: Altair line chart generator for wavenumber spectra.

  • build_chir_chart(df, title="|χ(R)|") -> alt.Chart: Altair line chart generator for radial Fourier transforms.

  • build_sigma2_chart(df, title="σ² vs R_eff") -> alt.Chart: Altair scatter chart generator for MSRD disorder curves.

  • calculate_path_vectors(positions, path_atom_indices) -> list[dict]: Computes 3D arrow vectors for WEAS-widget geometry overlays.