use crate::altermagnet::band_model::{AltermagnetBandModel, Spin};
use crate::error::Result;
use crate::math::CMatrix;
pub trait BlochHamiltonian {
fn hamiltonian_at(&self, kx: f64, ky: f64) -> Result<CMatrix>;
fn n_bands(&self) -> usize;
fn diagonalize_at(&self, kx: f64, ky: f64) -> Result<(Vec<f64>, CMatrix)> {
self.hamiltonian_at(kx, ky)?.hermitian_eigendecomposition()
}
}
#[derive(Debug, Clone, Copy)]
pub struct AltermagnetSpinHamiltonian<'a> {
pub model: &'a AltermagnetBandModel,
pub spin: Spin,
}
impl<'a> AltermagnetSpinHamiltonian<'a> {
pub fn new(model: &'a AltermagnetBandModel, spin: Spin) -> Self {
Self { model, spin }
}
}
impl BlochHamiltonian for AltermagnetSpinHamiltonian<'_> {
fn hamiltonian_at(&self, kx: f64, ky: f64) -> Result<CMatrix> {
self.model.hamiltonian_matrix(kx, ky, self.spin)
}
fn n_bands(&self) -> usize {
2
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::altermagnet::materials::AltermagneticSymmetry;
fn soc_model() -> AltermagnetBandModel {
AltermagnetBandModel::new(
AltermagneticSymmetry::DWave,
1.0,
0.3,
0.8,
0.6,
0.4,
0.5,
0.0,
1.0,
)
.expect("parameters are valid")
}
#[test]
fn n_bands_is_two() {
let model = soc_model();
let up = AltermagnetSpinHamiltonian::new(&model, Spin::Up);
let down = AltermagnetSpinHamiltonian::new(&model, Spin::Down);
assert_eq!(up.n_bands(), 2);
assert_eq!(down.n_bands(), 2);
}
#[test]
fn hamiltonian_at_matches_model_hamiltonian_matrix() {
let model = soc_model();
let up = AltermagnetSpinHamiltonian::new(&model, Spin::Up);
let (kx, ky) = (0.4, 0.15);
let via_trait = up.hamiltonian_at(kx, ky).expect("valid");
let via_model = model.hamiltonian_matrix(kx, ky, Spin::Up).expect("valid");
for i in 0..2 {
for j in 0..2 {
let a = via_trait.get(i, j);
let b = via_model.get(i, j);
assert!((a.re - b.re).abs() < 1e-15 && (a.im - b.im).abs() < 1e-15);
}
}
}
#[test]
fn diagonalize_at_matches_band_energy() {
let model = soc_model();
for spin in [Spin::Up, Spin::Down] {
let h = AltermagnetSpinHamiltonian::new(&model, spin);
let (kx, ky) = (0.4, 0.15);
let (evals, _) = h.diagonalize_at(kx, ky).expect("valid diagonalization");
assert_eq!(evals.len(), 2);
let expect_lower =
model.band_energy(kx, ky, spin, crate::altermagnet::band_model::Band::Lower);
let expect_upper =
model.band_energy(kx, ky, spin, crate::altermagnet::band_model::Band::Upper);
assert!((evals[0] - expect_lower).abs() < 1e-9);
assert!((evals[1] - expect_upper).abs() < 1e-9);
}
}
#[allow(clippy::needless_range_loop)]
#[test]
fn diagonalize_at_default_impl_is_hermitian_consistent() {
let model = soc_model();
let h = AltermagnetSpinHamiltonian::new(&model, Spin::Down);
let (kx, ky) = (-0.6, 0.9);
let hmat = h.hamiltonian_at(kx, ky).expect("valid");
let (evals, evecs) = h.diagonalize_at(kx, ky).expect("valid");
let hv = hmat.matmul(&evecs).expect("2x2 matmul");
for col in 0..2 {
for row in 0..2 {
let lhs = hv.get(row, col);
let rhs = evecs.get(row, col).scale(evals[col]);
assert!((lhs.re - rhs.re).abs() < 1e-9 && (lhs.im - rhs.im).abs() < 1e-9);
}
}
}
}