mag4.geometry

Geometric fingerprints for J-shell discrimination via M–X–M bond angles.

The four-state method needs each magnetic coupling to correspond to a unique physical exchange path. Grouping pairs by distance alone lumps together pairs that share the same M–M distance but route their super-exchange through different bridging ligands at different M–L–M angles — physically distinct couplings the four-state method would conflate into a single J.

This module adds a secondary discriminator derived from the bridging-ligand geometry of each pair: a quantised, sorted tuple of M–L–M angles. Pairs are clustered first by distance, then sub-clustered by fingerprint. Resulting sub-shells inherit their parent’s index plus a letter suffix (J1a, J1b, …).

The fingerprint computation reuses mag4.analysis.bond_angles.find_bridging_angles(); no new physics is introduced.

Module Attributes

AngleFingerprint

A geometric fingerprint for an M–M pair, packed as a 4-tuple:

reduce_to_primitive(crystal, group, *[, ...])

Backwards-compatible alias.

Functions

compute_pair_fingerprint(structure, site_i, ...)

Geometric fingerprint of one M–M pair.

crystal_to_pymatgen(crystal)

Build a pymatgen.core.Structure from a CrystalData.

filter_symops_by_fingerprint(sym_ops, ...[, ...])

Drop magnetic-sublattice symmetry ops that disagree with the bridging environment.

group_inequivalent_geom(results, structure, ...)

Cluster M–M pairs by (distance, angle fingerprint).

quantise_angles(angles, angle_tol)

Bin a sequence of angles to integer multiples of angle_tol.

reduce_to_primitive(crystal, group, *[, ...])

Backwards-compatible alias.

reduce_to_wien2k_cell(crystal, group, *[, ...])

Reduce a CrystalData to the standardized conventional cell of its spin-decorated (colored) structure — the setting WIEN2k's init_lapw pipeline expects.

mag4.geometry.AngleFingerprint

A geometric fingerprint for an M–M pair, packed as a 4-tuple:

``(n_bridges, sorted bridge-angle bins,

n_dihedrals, sorted signed dihedral bins)``

  • n_bridges / bridge bins describe the direct M–L–M paths.

  • n_dihedrals / dihedral bins describe the M–L–L–M super-super-exchange paths through two distinct ligands joined by an L–L contact within ll_cutoff.

The dihedral component is populated only when n_bridges == 0 and ll_cutoff > 0 — direct-bridge pairs keep (0, ()) for the dihedral slot so they are not over-split by what is, for them, a redundant secondary signal.

alias of Tuple[int, Tuple[int, …], int, Tuple[int, …]]

mag4.geometry.quantise_angles(angles, angle_tol)[source]

Bin a sequence of angles to integer multiples of angle_tol.

Returns a sorted tuple so equal fingerprints compare equal regardless of the input order. Empty input yields the empty tuple.

Return type:

Tuple[int, ...]

Parameters:
mag4.geometry.compute_pair_fingerprint(structure, site_i, site_j, image, ligand_elements, bond_cutoff, angle_tol, *, ll_cutoff=0.0, signed_dihedral=False)[source]

Geometric fingerprint of one M–M pair.

Parameters:
  • structure (pymatgen.core.Structure) – The cell containing the pair (primitive or supercell — both work).

  • site_i (int) – Indices of the two magnetic atoms in structure.

  • site_j (int) – Indices of the two magnetic atoms in structure.

  • image (tuple of 3 int) – Periodic image of site_j relative to site_i. Use (0, 0, 0) for direct intra-supercell pairs.

  • ligand_elements (sequence of str) – Element symbols treated as bridging ligands (e.g. ["O"]).

  • bond_cutoff (float) – Maximum M–L distance (Å) for a ligand to be considered bonded.

  • angle_tol (float) – Tolerance (°) for quantising both M–L–M angles and M–L–L–M dihedrals into integer bins.

  • ll_cutoff (float) – Maximum L–L distance (Å) for a pair of distinct ligands to be counted as a super-super-exchange bridge. Used only when the pair has no direct M–L–M bridge (n_bridges == 0). Set to 0 to disable dihedral analysis.

  • signed_dihedral (bool) – Keep the IUPAC sign of each M–L–L–M dihedral when True — this distinguishes mirror-image (chiral) paths and is appropriate when Dzyaloshinskii–Moriya / antisymmetric exchange matters. Default False folds every dihedral to |θ| in [0°, 180°] so chiral counterparts are merged, which is the right choice for plain Heisenberg J.

Returns:

