spintronics 0.3.0

Pure Rust library for simulating spin dynamics, spin current generation, and conversion phenomena in magnetic and topological materials
Documentation
//! Spin Seebeck Effect (SSE)
//!
//! The spin Seebeck effect generates a spin current in response to a temperature
//! gradient. This phenomenon was discovered by:
//!
//! K. Uchida et al., "Observation of the spin Seebeck effect",
//! Nature 455, 778-781 (2008)
//!
//! The spin current density generated by SSE is:
//!
//! J_s = L_s * ∇T
//!
//! where L_s is the spin Seebeck coefficient and ∇T is the temperature gradient.

use std::fmt;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

use crate::vector3::Vector3;

/// Spin Seebeck Effect properties
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SpinSeebeck {
    /// Spin Seebeck coefficient [J/(K·m²)]
    ///
    /// Material-dependent parameter determining the efficiency of
    /// thermal-to-spin current conversion
    pub l_s: f64,

    /// Interface thermal conductance [W/(K·m²)]
    pub g_th: f64,

    /// Spin polarization direction (typically parallel to magnetization)
    pub polarization: Vector3<f64>,
}

impl Default for SpinSeebeck {
    fn default() -> Self {
        Self {
            l_s: 1.0e-6,
            g_th: 1.0e6,
            polarization: Vector3::new(0.0, 0.0, 1.0),
        }
    }
}

impl SpinSeebeck {
    /// Create SSE properties for YIG/Pt system
    ///
    /// Based on experimental parameters from Uchida et al.
    ///
    /// # Example
    /// ```
    /// use spintronics::effect::sse::SpinSeebeck;
    /// use spintronics::Vector3;
    ///
    /// // Create YIG/Pt system
    /// let sse = SpinSeebeck::yig_pt();
    ///
    /// // Apply temperature gradient (1000 K/m in z direction)
    /// let grad_t = Vector3::new(0.0, 0.0, 1000.0);
    ///
    /// // Calculate spin current density
    /// let js = sse.spin_current(grad_t);
    ///
    /// // Spin current should be non-zero and polarized along z
    /// assert!(js.magnitude() > 0.0);
    /// assert!(js.z > 0.0);
    /// ```
    pub fn yig_pt() -> Self {
        Self {
            l_s: 1.0e-6,
            g_th: 5.0e5,
            polarization: Vector3::new(0.0, 0.0, 1.0),
        }
    }

    /// Calculate spin current from temperature gradient
    ///
    /// # Arguments
    /// * `grad_t` - Temperature gradient vector \[K/m\]
    ///
    /// # Returns
    /// Spin current density \[J/m²\]
    ///
    /// # Physical Interpretation
    /// A temperature gradient creates a non-equilibrium distribution of
    /// magnons (spin waves) in the ferromagnet. These magnons carry spin
    /// angular momentum and can transfer it to conduction electrons at
    /// the interface, creating a spin current.
    ///
    /// # Example
    /// ```
    /// use spintronics::effect::sse::SpinSeebeck;
    /// use spintronics::Vector3;
    ///
    /// let sse = SpinSeebeck {
    ///     l_s: 1.0e-6,                        // Spin Seebeck coefficient
    ///     g_th: 1.0e6,                        // Thermal conductance
    ///     polarization: Vector3::new(0.0, 0.0, 1.0),  // z-polarized
    /// };
    ///
    /// // Temperature gradient: 5000 K/m in z direction
    /// let grad_t = Vector3::new(0.0, 0.0, 5000.0);
    ///
    /// // Calculate spin current
    /// let js = sse.spin_current(grad_t);
    ///
    /// // Spin current magnitude: J_s = L_s * |∇T|
    /// let expected_magnitude = 1.0e-6 * 5000.0;  // = 5.0e-3 J/m²
    /// assert!((js.magnitude() - expected_magnitude).abs() < 1e-6);
    ///
    /// // Spin current is polarized along magnetization (z direction)
    /// assert!(js.z > 0.0);
    /// assert!(js.x.abs() < 1e-20);
    /// assert!(js.y.abs() < 1e-20);
    /// ```
    #[inline]
    pub fn spin_current(&self, grad_t: Vector3<f64>) -> Vector3<f64> {
        // Spin current is proportional to temperature gradient
        // and polarized along the magnetization direction
        let magnitude = self.l_s * grad_t.magnitude();
        self.polarization * magnitude
    }

