use crate::constants::GAMMA;
use crate::vector3::Vector3;
#[derive(Debug, Clone)]
pub struct Antiferromagnet {
pub m_a: Vector3<f64>,
pub m_b: Vector3<f64>,
pub h_exchange: f64,
pub gamma: f64,
pub alpha: f64,
pub h_anisotropy: f64,
}
impl Antiferromagnet {
pub fn new(h_exchange: f64, alpha: f64, h_anisotropy: f64) -> Self {
Self {
m_a: Vector3::new(0.0, 0.0, 1.0),
m_b: Vector3::new(0.0, 0.0, -1.0),
h_exchange,
gamma: GAMMA,
alpha,
h_anisotropy,
}
}
pub fn nio() -> Self {
Self::new(
8.0e7, 0.005, 1.0e6, )
}
pub fn mnf2() -> Self {
Self::new(
5.0e7, 0.01, 5.0e5,
)
}
pub fn neel_vector(&self) -> Vector3<f64> {
(self.m_a + self.m_b * (-1.0)) * 0.5
}
pub fn total_magnetization(&self) -> Vector3<f64> {
(self.m_a + self.m_b) * 0.5
}
pub fn evolve(&mut self, h_ext: Vector3<f64>, dt: f64) {
let (dma_dt1, dmb_dt1) = self.calc_derivatives(h_ext);
let m_a_pred = (self.m_a + dma_dt1 * dt).normalize();
let m_b_pred = (self.m_b + dmb_dt1 * dt).normalize();
let m_a_orig = self.m_a;
let m_b_orig = self.m_b;
self.m_a = m_a_pred;
self.m_b = m_b_pred;
let (dma_dt2, dmb_dt2) = self.calc_derivatives(h_ext);
self.m_a = (m_a_orig + (dma_dt1 + dma_dt2) * (0.5 * dt)).normalize();
self.m_b = (m_b_orig + (dmb_dt1 + dmb_dt2) * (0.5 * dt)).normalize();
}
fn calc_derivatives(&self, h_ext: Vector3<f64>) -> (Vector3<f64>, Vector3<f64>) {
let h_anis_a = Vector3::new(0.0, 0.0, self.h_anisotropy * self.m_a.z);
let h_anis_b = Vector3::new(0.0, 0.0, self.h_anisotropy * self.m_b.z);
let h_eff_a = h_ext + h_anis_a + self.m_b * (-self.h_exchange);
let h_eff_b = h_ext + h_anis_b + self.m_a * (-self.h_exchange);
let dma_dt = self.calc_llg(self.m_a, h_eff_a);
let dmb_dt = self.calc_llg(self.m_b, h_eff_b);
(dma_dt, dmb_dt)
}
fn calc_llg(&self, m: Vector3<f64>, h: Vector3<f64>) -> Vector3<f64> {
let precession = m.cross(&h);
let damping = m.cross(&precession) * self.alpha;
(precession + damping) * (-self.gamma / (1.0 + self.alpha * self.alpha))
}
pub fn resonance_frequency(&self) -> f64 {
let mu0 = 1.256637e-6; let omega = self.gamma * mu0 * (self.h_exchange * self.h_anisotropy).sqrt();
omega / (2.0 * std::f64::consts::PI)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_afm_creation() {
let afm = Antiferromagnet::nio();
assert!(afm.h_exchange > 0.0);
assert!((afm.m_a.z - 1.0).abs() < 1e-10);
assert!((afm.m_b.z + 1.0).abs() < 1e-10);
}
#[test]
fn test_neel_vector() {
let afm = Antiferromagnet::nio();
let neel = afm.neel_vector();
assert!((neel.z - 1.0).abs() < 1e-10);
assert!((neel.x).abs() < 1e-10);
assert!((neel.y).abs() < 1e-10);
}
#[test]
fn test_total_magnetization_zero() {
let afm = Antiferromagnet::nio();
let m_total = afm.total_magnetization();
assert!(m_total.magnitude() < 1e-10);
}
#[test]
fn test_resonance_frequency_thz() {
let afm = Antiferromagnet::nio();
let f_res = afm.resonance_frequency();
assert!(f_res > 1.0e11); assert!(f_res < 1.0e14); }
#[test]
fn test_afm_evolution() {
let mut afm = Antiferromagnet::nio();
let h_ext = Vector3::new(1.0e6, 0.0, 0.0);
let neel_before = afm.neel_vector();
for _ in 0..10 {
afm.evolve(h_ext, 1.0e-14); }
let neel_after = afm.neel_vector();
assert!((neel_before - neel_after).magnitude() > 1e-6);
assert!((afm.m_a.magnitude() - 1.0).abs() < 1e-6);
assert!((afm.m_b.magnitude() - 1.0).abs() < 1e-6);
}
}