Source code for mag4.pipeline

"""High-level CIF analysis pipeline (loads structure, prints distance tables, returns crystal + couplings)."""

from __future__ import annotations

import logging
from typing import Optional, Sequence

from pymatgen.core import Structure

from mag4.constants import DIST_EQUIV_TOL, CrystalData
from mag4.io_cif import (
    crystal_from_cif,
    get_mm_distances,
    group_inequivalent,
)
from mag4.reports import print_inequivalent, print_mm_distances

log = logging.getLogger(__name__)


[docs] def run_cif_analysis( cif_path: str, element: str, cutoff: float, tol: float = DIST_EQUIV_TOL, *, ligand_elements: Optional[Sequence[str]] = None, bond_cutoff: float = 2.5, angle_tol: float = 1.0, ll_cutoff: float = 0.0, signed_dihedral: bool = False, ) -> tuple[CrystalData, list]: """Print distance / J tables and return ``(crystal, couplings)``. ``couplings`` is a list of items that can be fed straight into :func:`mag4.dimers.find_dimers`: * Without ``ligand_elements``: ``[[label, distance], ...]`` (legacy form, distance is the only discriminator). * With ``ligand_elements`` set: ``[[label, distance, fingerprint], ...]`` where ``fingerprint`` is an :data:`mag4.geometry.AngleFingerprint` carrying the M–L–M bridging geometry — used by ``find_dimers`` as a secondary discriminator so that pairs at the same distance but different bridging angles end up in different J shells (``J1a``, ``J1b``, …). """ print(f"\nLoading structure from CIF: {cif_path}") structure = Structure.from_file(cif_path) print(f" Formula : {structure.composition.reduced_formula}") print(f" Space grp: {structure.get_space_group_info()}") a, b, c = structure.lattice.a, structure.lattice.b, structure.lattice.c al, be, ga = (structure.lattice.alpha, structure.lattice.beta, structure.lattice.gamma) print(f" Lattice : a={a:.4f} b={b:.4f} c={c:.4f} Å") print(f" α={al:.3f}° β={be:.3f}° γ={ga:.3f}°") # Formula units per primitive cell — used to normalise the multiplicity # column ("dimers per formula unit") in the J shell table. _reduced_formula, z_formula_units = structure.composition.get_reduced_formula_and_factor() results = get_mm_distances(cif_path, element, cutoff) print_mm_distances(results, element, cutoff, structure) if ligand_elements: # Geometry-aware grouping: distance THEN bridging-angle fingerprint. from mag4.geometry import group_inequivalent_geom groups = group_inequivalent_geom( results, structure, ligand_elements=ligand_elements, bond_cutoff=bond_cutoff, dist_tol=tol, angle_tol=angle_tol, ll_cutoff=ll_cutoff, signed_dihedral=signed_dihedral, ) print_inequivalent(groups, element, tol, ligand_elements=ligand_elements, bond_cutoff=bond_cutoff, angle_tol=angle_tol, ll_cutoff=ll_cutoff, z_formula_units=int(z_formula_units)) couplings = [[g["label"], g["distance"], g["fingerprint"]] for g in groups] else: groups = group_inequivalent(results, tol) print_inequivalent(groups, element, tol, z_formula_units=int(z_formula_units)) couplings = [[g["label"], g["distance"]] for g in groups] crystal = crystal_from_cif(cif_path, element) return crystal, couplings