use std::f64::consts::PI;
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::materials::{Altermagnet, AltermagneticSymmetry};
use crate::constants::{E_CHARGE, HBAR, KB};
use crate::error::{Error, Result};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct AltermagnetTransport {
pub conductivity: f64,
pub scattering_time: f64,
pub spin_splitting_j: f64,
pub symmetry: AltermagneticSymmetry,
pub neel_temperature: f64,
pub fermi_energy: f64,
}
impl AltermagnetTransport {
pub fn new(material: &Altermagnet, conductivity: f64, scattering_time: f64) -> Result<Self> {
if conductivity <= 0.0 {
return Err(Error::InvalidParameter {
param: "conductivity".to_string(),
reason: "Conductivity must be positive".to_string(),
});
}
if scattering_time <= 0.0 {
return Err(Error::InvalidParameter {
param: "scattering_time".to_string(),
reason: "Scattering time must be positive".to_string(),
});
}
let spin_splitting_j = material.spin_splitting * E_CHARGE;
let fermi_energy = 5.0 * E_CHARGE;
Ok(Self {
conductivity,
scattering_time,
spin_splitting_j,
symmetry: material.symmetry,
neel_temperature: material.neel_temperature,
fermi_energy,
})
}
pub fn with_fermi_energy(
material: &Altermagnet,
conductivity: f64,
scattering_time: f64,
fermi_energy_ev: f64,
) -> Result<Self> {
if fermi_energy_ev <= 0.0 {
return Err(Error::InvalidParameter {
param: "fermi_energy_ev".to_string(),
reason: "Fermi energy must be positive".to_string(),
});
}
let mut transport = Self::new(material, conductivity, scattering_time)?;
transport.fermi_energy = fermi_energy_ev * E_CHARGE;
Ok(transport)
}
pub fn spin_splitter_current(&self, charge_current_density: f64) -> Result<f64> {
if !charge_current_density.is_finite() {
return Err(Error::InvalidParameter {
param: "charge_current_density".to_string(),
reason: "Charge current density must be finite".to_string(),
});
}
let angular_efficiency = 2.0 / PI;
let splitting_ratio = self.spin_splitting_j / (2.0 * self.fermi_energy);
let j_s = splitting_ratio * angular_efficiency * charge_current_density;
Ok(j_s)
}
pub fn spin_splitter_efficiency(&self) -> f64 {
let angular_efficiency = 2.0 / PI;
let splitting_ratio = self.spin_splitting_j / (2.0 * self.fermi_energy);
splitting_ratio * angular_efficiency
}
pub fn crystal_hall_conductivity(&self, lattice_constant: f64) -> Result<f64> {
if lattice_constant <= 0.0 {
return Err(Error::InvalidParameter {
param: "lattice_constant".to_string(),
reason: "Lattice constant must be positive".to_string(),
});
}
let g0 = E_CHARGE * E_CHARGE / (2.0 * PI * HBAR);
let delta_ratio = self.spin_splitting_j / self.fermi_energy;
let delta_ratio_sq = delta_ratio * delta_ratio;
let symmetry_factor = match self.symmetry {
AltermagneticSymmetry::DWave => 1.0,
AltermagneticSymmetry::GWave => 0.5,
AltermagneticSymmetry::IWave => 0.25,
};
let sigma_h = g0 * delta_ratio_sq * symmetry_factor / lattice_constant;
Ok(sigma_h)
}
pub fn crystal_hall_conductivity_for_material(&self, material: &Altermagnet) -> Result<f64> {
self.crystal_hall_conductivity(material.lattice_constant)
}
pub fn spin_splitter_efficiency_at_temperature(&self, temperature: f64) -> Result<f64> {
if temperature < 0.0 {
return Err(Error::InvalidParameter {
param: "temperature".to_string(),
reason: "Temperature must be non-negative".to_string(),
});
}
if temperature >= self.neel_temperature {
return Ok(0.0);
}
let beta = 0.35;
let reduced_t = temperature / self.neel_temperature;
let order_param = (1.0 - reduced_t).powf(beta);
Ok(self.spin_splitter_efficiency() * order_param)
}
pub fn spin_conductivity_difference(&self, phi: f64) -> f64 {
let angular = self.symmetry.angular_factor(phi);
let splitting_ratio = self.spin_splitting_j / self.fermi_energy;
self.conductivity * splitting_ratio * angular
}
pub fn directional_spin_current(&self, charge_current_density: f64, phi: f64) -> Result<f64> {
if !charge_current_density.is_finite() {
return Err(Error::InvalidParameter {
param: "charge_current_density".to_string(),
reason: "Charge current density must be finite".to_string(),
});
}
let angular = self.symmetry.angular_factor(phi);
let splitting_ratio = self.spin_splitting_j / (2.0 * self.fermi_energy);
Ok(splitting_ratio * angular * charge_current_density)
}
pub fn spin_charge_conversion_efficiency(
&self,
current_angle: f64,
detection_angle: f64,
) -> f64 {
let splitting_ratio = self.spin_splitting_j / (2.0 * self.fermi_energy);
let current_factor = self.symmetry.angular_factor(current_angle);
let detection_factor = self.symmetry.angular_factor(detection_angle);
splitting_ratio * (current_factor - detection_factor).abs()
}
pub fn thermal_smearing_factor(&self, temperature: f64) -> Result<f64> {
if temperature < 0.0 {
return Err(Error::InvalidParameter {
param: "temperature".to_string(),
reason: "Temperature must be non-negative".to_string(),
});
}
if self.spin_splitting_j.abs() < 1e-30 {
return Ok(0.0);
}
let kbt = KB * temperature;
let ratio_sq = (kbt / self.spin_splitting_j).powi(2);
let factor = 1.0 - (PI * PI / 6.0) * ratio_sq;
Ok(factor.clamp(0.0, 1.0))
}
}
impl fmt::Display for AltermagnetTransport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"AltermagnetTransport(σ={:.2e} S/m, τ={:.2e} s, Δ={:.3} eV, {}, θ_AM={:.4})",
self.conductivity,
self.scattering_time,
self.spin_splitting_j / E_CHARGE,
self.symmetry,
self.spin_splitter_efficiency()
)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_ruo2_transport() -> AltermagnetTransport {
let ruo2 = Altermagnet::ruo2();
AltermagnetTransport::new(&ruo2, 1.0e6, 1.0e-14).expect("Should create RuO2 transport")
}
#[test]
fn test_spin_splitter_nonzero_current() {
let transport = make_ruo2_transport();
let j_c = 1.0e10; let j_s = transport
.spin_splitter_current(j_c)
.expect("Should compute spin current");
assert!(
j_s.abs() > 0.0,
"Spin-splitter current should be nonzero for finite charge current"
);
let j_s_double = transport
.spin_splitter_current(2.0 * j_c)
.expect("Should compute spin current");
assert!(
((j_s_double / j_s) - 2.0).abs() < 1e-10,
"Spin current should scale linearly with charge current"
);
}
#[test]
fn test_spin_splitter_efficiency_positive() {
let transport = make_ruo2_transport();
let efficiency = transport.spin_splitter_efficiency();
assert!(
efficiency > 0.0,
"Spin-splitter efficiency should be positive"
);
assert!(
efficiency < 1.0,
"Spin-splitter efficiency should be less than 1"
);
}
#[test]
fn test_crystal_hall_conductivity_sign_magnitude() {
let ruo2 = Altermagnet::ruo2();
let transport = make_ruo2_transport();
let sigma_h = transport
.crystal_hall_conductivity(ruo2.lattice_constant)
.expect("Should compute Hall conductivity");
assert!(
sigma_h > 0.0,
"Crystal Hall conductivity should be positive"
);
assert!(
sigma_h < 1.0e8,
"Crystal Hall conductivity should be reasonable in magnitude"
);
}
#[test]
fn test_crystal_hall_symmetry_dependence() {
let ruo2 = Altermagnet::ruo2(); let crsb = Altermagnet::crsb();
let transport_d = AltermagnetTransport::new(&ruo2, 1.0e6, 1.0e-14)
.expect("Should create d-wave transport");
let transport_g = AltermagnetTransport::new(&crsb, 1.0e6, 1.0e-14)
.expect("Should create g-wave transport");
let sigma_h_d = transport_d
.crystal_hall_conductivity(ruo2.lattice_constant)
.expect("Should compute d-wave Hall");
let sigma_h_g = transport_g
.crystal_hall_conductivity(crsb.lattice_constant)
.expect("Should compute g-wave Hall");
assert!(sigma_h_d > 0.0);
assert!(sigma_h_g > 0.0);
}
#[test]
fn test_efficiency_vanishes_at_neel_temperature() {
let transport = make_ruo2_transport();
let eff_at_tn = transport
.spin_splitter_efficiency_at_temperature(transport.neel_temperature)
.expect("Should compute efficiency at T_N");
assert!(
eff_at_tn.abs() < 1e-10,
"Efficiency should vanish at Neel temperature"
);
}
#[test]
fn test_efficiency_temperature_monotonic() {
let transport = make_ruo2_transport();
let eff_0 = transport
.spin_splitter_efficiency_at_temperature(0.0)
.expect("Should compute at T=0");
let eff_100 = transport
.spin_splitter_efficiency_at_temperature(100.0)
.expect("Should compute at T=100");
let eff_200 = transport
.spin_splitter_efficiency_at_temperature(200.0)
.expect("Should compute at T=200");
assert!(
eff_0 >= eff_100,
"Efficiency should decrease with temperature"
);
assert!(
eff_100 >= eff_200,
"Efficiency should decrease with temperature"
);
}
#[test]
fn test_spin_conductivity_difference_angular_dependence() {
let transport = make_ruo2_transport();
let delta_sigma_0 = transport.spin_conductivity_difference(0.0);
assert!(delta_sigma_0.abs() > 0.0);
let delta_sigma_node = transport.spin_conductivity_difference(PI / 4.0);
assert!(
delta_sigma_node.abs() < 1e-10,
"Spin conductivity difference should vanish at node angle"
);
let delta_sigma_90 = transport.spin_conductivity_difference(PI / 2.0);
assert!(
(delta_sigma_0 + delta_sigma_90).abs() < 1e-10,
"d-wave: conductivity at 90deg should be opposite to 0deg"
);
}
#[test]
fn test_directional_spin_current_at_node() {
let transport = make_ruo2_transport();
let j_c = 1.0e10;
let j_s_node = transport
.directional_spin_current(j_c, PI / 4.0)
.expect("Should compute directional spin current at node");
assert!(
j_s_node.abs() < 1e-5,
"Directional spin current should vanish at symmetry node"
);
}
#[test]
fn test_thermal_smearing_factor() {
let transport = make_ruo2_transport();
let f0 = transport
.thermal_smearing_factor(0.0)
.expect("Should compute at T=0");
assert!(
(f0 - 1.0).abs() < 1e-10,
"Thermal smearing factor should be 1.0 at T=0"
);
let f300 = transport
.thermal_smearing_factor(300.0)
.expect("Should compute at T=300K");
assert!(
f300 > 0.99,
"Thermal smearing should be small for k_BT << Δ, got {}",
f300
);
let f1000 = transport
.thermal_smearing_factor(1000.0)
.expect("Should compute at T=1000K");
assert!(
f1000 < f300,
"Smearing factor should decrease with temperature"
);
}
#[test]
fn test_invalid_parameters() {
let ruo2 = Altermagnet::ruo2();
let result = AltermagnetTransport::new(&ruo2, -1.0, 1.0e-14);
assert!(result.is_err(), "Should reject negative conductivity");
let result = AltermagnetTransport::new(&ruo2, 1.0e6, 0.0);
assert!(result.is_err(), "Should reject zero scattering time");
let transport = make_ruo2_transport();
assert!(transport
.spin_splitter_efficiency_at_temperature(-10.0)
.is_err());
assert!(transport.thermal_smearing_factor(-10.0).is_err());
}
#[test]
fn test_zero_charge_current_gives_zero_spin_current() {
let transport = make_ruo2_transport();
let j_s = transport
.spin_splitter_current(0.0)
.expect("Should compute with zero current");
assert!(
j_s.abs() < 1e-30,
"Zero charge current should give zero spin current"
);
}
#[test]
fn test_display_formatting() {
let transport = make_ruo2_transport();
let display = format!("{}", transport);
assert!(display.contains("AltermagnetTransport"));
assert!(display.contains("d-wave"));
}
#[test]
fn test_spin_charge_conversion_efficiency() {
let transport = make_ruo2_transport();
let eff_same = transport.spin_charge_conversion_efficiency(0.0, 0.0);
assert!(
eff_same.abs() < 1e-15,
"Same angle should give zero conversion"
);
let eff_max = transport.spin_charge_conversion_efficiency(0.0, PI / 2.0);
assert!(
eff_max > 0.0,
"90 degree offset should give finite conversion"
);
}
#[test]
fn test_with_fermi_energy() {
let ruo2 = Altermagnet::ruo2();
let transport = AltermagnetTransport::with_fermi_energy(&ruo2, 1.0e6, 1.0e-14, 3.0)
.expect("Should create with custom Fermi energy");
let transport_default = make_ruo2_transport();
assert!(
transport.spin_splitter_efficiency() > transport_default.spin_splitter_efficiency(),
"Lower Fermi energy should give higher efficiency"
);
let result = AltermagnetTransport::with_fermi_energy(&ruo2, 1.0e6, 1.0e-14, -1.0);
assert!(result.is_err());
}
}