crystod.mag#

Public magnetism API of CrystOD: the crystod-mag domain.

This module covers the symmetry analysis of spin arrangements. The spins on the sites of one element are treated as axial-vector degrees of freedom, the 3N-dimensional spin space is decomposed into irreps of the space group at a q point, and the resulting symmetry-adapted spin bases (cluster multipoles / symmetry-adapted multipole moments, SAMM, after M.-T. Suzuki et al., Phys. Rev. B 95, 094406 (2017) and Phys. Rev. B 99, 174407 (2019)) are classified as ferromagnetic (cluster dipole) or antiferromagnetic (octupole and higher). It mirrors the crystod-mag command, whose spin listings, VESTA exports and noncollinear magnetization input (VASP MAGMOM, Quantum ESPRESSO starting_magnetization/angle1/angle2) are built from these functions; the implementation lives in crystod.spin_basis.

Spin representation (crystod-mag --qpoint, and the survey over all special k points when --qpoint is omitted)

  • get_spin_representation: the little-group irreps at q and the axial-vector representation on the selected sites, ready for spgrep.representation.project_to_irrep.

Multipole classification (crystod-mag at q = 0)

  • get_multipole_rank_lists: for every irrep, the ranks of the magnetic multipoles (dipole, octupole, …) in which it appears.

  • separate_ferro_combination: splits the net-moment combination (FM, cluster dipole) off the projected spaces of one irrep; the rest are AFM.

  • MULTIPOLE_NAMES: rank-to-name table (1: "dipole", 3: "octupole", …).

A session mirrors the command:

from phonopy.interface.vasp import read_vasp
from spgrep.representation import project_to_irrep
from crystod import mag, phonon

vib = phonon.SymmetryOnlyVibrations(read_vasp("POSCAR"), standardize=False)
sites = [i for i, s in enumerate(vib.primitive_cell.symbols) if s == "Ni"]
q = [0, 0, 0]
irreps, spin_rep, mapping = mag.get_spin_representation(vib, sites, q)
labels = vib.get_irrep_labels(q, irreps, mapping)
ranks = mag.get_multipole_rank_lists(vib, vib.rotations[mapping], irreps)
for irrep, label, rank_list in zip(irreps, labels, ranks):
    spaces = [s.real for s in project_to_irrep(spin_rep, irrep)]
    if spaces:
        ferro, afm = mag.separate_ferro_combination(spaces, spin_rep, len(sites))

Attributes resolve lazily (PEP 562): importing this module is instant, and phonopy/spgrep are loaded on the first attribute access. Bad input raises ValueError here, where the implementation module raises SystemExit.

crystod.mag.get_multipole_rank_lists(vibrations, little_rotations, irreps, max_rank=9)[source]#

Ranks at which each irrep occurs in the magnetic multipole representations.

crystod-mag uses these lists at q = 0 to name the symmetry-adapted spin bases: the spaces of an irrep are named in the order of the ranks in its list, so the ferromagnetic combination (cluster dipole, rank 1) comes first and the antiferromagnetic ones follow as octupole, … (the logic of Table III of M.-T. Suzuki et al., Phys. Rev. B 95, 094406). The rank-p magnetic (time-odd, axial) multipole transforms as the angular-momentum- p representation with parity (-1)**(p + 1) under inversion: the dipole (p = 1) and octupole (p = 3) are parity-even, the magnetic quadrupole (p = 2) is parity-odd, and so on. The multiplicity of every irrep is obtained by character orthogonality against the rotation characters sin((p + 1/2) theta) / sin(theta / 2) of the proper part of every little-group operation.

Parameters:
  • vibrations (SymmetryOnlyVibrations) – Symmetry container of the primitive cell (crystod.phonon.SymmetryOnlyVibrations); its lattice converts the integer rotations to Cartesian form.

  • little_rotations (ndarray[tuple[Any, ...], dtype[int64]]) – Integer rotation matrices of the little-group operations, in the order of the irrep matrices: normally vibrations.rotations[mapping] with the mapping returned by get_spin_representation.

  • irreps – The spgrep irreps of the little group, as returned by get_spin_representation.

  • max_rank (int) – Highest rank p included in the search.

Returns:

One list per irrep (same order as irreps), holding the ranks 1 <= p <= max_rank at which the irrep appears in the rank-p magnetic multipole representation, in increasing order and repeated according to the multiplicity. MULTIPOLE_NAMES translates ranks to names.

Return type:

list[list[int]]

Example

>>> # vib, sites, q: see get_spin_representation (O sites of SrTiO3)
>>> irreps, spin_rep, mapping = mag.get_spin_representation(vib, sites, q)
>>> labels = vib.get_irrep_labels(q, irreps, mapping)
>>> rotations = vib.rotations[mapping]
>>> ranks = mag.get_multipole_rank_lists(vib, rotations, irreps)
>>> dict(zip(labels, ranks))["GM4+(3)"]
[1, 3, 5, 5, 7, 7, 9, 9, 9]
crystod.mag.get_spin_representation(vibrations, site_indices, qpoint)[source]#