4-tuple (n_bridges, bridge bins, n_dihedrals, dihedral bins). For pairs with a direct bridge the dihedral slot is (0, ()) — selective fallback so bridged shells aren’t over-split.

Return type:

AngleFingerprint

mag4.geometry.group_inequivalent_geom(results, structure, *, ligand_elements, bond_cutoff=2.5, dist_tol=0.01, angle_tol=1.0, ll_cutoff=0.0, signed_dihedral=False)[source]

Cluster M–M pairs by (distance, angle fingerprint).

First-level clustering by distance is identical to mag4.io_cif.group_inequivalent(). Within each distance shell, pairs are then sub-grouped by their bridging-ligand fingerprint. Sub-shells take suffix letters (J1a, J1b, …) when a split occurs; singletons keep the plain J1 label.

Each output dict carries an extra fingerprint field (AngleFingerprint) and a fingerprint_str field (human-readable summary).

Return type:

list

Parameters:
mag4.geometry.filter_symops_by_fingerprint(sym_ops, structure, n_mag, *, ligand_elements, bond_cutoff, angle_tol, ll_cutoff=0.0, signed_dihedral=False)[source]

Drop magnetic-sublattice symmetry ops that disagree with the bridging environment.

The default mag4.symmetry.find_symmetry_ops() considers only the magnetic sites: two pairs related by such an op are considered equivalent even if their bridging ligands sit very differently in space. When that happens, mag4.spinconfig.find_unique_configs() merges configurations from physically distinct sub-shells (e.g. J1a vs J1b) and the extraction formulas degenerate.

This filter keeps an op only when the permutation maps every M–M pair onto another pair with the same bridging fingerprint — i.e. the op is a symmetry of the decorated magnetic structure, not just of the bare sublattice. Fingerprints are computed once per pair and cached.

Return type:

List[Tuple[int, ...]]

Parameters:
mag4.geometry.reduce_to_wien2k_cell(crystal, group, *, spin_magnitude=1.0, symprec=0.001)[source]

Reduce a CrystalData to the standardized conventional cell of its spin-decorated (colored) structure — the setting WIEN2k’s init_lapw pipeline expects.

Uses spglib.standardize_cell(no_idealize=False, to_primitive=False) on the same colored (lattice, frac_coords, atom_types) tuple that analyze_config_symmetry() already builds.

  • to_primitive=False — return the conventional cell, not the primitive cell. WIEN2k’s lattice-type letters (F, I, R, …) are defined relative to the conventional cell of the parent space group; for F-centered cubic groups like Fm-3m the primitive cell is rhombohedral with 60° angles, which WIEN2k’s sgroup immediately rewrites to the cubic conventional setting — yielding a sym-op mismatch with whatever mag4 had written. Emitting the conventional cell directly avoids that round trip.

  • no_idealize=False — apply spglib’s origin shift so the cell sits at the canonical origin of its space group (4/m m m at the corner for P4/mmm, etc.). Without this, sgroup complains “Wrong multiplicity for atom 1” / “Struct file is not consistent” because the orbit-grouping mag4 reports (computed in spglib’s canonical setting) does not match the atom positions (kept in mag4’s input setting). Idealizing the cell aligns positions and orbits in one go.

For primitive groups (Pm-3m, P4/mmm, …) the conventional and primitive cells coincide; only the origin shift is non-trivial.

Returns a brand-new CrystalData whose cell, lattice_vectors, all_species, target_species, nb_species, total_atoms and elements describe the reduced cell. Atom labels mirror the crystal.elements order (Ni1, Ni2, …) re-numbered inside the smaller cell.

Falls back to the input crystal when spglib refuses to reduce and would not shift the origin either (typically: cell already in canonical setting and primitive). The fallback path emits a single debug log entry and never raises.

Return type:

CrystalData

Parameters:
mag4.geometry.reduce_to_primitive(crystal, group, *, spin_magnitude=1.0, symprec=0.001)

Backwards-compatible alias. The function used to return the rhombohedral primitive for F-centered groups (which WIEN2k’s init_lapw rejected), it now returns the conventional cell. The old name is preserved so existing imports keep working.

Return type:

CrystalData

Parameters:
mag4.geometry.crystal_to_pymatgen(crystal)[source]

Build a pymatgen.core.Structure from a CrystalData.

Magnetic atoms (crystal.target_species) are placed first in the structure so that their mag4 supercell indices coincide with their pymatgen indices. This makes it trivial for find_dimers() to look up bridging-angle fingerprints by passing the same integer index it already uses internally.

Parameters:

crystal (CrystalData)