use scirs2_core::random::core::Random;
use scirs2_core::random::rand_distributions::Normal;
use scirs2_core::random::thread_rng;
use scirs2_core::rngs;
use crate::constants::{GAMMA, KB};
use crate::vector3::Vector3;
pub struct ThermalField {
pub temperature: f64,
pub volume: f64,
pub ms: f64,
pub alpha: f64,
pub gamma: f64,
rng: Random<rngs::ThreadRng>,
normal: Normal<f64>,
}
impl ThermalField {
pub fn new(temperature: f64, volume: f64, ms: f64, alpha: f64) -> Self {
Self {
temperature,
volume,
ms,
alpha,
gamma: GAMMA,
rng: thread_rng(),
normal: Normal::new(0.0, 1.0)
.expect("standard normal distribution parameters are valid"),
}
}
pub fn generate(&mut self, dt: f64) -> Vector3<f64> {
if self.temperature <= 0.0 || dt <= 0.0 {
return Vector3::new(0.0, 0.0, 0.0);
}
let variance =
(2.0 * self.alpha * KB * self.temperature) / (self.gamma * self.ms * self.volume * dt);
let sigma = variance.sqrt();
Vector3::new(
self.rng.sample(self.normal) * sigma,
self.rng.sample(self.normal) * sigma,
self.rng.sample(self.normal) * sigma,
)
}
pub fn generate_scaled(&mut self, dt: f64, scale: f64) -> Vector3<f64> {
let base_field = self.generate(dt);
base_field * scale
}
pub fn set_temperature(&mut self, temperature: f64) {
self.temperature = temperature;
}
pub fn thermal_energy(&self) -> f64 {
KB * self.temperature
}
pub fn attempt_frequency(&self) -> f64 {
let mu0 = 1.256637e-6; (self.gamma * mu0 * self.ms) / (2.0 * std::f64::consts::PI)
}
}
pub struct StochasticLLG {
pub magnetization: Vector3<f64>,
pub thermal: ThermalField,
pub alpha: f64,
}
impl StochasticLLG {
pub fn new(
initial_m: Vector3<f64>,
temperature: f64,
volume: f64,
ms: f64,
alpha: f64,
) -> Self {
Self {
magnetization: initial_m.normalize(),
thermal: ThermalField::new(temperature, volume, ms, alpha),
alpha,
}
}
pub fn evolve(&mut self, h_eff: Vector3<f64>, dt: f64) {
let h_thermal = self.thermal.generate(dt);
let h_total = h_eff + h_thermal;
let gamma = GAMMA;
let m_cross_h = self.magnetization.cross(&h_total);
let damping = self.magnetization.cross(&m_cross_h) * self.alpha;
let dm_dt = (m_cross_h + damping) * (-gamma / (1.0 + self.alpha * self.alpha));
self.magnetization = (self.magnetization + dm_dt * dt).normalize();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_thermal_field_creation() {
let thermal = ThermalField::new(300.0, 1.0e-24, 1.0e6, 0.01);
assert!((thermal.temperature - 300.0).abs() < 1e-10);
}
#[test]
fn test_thermal_field_zero_temp() {
let mut thermal = ThermalField::new(0.0, 1.0e-24, 1.0e6, 0.01);
let h_th = thermal.generate(1.0e-12);
assert!(h_th.magnitude() < 1e-20);
}
#[test]
fn test_thermal_field_nonzero() {
let mut thermal = ThermalField::new(300.0, 1.0e-24, 1.0e6, 0.01);
let h_th = thermal.generate(1.0e-12);
assert!(h_th.magnitude() > 0.0);
}
#[test]
fn test_thermal_energy() {
let thermal = ThermalField::new(300.0, 1.0e-24, 1.0e6, 0.01);
let e_th = thermal.thermal_energy();
assert!((e_th - 4.14e-21).abs() < 1e-22);
}
#[test]
fn test_attempt_frequency() {
let thermal = ThermalField::new(300.0, 1.0e-24, 1.0e6, 0.01);
let f0 = thermal.attempt_frequency();
assert!(f0 > 1.0e8); assert!(f0 < 1.0e12); }
#[test]
fn test_stochastic_llg_creation() {
let sllg = StochasticLLG::new(Vector3::new(1.0, 0.0, 0.0), 300.0, 1.0e-24, 1.0e6, 0.01);
assert!((sllg.magnetization.magnitude() - 1.0).abs() < 1e-10);
}
#[test]
fn test_stochastic_evolution() {
let mut sllg = StochasticLLG::new(Vector3::new(1.0, 0.0, 0.0), 300.0, 1.0e-24, 1.0e6, 0.01);
let h_ext = Vector3::new(0.0, 0.0, 1.0e5);
for _ in 0..100 {
sllg.evolve(h_ext, 1.0e-13);
}
assert!((sllg.magnetization.magnitude() - 1.0).abs() < 1e-6);
}
#[test]
#[allow(dead_code)]
fn test_temperature_scaling() {
let mut thermal_cold = ThermalField::new(100.0, 1.0e-24, 1.0e6, 0.01);
let mut thermal_hot = ThermalField::new(600.0, 1.0e-24, 1.0e6, 0.01);
let dt = 1.0e-12;
let mut variance_cold = 0.0;
let mut variance_hot = 0.0;
let n_samples = 1000;
for _ in 0..n_samples {
let h_cold = thermal_cold.generate(dt);
let h_hot = thermal_hot.generate(dt);
variance_cold += h_cold.magnitude().powi(2);
variance_hot += h_hot.magnitude().powi(2);
}
variance_cold /= n_samples as f64;
variance_hot /= n_samples as f64;
assert!(variance_hot > variance_cold * 0.5);
}
}