use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::constants::{E_CHARGE, HBAR};
use crate::error::{Error, Result};
use crate::vector3::Vector3;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct OrbitalHallMaterial {
pub name: &'static str,
pub sigma_oh: f64,
pub resistivity: f64,
pub soc_strength: f64,
pub orbital_hall_angle: f64,
}
impl OrbitalHallMaterial {
pub fn new(
name: &'static str,
sigma_oh: f64,
resistivity: f64,
soc_strength: f64,
orbital_hall_angle: f64,
) -> Result<Self> {
if resistivity <= 0.0 {
return Err(Error::InvalidParameter {
param: "resistivity".to_string(),
reason: "must be positive".to_string(),
});
}
if soc_strength < 0.0 {
return Err(Error::InvalidParameter {
param: "soc_strength".to_string(),
reason: "must be non-negative".to_string(),
});
}
Ok(Self {
name,
sigma_oh,
resistivity,
soc_strength,
orbital_hall_angle,
})
}
pub fn chromium() -> Self {
Self {
name: "Cr",
sigma_oh: 5000.0,
resistivity: 1.29e-7,
soc_strength: 0.04,
orbital_hall_angle: 0.065,
}
}
pub fn titanium() -> Self {
Self {
name: "Ti",
sigma_oh: 3000.0,
resistivity: 4.20e-7,
soc_strength: 0.02,
orbital_hall_angle: 0.126,
}
}
pub fn vanadium() -> Self {
Self {
name: "V",
sigma_oh: 2500.0,
resistivity: 1.97e-7,
soc_strength: 0.03,
orbital_hall_angle: 0.049,
}
}
pub fn copper() -> Self {
Self {
name: "Cu",
sigma_oh: 1500.0,
resistivity: 1.68e-8,
soc_strength: 0.01,
orbital_hall_angle: 0.003,
}
}
pub fn aluminum() -> Self {
Self {
name: "Al",
sigma_oh: 800.0,
resistivity: 2.65e-8,
soc_strength: 0.005,
orbital_hall_angle: 0.002,
}
}
pub fn platinum() -> Self {
Self {
name: "Pt",
sigma_oh: 4000.0,
resistivity: 1.06e-7,
soc_strength: 1.0,
orbital_hall_angle: 0.042,
}
}
pub fn tungsten() -> Self {
Self {
name: "W",
sigma_oh: 3500.0,
resistivity: 2.0e-7,
soc_strength: 0.8,
orbital_hall_angle: 0.070,
}
}
pub fn manganese() -> Self {
Self {
name: "Mn",
sigma_oh: 2000.0,
resistivity: 1.44e-6,
soc_strength: 0.05,
orbital_hall_angle: 0.288,
}
}
pub fn charge_conductivity(&self) -> f64 {
1.0 / self.resistivity
}
pub fn is_light_metal(&self) -> bool {
self.soc_strength < 0.1
}
pub fn ohe_to_pt_she_ratio(&self) -> f64 {
let sigma_sh_pt = 2000.0; self.sigma_oh / sigma_sh_pt
}
}
impl fmt::Display for OrbitalHallMaterial {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"OrbitalHallMaterial({}: sigma_OH={:.0} (hbar/e)(Ohm*cm)^-1, \
rho={:.2e} Ohm*m, SOC={:.3}, theta_OH={:.4})",
self.name, self.sigma_oh, self.resistivity, self.soc_strength, self.orbital_hall_angle
)
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct OrbitalToSpinConverter {
pub conversion_efficiency: f64,
pub interface_type: &'static str,
}
impl OrbitalToSpinConverter {
pub fn new(conversion_efficiency: f64, interface_type: &'static str) -> Result<Self> {
if conversion_efficiency.abs() > 1.0 {
return Err(Error::InvalidParameter {
param: "conversion_efficiency".to_string(),
reason: "must be between -1.0 and 1.0".to_string(),
});
}
Ok(Self {
conversion_efficiency,
interface_type,
})
}
pub fn cu_pt() -> Self {
Self {
conversion_efficiency: 0.6,
interface_type: "Cu/Pt (heavy metal SOC)",
}
}
pub fn ti_pt() -> Self {
Self {
conversion_efficiency: 0.55,
interface_type: "Ti/Pt (light/heavy metal)",
}
}
pub fn cr_pt() -> Self {
Self {
conversion_efficiency: 0.5,
interface_type: "Cr/Pt (giant OHE/heavy metal)",
}
}
pub fn cu_biox() -> Self {
Self {
conversion_efficiency: 0.25,
interface_type: "Cu/BiOx (Rashba SOC)",
}
}
pub fn cu_bi2se3() -> Self {
Self {
conversion_efficiency: 0.7,
interface_type: "Cu/Bi2Se3 (TI surface SOC)",
}
}
pub fn weak() -> Self {
Self {
conversion_efficiency: 0.05,
interface_type: "weak SOC interface",
}
}
#[inline]
pub fn convert_to_spin(&self, j_orbital: Vector3<f64>) -> Vector3<f64> {
j_orbital * self.conversion_efficiency
}
}
impl fmt::Display for OrbitalToSpinConverter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"OrbitalToSpinConverter(eta_LS={:.3}, type={})",
self.conversion_efficiency, self.interface_type
)
}
}
#[derive(Debug, Clone)]
pub struct OrbitalHallEffect {
pub material: OrbitalHallMaterial,
pub converter: OrbitalToSpinConverter,
}
impl OrbitalHallEffect {
pub fn new(material: OrbitalHallMaterial, converter: OrbitalToSpinConverter) -> Self {
Self {
material,
converter,
}
}
#[inline]
pub fn orbital_current_density(&self, e_field: Vector3<f64>) -> Vector3<f64> {
let z_hat = Vector3::unit_z();
let cross = e_field.cross(&z_hat);
cross * (self.material.sigma_oh * 100.0)
}
#[inline]
pub fn orbital_current_from_charge(
&self,
j_charge: f64,
current_direction: Vector3<f64>,
) -> Vector3<f64> {
let z_hat = Vector3::unit_z();
let j_vec = current_direction * j_charge;
let cross = j_vec.cross(&z_hat);
cross * self.material.orbital_hall_angle
}
pub fn effective_spin_current(&self, e_field: Vector3<f64>) -> Vector3<f64> {
let j_orbital = self.orbital_current_density(e_field);
self.converter.convert_to_spin(j_orbital)
}
pub fn effective_spin_hall_conductivity(&self) -> f64 {
self.converter.conversion_efficiency * self.material.sigma_oh
}
pub fn enhancement_over_pt_she(&self) -> f64 {
let sigma_sh_pt = 2000.0; self.effective_spin_hall_conductivity() / sigma_sh_pt
}
pub fn orbital_current_magnitude(&self, j_charge: f64) -> f64 {
self.material.orbital_hall_angle.abs() * j_charge.abs()
}
pub fn orbital_accumulation(
&self,
e_field_magnitude: f64,
orbital_diffusion_length: f64,
) -> f64 {
let sigma_oh_si = self.material.sigma_oh * 100.0;
let sigma_charge = self.material.charge_conductivity();
sigma_oh_si * e_field_magnitude * orbital_diffusion_length / sigma_charge
}
}
impl fmt::Display for OrbitalHallEffect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"OrbitalHallEffect(material={}, converter={})",
self.material.name, self.converter.interface_type
)
}
}
pub fn cr_pt_system() -> OrbitalHallEffect {
OrbitalHallEffect::new(
OrbitalHallMaterial::chromium(),
OrbitalToSpinConverter::cr_pt(),
)
}
pub fn ti_pt_system() -> OrbitalHallEffect {
OrbitalHallEffect::new(
OrbitalHallMaterial::titanium(),
OrbitalToSpinConverter::ti_pt(),
)
}
pub fn cu_pt_system() -> OrbitalHallEffect {
OrbitalHallEffect::new(
OrbitalHallMaterial::copper(),
OrbitalToSpinConverter::cu_pt(),
)
}
pub fn material_ohe_ranking() -> Vec<(&'static str, f64)> {
let materials = [
OrbitalHallMaterial::chromium(),
OrbitalHallMaterial::titanium(),
OrbitalHallMaterial::vanadium(),
OrbitalHallMaterial::copper(),
OrbitalHallMaterial::aluminum(),
OrbitalHallMaterial::platinum(),
OrbitalHallMaterial::tungsten(),
OrbitalHallMaterial::manganese(),
];
let mut ranking: Vec<(&'static str, f64)> =
materials.iter().map(|m| (m.name, m.sigma_oh)).collect();
ranking.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
ranking
}
pub fn sigma_oh_to_si(sigma_oh_cgs: f64) -> f64 {
sigma_oh_cgs * 100.0 * HBAR / (2.0 * E_CHARGE)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cr_giant_ohe() {
let cr = OrbitalHallMaterial::chromium();
assert!(
cr.sigma_oh > 4000.0,
"Cr sigma_OH should be giant, got {}",
cr.sigma_oh
);
assert!(
cr.is_light_metal(),
"Cr should be classified as light metal"
);
assert!(
cr.ohe_to_pt_she_ratio() > 1.0,
"Cr OHE should exceed Pt SHE"
);
}
#[test]
fn test_ti_large_ohe() {
let ti = OrbitalHallMaterial::titanium();
assert!(
ti.sigma_oh > 2000.0,
"Ti sigma_OH should be large, got {}",
ti.sigma_oh
);
assert!(
ti.is_light_metal(),
"Ti should be classified as light metal"
);
}
#[test]
fn test_ohe_sign_cr_ti() {
let cr = OrbitalHallMaterial::chromium();
let ti = OrbitalHallMaterial::titanium();
assert!(cr.sigma_oh > 0.0, "Cr sigma_OH should be positive");
assert!(ti.sigma_oh > 0.0, "Ti sigma_OH should be positive");
}
#[test]
fn test_giant_ohe_in_light_metals_vs_pt_she() {
let cr = OrbitalHallMaterial::chromium();
let pt = OrbitalHallMaterial::platinum();
let sigma_sh_pt = 2000.0;
assert!(
cr.sigma_oh > sigma_sh_pt * 2.0,
"Cr OHE ({}) should be >> Pt SHE ({})",
cr.sigma_oh,
sigma_sh_pt
);
assert!(
cr.soc_strength < pt.soc_strength * 0.1,
"Cr SOC ({}) should be << Pt SOC ({})",
cr.soc_strength,
pt.soc_strength
);
}
#[test]
fn test_orbital_to_spin_conversion_efficiency_bounds() {
let valid = OrbitalToSpinConverter::new(0.5, "test").expect("valid converter");
assert!((valid.conversion_efficiency - 0.5).abs() < 1e-10);
let neg_valid =
OrbitalToSpinConverter::new(-0.8, "test negative").expect("valid negative converter");
assert!((neg_valid.conversion_efficiency - (-0.8)).abs() < 1e-10);
let too_large = OrbitalToSpinConverter::new(1.5, "invalid");
assert!(too_large.is_err(), "efficiency > 1 should be rejected");
let too_negative = OrbitalToSpinConverter::new(-1.5, "invalid");
assert!(too_negative.is_err(), "efficiency < -1 should be rejected");
}
#[test]
fn test_orbital_current_density_direction() {
let ohe = cr_pt_system();
let e_field = Vector3::new(1.0e5, 0.0, 0.0);
let j_l = ohe.orbital_current_density(e_field);
assert!(
j_l.y < 0.0,
"Orbital current should be along -y for E along x, got j_l.y={}",
j_l.y
);
assert!(j_l.x.abs() < 1e-10, "j_l.x should be zero");
assert!(j_l.z.abs() < 1e-10, "j_l.z should be zero");
}
#[test]
fn test_orbital_current_density_magnitude() {
let ohe = cr_pt_system();
let e_field = Vector3::new(1.0e5, 0.0, 0.0);
let j_l = ohe.orbital_current_density(e_field);
let mag = j_l.magnitude();
let expected = 5000.0 * 100.0 * 1.0e5;
let rel_err = ((mag - expected) / expected).abs();
assert!(
rel_err < 1e-10,
"Orbital current magnitude mismatch: got {}, expected {}",
mag,
expected
);
}
#[test]
fn test_effective_spin_current_conversion() {
let ohe = cr_pt_system();
let e_field = Vector3::new(1.0e5, 0.0, 0.0);
let j_orbital = ohe.orbital_current_density(e_field);
let j_spin = ohe.effective_spin_current(e_field);
let eta = ohe.converter.conversion_efficiency;
let expected_ratio = eta.abs();
let actual_ratio = j_spin.magnitude() / j_orbital.magnitude();
assert!(
(actual_ratio - expected_ratio).abs() < 1e-10,
"Spin/orbital ratio mismatch"
);
}
#[test]
fn test_light_metal_enhancement_factor() {
let cr_system = cr_pt_system();
let enhancement = cr_system.enhancement_over_pt_she();
assert!(
enhancement > 1.0,
"Cr/Pt OHE should enhance over Pt SHE, got {}",
enhancement
);
}
#[test]
fn test_material_parameter_validation() {
let mat = OrbitalHallMaterial::new("Test", 1000.0, 1e-7, 0.5, 0.01);
assert!(mat.is_ok(), "Valid material should be accepted");
let bad_rho = OrbitalHallMaterial::new("Bad", 1000.0, 0.0, 0.5, 0.01);
assert!(bad_rho.is_err(), "Zero resistivity should be rejected");
let neg_rho = OrbitalHallMaterial::new("Bad", 1000.0, -1.0, 0.5, 0.01);
assert!(neg_rho.is_err(), "Negative resistivity should be rejected");
let neg_soc = OrbitalHallMaterial::new("Bad", 1000.0, 1e-7, -0.1, 0.01);
assert!(neg_soc.is_err(), "Negative SOC should be rejected");
}
#[test]
fn test_material_ohe_ranking() {
let ranking = material_ohe_ranking();
assert!(!ranking.is_empty(), "Ranking should not be empty");
assert_eq!(
ranking[0].0, "Cr",
"Cr should have the highest OHE conductivity"
);
for i in 1..ranking.len() {
assert!(
ranking[i - 1].1 >= ranking[i].1,
"Ranking should be descending: {} ({}) >= {} ({})",
ranking[i - 1].0,
ranking[i - 1].1,
ranking[i].0,
ranking[i].1
);
}
}
#[test]
fn test_orbital_accumulation() {
let ohe = cr_pt_system();
let e_mag = 1.0e5; let lambda_l = 10.0e-9;
let acc = ohe.orbital_accumulation(e_mag, lambda_l);
assert!(acc > 0.0, "Orbital accumulation should be positive");
assert!(acc.is_finite(), "Orbital accumulation should be finite");
}
#[test]
fn test_current_direction_dependence() {
let ohe = cr_pt_system();
let e_x = Vector3::new(1.0e5, 0.0, 0.0);
let j_x = ohe.orbital_current_density(e_x);
assert!(j_x.y < 0.0, "E_x should give J_L in -y");
let e_y = Vector3::new(0.0, 1.0e5, 0.0);
let j_y = ohe.orbital_current_density(e_y);
assert!(j_y.x > 0.0, "E_y should give J_L in +x");
let e_z = Vector3::new(0.0, 0.0, 1.0e5);
let j_z = ohe.orbital_current_density(e_z);
assert!(
j_z.magnitude() < 1e-5,
"E_z should give zero orbital current"
);
}
#[test]
fn test_sigma_oh_unit_conversion() {
let sigma_cgs = 5000.0; let sigma_si = sigma_oh_to_si(sigma_cgs);
assert!(sigma_si > 0.0, "SI sigma should be positive");
assert!(sigma_si.is_finite(), "SI sigma should be finite");
let approx = 5000.0 * 100.0 * HBAR / (2.0 * E_CHARGE);
let rel_err = ((sigma_si - approx) / approx).abs();
assert!(rel_err < 1e-10, "Unit conversion mismatch");
}
#[test]
fn test_display_formats() {
let cr = OrbitalHallMaterial::chromium();
let display = format!("{}", cr);
assert!(
display.contains("Cr"),
"Display should contain material name"
);
let conv = OrbitalToSpinConverter::cu_pt();
let display = format!("{}", conv);
assert!(
display.contains("Cu/Pt"),
"Display should contain interface type"
);
let ohe = cr_pt_system();
let display = format!("{}", ohe);
assert!(
display.contains("Cr"),
"OHE display should contain material"
);
}
}