Irreps and the axial-vector (spin) representation on selected sites at q.

This is the first step of crystod-mag (at the --qpoint given, or at every special k point in survey mode): the spins of the site_indices atoms of the primitive cell are treated as axial vectors, and the little group of qpoint acts on the 3N-dimensional spin space by the same construction as the vibration (polar-vector) representation of crystod-phonon, with the Cartesian rotation R replaced by det(R) * R and the site permutation restricted to the selected sites. The Bloch-phase convention is that of spgrep, so the returned matrices can be projected onto the returned irreps directly with spgrep.representation.project_to_irrep.

Parameters:
  • vibrations (SymmetryOnlyVibrations) – Symmetry container of the primitive cell (crystod.phonon.SymmetryOnlyVibrations); its rotations, translations and primitive_cell define the space group.

  • site_indices (list[int]) – Indices (into vibrations.primitive_cell) of the N atoms that carry a spin, e.g. every atom of the magnetic element.

  • qpoint (list[float]) – The q point in primitive reciprocal coordinates, e.g. [0, 0, 0] or [0.5, 0.5, 0.5].

Returns:

(irreps, spin_rep, mapping). irreps is the list of spgrep irreps of the little group at qpoint (each an array of shape (order, dim, dim)); spin_rep is the complex array of shape (order, 3N, 3N) holding the spin-representation matrix of every little-group operation, in the order of the irrep matrices; mapping is the integer array that indexes those operations in vibrations.rotations (what get_irrep_labels and get_multipole_rank_lists expect).

Return type:

tuple

Example

>>> from phonopy.interface.vasp import read_vasp
>>> from crystod import mag, phonon
>>> from crystod.examples import example_path
>>> cell = read_vasp(example_path("221_PPOSCAR_SrTiO3"))
>>> vib = phonon.SymmetryOnlyVibrations(cell, standardize=False)
>>> symbols = vib.primitive_cell.symbols
>>> sites = [i for i, s in enumerate(symbols) if s == "O"]   # 3 sites
>>> q = [0, 0, 0]
>>> irreps, spin_rep, mapping = mag.get_spin_representation(vib, sites, q)
>>> spin_rep.shape
(48, 9, 9)
>>> vib.get_irrep_labels(q, irreps, mapping)[6]
'GM4+(3)'
crystod.mag.separate_ferro_combination(spaces, representation, n_sites)[source]#

Split the ferromagnetic combination off the spaces of one irrep.

At q = 0 an irrep can occur several times in the spin representation (2 x GM4+ for the three Ni sites of AlNi3); crystod-mag calls this function once per irrep label with all of its projected spaces. The spaces are first aligned to a common partner basis (an intertwiner with representation is found for every space, and complex circular bases are recombined into real ones), then the net moments sum_i S_i of the aligned spaces are combined into one unit vector: its unitary completion gives the unique combination that carries a net moment (the cluster dipole, printed as FM) and the orthogonal combinations with sum_i S_i = 0 (AFM: octupoles and higher). When no space carries a net moment, or the completion does not separate cleanly, the aligned spaces are returned unchanged as antiferromagnetic.

Parameters:
  • spaces (list[ndarray[tuple[Any, ...], dtype[float64]]]) – Real basis arrays of shape (dim, 3N) (one row per partner of the irrep; 3N = three Cartesian components of N spins) that all carry the same irrep, e.g. the output of spgrep.representation.project_to_irrep for one irrep.

  • representation (ndarray[tuple[Any, ...], dtype[complex128]]) – The spin-representation matrices of shape (order, 3N, 3N) returned by get_spin_representation; used to align the spaces to a common basis.

  • n_sites (int) – The number N of spin-carrying sites.

Returns:

(ferro, afm). ferro is the basis array of shape (dim, 3N) with a net moment (None when no combination has one) and afm is the list of the remaining net-zero basis arrays; together they span the same space as the input.

Return type:

tuple

Example

>>> # spin_rep, irreps, sites: see get_spin_representation (SrTiO3, O)
>>> import numpy as np
>>> from spgrep.representation import project_to_irrep
>>> gm4p = irreps[6]                                     # GM4+, twice
>>> spaces = [np.real(s) for s in project_to_irrep(spin_rep, gm4p)]
>>> ferro, afm = mag.separate_ferro_combination(spaces, spin_rep, 3)
>>> ferro.shape, len(afm)
((3, 9), 1)
>>> bool(np.abs(afm[0].reshape(3, 3, 3).sum(axis=1)).max() < 1e-8)
True
crystod.mag.MULTIPOLE_NAMES = {0: 'monopole', 1: 'dipole', 2: 'quadrupole', 3: 'octupole', 4: 'hexadecapole', 5: '32pole', 6: '64pole'}#

Names of the magnetic multipole ranks used in the crystod-mag output (rank p -> 2**p-pole): {1: "dipole", 3: "octupole", ...}.