use crate::error::invalid_param;
use crate::vector3::Vector3;
#[inline]
pub fn check_temperature(t: f64) -> crate::error::Result<()> {
if t < 0.0 {
return Err(invalid_param("temperature", "must be non-negative"));
}
debug_assert!(t < 10000.0, "Temperature unreasonably high: {} K", t);
Ok(())
}
#[inline]
pub fn check_magnetization(ms: f64) -> crate::error::Result<()> {
if ms < 0.0 {
return Err(invalid_param("magnetization", "must be non-negative"));
}
debug_assert!(ms < 2.0e6, "Magnetization unreasonably high: {} A/m", ms);
Ok(())
}
#[inline]
pub fn check_damping(alpha: f64) -> crate::error::Result<()> {
if alpha < 0.0 {
return Err(invalid_param("damping", "must be non-negative"));
}
if alpha >= 1.0 {
return Err(invalid_param("damping", "must be < 1"));
}
debug_assert!(alpha < 0.5, "Damping unusually high: {}", alpha);
Ok(())
}
#[inline]
pub fn check_normalized(v: Vector3<f64>, tolerance: f64) {
let mag = v.magnitude();
debug_assert!(
(mag - 1.0).abs() < tolerance,
"Vector not normalized: magnitude = {}",
mag
);
}
#[inline]
pub fn check_time_step(dt: f64) -> crate::error::Result<()> {
if dt <= 0.0 {
return Err(invalid_param("time step", "must be positive"));
}
debug_assert!(dt < 1e-6, "Time step unreasonably large: {} s", dt);
Ok(())
}
#[inline]
pub fn check_exchange(a_ex: f64) -> crate::error::Result<()> {
if a_ex <= 0.0 {
return Err(invalid_param("exchange constant", "must be positive"));
}
debug_assert!(
a_ex < 1e-10,
"Exchange constant unreasonably large: {} J/m",
a_ex
);
Ok(())
}
#[inline]
pub fn check_current_density(j: f64) {
debug_assert!(
j.abs() < 1e13,
"Current density unreasonably high: {} A/m²",
j
);
}
#[inline]
pub fn check_spin_hall_angle(theta_sh: f64) -> crate::error::Result<()> {
if theta_sh.abs() > 1.0 {
return Err(invalid_param(
"spin Hall angle",
"must be <= 1 in magnitude",
));
}
Ok(())
}
#[inline]
pub fn check_mesh_dimensions(nx: usize, ny: usize, nz: usize) -> crate::error::Result<()> {
if nx == 0 || ny == 0 || nz == 0 {
return Err(invalid_param("mesh dimensions", "must be non-zero"));
}
debug_assert!(
nx * ny * nz < 1_000_000_000,
"Mesh too large: {}x{}x{}",
nx,
ny,
nz
);
Ok(())
}
#[inline]
pub fn check_length(length: f64, name: &str) -> crate::error::Result<()> {
if length <= 0.0 {
return Err(invalid_param(name, "must be positive"));
}
Ok(())
}
#[inline]
pub fn check_spin_polarization(p: f64) -> crate::error::Result<()> {
if !(0.0..=1.0).contains(&p) {
return Err(invalid_param("spin polarization", "must be in [0, 1]"));
}
Ok(())
}
#[cfg(test)]
mod experimental_validation {
use std::f64::consts::PI;
use crate::constants::GAMMA;
use crate::dynamics::llg::calc_dm_dt;
use crate::effect::ishe::InverseSpinHall;
use crate::material::{Ferromagnet, SpinInterface};
use crate::texture::dmi::DmiParameters;
use crate::transport::pumping::spin_pumping_current;
use crate::Vector3;
#[test]
fn validate_saitoh_2006_spin_pumping_ishe() {
let yig = Ferromagnet::yig();
let interface = SpinInterface::yig_pt();
let pt = InverseSpinHall::platinum();
let _omega = 2.0 * PI * 9.4e9; let h0 = 0.1; let precession_angle = 1.0_f64.to_radians();
let m = Vector3::new(precession_angle.sin(), 0.0, precession_angle.cos()).normalize();
let h_eff = Vector3::new(0.0, 0.0, h0);
let dm_dt = calc_dm_dt(m, h_eff, GAMMA, yig.alpha);
let calc_omega = dm_dt.magnitude() / precession_angle.sin();
let expected_omega = GAMMA * h0; assert!(
(calc_omega - expected_omega).abs() / expected_omega < 0.5,
"Precession frequency mismatch: {} vs {} rad/s",
calc_omega,
expected_omega
);
let js = spin_pumping_current(&interface, m, dm_dt);
assert!(
js.magnitude() > 1e-10,
"Spin current too small: {} J/m²",
js.magnitude()
);
let interface_normal = Vector3::new(0.0, 0.0, 1.0);
let e_field = pt.convert(interface_normal, js);
let width = 0.1e-3; let voltage = e_field.magnitude() * width;
assert!(
voltage > 1e-20 && voltage < 1e-3,
"Voltage out of physical range: {:.3e} V",
voltage
);
println!(
"Saitoh 2006 validation: V = {:.2} μV (experimental: ~1 μV)",
voltage * 1e6
);
}
#[test]
fn validate_llg_larmor_precession() {
let theta0 = 5.0_f64.to_radians();
let m = Vector3::new(theta0.sin(), 0.0, theta0.cos());
let h_field = 0.1; let h_eff = Vector3::new(0.0, 0.0, h_field);
let alpha = 0.001;
let dm_dt = calc_dm_dt(m, h_eff, GAMMA, alpha);
let omega_larmor = GAMMA * h_field;
let omega_calc = dm_dt.magnitude() / theta0.sin();
let error = (omega_calc - omega_larmor).abs() / omega_larmor;
assert!(
error < 0.01,
"Larmor frequency error: {:.2}% (calc: {:.3e}, expected: {:.3e} rad/s)",
error * 100.0,
omega_calc,
omega_larmor
);
println!(
"LLG Larmor validation: ω = {:.3e} rad/s (expected: {:.3e})",
omega_calc, omega_larmor
);
}
#[test]
fn validate_skyrmion_size_pt_cofeb() {
let dmi = DmiParameters::pt_cofeb();
let a_ex = 1.5e-11;
let diameter = dmi.skyrmion_diameter(a_ex);
assert!(
diameter > 1e-9,
"Characteristic length too small: {:.1} nm",
diameter * 1e9
);
assert!(
diameter < 1e-2,
"Characteristic length unreasonably large: {:.1} mm",
diameter * 1e3
);
println!(
"Skyrmion diameter (DMI-exchange): {:.1} nm (experimental: 30-150 nm)",
diameter * 1e9
);
}
#[test]
fn validate_critical_dmi_formula() {
let a_ex = 1.5e-11; let k_u = 1.0e6;
let d_c = DmiParameters::critical_dmi(a_ex, k_u);
assert!(
d_c > 1e-4 && d_c < 1e-2,
"Critical DMI unrealistic: {:.2} mJ/m²",
d_c * 1e3
);
let pt_co = DmiParameters::pt_co();
let k_u_ptco = 8.0e4; let d_c_ptco = DmiParameters::critical_dmi(a_ex, k_u_ptco);
let ratio = pt_co.d / d_c_ptco;
assert!(
ratio > 0.5 && ratio < 10.0,
"Pt/Co DMI ratio unrealistic: D/D_c = {:.2}",
ratio
);
println!(
"Critical DMI: D_c = {:.2} mJ/m² (Pt/Co: {:.2} mJ/m², ratio: {:.2})",
d_c * 1e3,
pt_co.d * 1e3,
ratio
);
}
#[test]
fn validate_spin_hall_angles() {
use crate::effect::sot::SpinOrbitTorque;
let pt = SpinOrbitTorque::platinum_cofeb();
let ta = SpinOrbitTorque::tantalum_cofeb();
let w = SpinOrbitTorque::tungsten_cofeb();
assert!(
pt.theta_sh > 0.0 && pt.theta_sh < 0.15,
"Pt spin Hall angle unrealistic: {}",
pt.theta_sh
);
assert!(
ta.theta_sh < 0.0 && ta.theta_sh > -0.3,
"Ta spin Hall angle unrealistic: {}",
ta.theta_sh
);
assert!(
w.theta_sh.abs() > ta.theta_sh.abs(),
"W should have larger spin Hall angle than Ta"
);
assert!(
w.theta_sh < 0.0 && w.theta_sh > -0.5,
"W spin Hall angle unrealistic: {}",
w.theta_sh
);
println!(
"Spin Hall angles - Pt: {:.3}, Ta: {:.3}, W: {:.3}",
pt.theta_sh, ta.theta_sh, w.theta_sh
);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_temperature_validation() {
assert!(check_temperature(300.0).is_ok());
assert!(check_temperature(0.0).is_ok());
assert!(check_temperature(-1.0).is_err());
}
#[test]
fn test_magnetization_validation() {
assert!(check_magnetization(8e5).is_ok());
assert!(check_magnetization(0.0).is_ok());
assert!(check_magnetization(-100.0).is_err());
}
#[test]
fn test_damping_validation() {
assert!(check_damping(0.01).is_ok());
assert!(check_damping(0.0).is_ok());
assert!(check_damping(0.1).is_ok());
assert!(check_damping(-0.1).is_err());
assert!(check_damping(1.0).is_err());
}
#[test]
fn test_normalized_check() {
let v = Vector3::new(1.0, 0.0, 0.0);
check_normalized(v, 1e-10);
let v2 = Vector3::new(0.6, 0.8, 0.0);
check_normalized(v2, 1e-10);
}
#[test]
fn test_time_step_validation() {
assert!(check_time_step(1e-12).is_ok());
assert!(check_time_step(0.0).is_err());
assert!(check_time_step(-1e-12).is_err());
}
#[test]
fn test_spin_hall_angle_validation() {
assert!(check_spin_hall_angle(0.5).is_ok());
assert!(check_spin_hall_angle(-0.3).is_ok());
assert!(check_spin_hall_angle(1.0).is_ok());
assert!(check_spin_hall_angle(1.5).is_err());
}
#[test]
fn test_mesh_dimensions() {
assert!(check_mesh_dimensions(10, 10, 1).is_ok());
assert!(check_mesh_dimensions(0, 10, 1).is_err());
assert!(check_mesh_dimensions(10, 0, 1).is_err());
}
#[test]
fn test_spin_polarization_validation() {
assert!(check_spin_polarization(0.5).is_ok());
assert!(check_spin_polarization(0.0).is_ok());
assert!(check_spin_polarization(1.0).is_ok());
assert!(check_spin_polarization(-0.1).is_err());
assert!(check_spin_polarization(1.5).is_err());
}
}