    /// Calculate spin current for a linear temperature gradient
    ///
    /// # Arguments
    /// * `delta_t` - Temperature difference \[K\]
    /// * `thickness` - Distance over which temperature drops \[m\]
    /// * `direction` - Direction of heat flow (normalized)
    ///
    /// # Returns
    /// Spin current density \[J/m²\]
    ///
    /// # Example
    /// ```
    /// use spintronics::effect::sse::SpinSeebeck;
    /// use spintronics::Vector3;
    ///
    /// // YIG/Pt system
    /// let sse = SpinSeebeck::yig_pt();
    ///
    /// // Temperature difference: 10 K across 1 mm YIG film
    /// let delta_t = 10.0;           // K
    /// let thickness = 1.0e-3;       // 1 mm
    /// let direction = Vector3::new(0.0, 0.0, 1.0);  // Heat flows in z
    ///
    /// // Calculate spin current
    /// let js = sse.linear_gradient(delta_t, thickness, direction);
    ///
    /// // Spin current should be non-zero
    /// assert!(js.magnitude() > 0.0);
    ///
    /// // Gradient: ∇T = ΔT / d = 10 / 1e-3 = 10000 K/m
    /// // J_s = L_s * |∇T| = 1.0e-6 * 10000 = 0.01 J/m²
    /// let expected = 1.0e-6 * 10000.0;
    /// assert!((js.magnitude() - expected).abs() < 1e-10);
    /// ```
    pub fn linear_gradient(
        &self,
        delta_t: f64,
        thickness: f64,
        direction: Vector3<f64>,
    ) -> Vector3<f64> {
        let grad_t_magnitude = delta_t / thickness;
        let grad_t = direction.normalize() * grad_t_magnitude;
        self.spin_current(grad_t)
    }

    /// Calculate thermal spin current across interface
    ///
    /// Accounts for finite thermal interface conductance
    #[inline]
    pub fn interface_current(&self, delta_t_interface: f64) -> Vector3<f64> {
        let magnitude = self.l_s * self.g_th * delta_t_interface;
        self.polarization * magnitude
    }

    /// Builder method to set spin Seebeck coefficient
    pub fn with_l_s(mut self, l_s: f64) -> Self {
        self.l_s = l_s;
        self
    }

    /// Builder method to set thermal conductance
    pub fn with_g_th(mut self, g_th: f64) -> Self {
        self.g_th = g_th;
        self
    }

    /// Builder method to set spin polarization direction
    pub fn with_polarization(mut self, polarization: Vector3<f64>) -> Self {
        self.polarization = polarization.normalize();
        self
    }
}

impl fmt::Display for SpinSeebeck {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "SpinSeebeck(L_s={:.2e} J/(K·m²), G_th={:.2e} W/(K·m²))",
            self.l_s, self.g_th
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_sse_zero_gradient() {
        let sse = SpinSeebeck::default();
        let grad_t = Vector3::new(0.0, 0.0, 0.0);

        let js = sse.spin_current(grad_t);
        assert!(js.magnitude() < 1e-50);
    }

    #[test]
    fn test_sse_polarization() {
        let sse = SpinSeebeck {
            polarization: Vector3::new(1.0, 0.0, 0.0),
            ..Default::default()
        };
        let grad_t = Vector3::new(0.0, 100.0, 0.0); // 100 K/m in y direction

        let js = sse.spin_current(grad_t);

        // Spin current should be polarized along magnetization (x direction)
        // regardless of temperature gradient direction
        assert!(js.y.abs() < 1e-50);
        assert!(js.z.abs() < 1e-50);
        assert!(js.x.abs() > 0.0);
    }

    #[test]
    fn test_linear_gradient() {
        let sse = SpinSeebeck::default();
        let delta_t = 10.0; // 10 K difference
        let thickness = 1.0e-6; // 1 μm
        let direction = Vector3::new(0.0, 1.0, 0.0);

        let js = sse.linear_gradient(delta_t, thickness, direction);
        assert!(js.magnitude() > 0.0);
    }

    #[test]
    fn test_yig_pt_system() {
        let sse = SpinSeebeck::yig_pt();
        assert!(sse.l_s > 0.0);
        assert!(sse.g_th > 0.0);
    }
}