use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::vector3::Vector3;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum WallType {
Bloch,
Neel,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DomainWall {
pub center: f64,
pub width: f64,
pub wall_type: WallType,
pub normal: Vector3<f64>,
}
impl Default for DomainWall {
fn default() -> Self {
Self::new(0.0, 10.0e-9, WallType::Bloch)
}
}
impl DomainWall {
pub fn new(center: f64, width: f64, wall_type: WallType) -> Self {
Self {
center,
width,
wall_type,
normal: Vector3::new(1.0, 0.0, 0.0),
}
}
pub fn calculate_width(exchange_a: f64, anisotropy_k: f64) -> f64 {
(exchange_a / anisotropy_k).sqrt()
}
pub fn magnetization_at(&self, x: f64) -> Vector3<f64> {
let xi = (x - self.center) / self.width;
let theta = 2.0 * xi.exp().atan();
match self.wall_type {
WallType::Bloch => {
Vector3::new(theta.cos(), theta.sin(), 0.0)
},
WallType::Neel => {
Vector3::new(theta.cos(), 0.0, theta.sin())
},
}
}
pub fn energy_density(exchange_a: f64, anisotropy_k: f64) -> f64 {
4.0 * (exchange_a * anisotropy_k).sqrt()
}
#[allow(dead_code)]
pub fn velocity_stt(
&self,
current_density: f64,
spin_polarization: f64,
saturation_mag: f64,
) -> f64 {
use crate::constants::{E_CHARGE, MU_B};
let prefactor = MU_B * spin_polarization / (E_CHARGE * saturation_mag * self.width);
prefactor * current_density
}
pub fn with_center(mut self, center: f64) -> Self {
self.center = center;
self
}
pub fn with_width(mut self, width: f64) -> Self {
self.width = width;
self
}
pub fn with_type(mut self, wall_type: WallType) -> Self {
self.wall_type = wall_type;
self
}
pub fn with_normal(mut self, normal: Vector3<f64>) -> Self {
self.normal = normal.normalize();
self
}
}
impl fmt::Display for WallType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
WallType::Bloch => write!(f, "Bloch"),
WallType::Neel => write!(f, "Néel"),
}
}
}
impl fmt::Display for DomainWall {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"DomainWall[{}]: δ={:.1} nm, center={:.1} nm",
self.wall_type,
self.width * 1e9,
self.center * 1e9
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wall_width_calculation() {
let a_ex = 1.0e-11; let k_u = 1.0e5;
let width = DomainWall::calculate_width(a_ex, k_u);
assert!(width > 0.0);
assert!(width < 1.0e-6); }
#[test]
fn test_magnetization_profile() {
let wall = DomainWall::new(0.0, 10.0e-9, WallType::Bloch);
let m_left = wall.magnetization_at(-100.0e-9);
assert!(m_left.x.abs() > 0.9);
let m_right = wall.magnetization_at(100.0e-9);
assert!((m_right.x - (-1.0)).abs() < 0.1 || (m_right.x - 1.0).abs() < 0.1);
let m_center = wall.magnetization_at(0.0);
assert!(m_center.y.abs() > 0.5 || m_center.z.abs() > 0.5);
}
#[test]
fn test_wall_energy() {
let a_ex = 1.0e-11;
let k_u = 1.0e5;
let energy = DomainWall::energy_density(a_ex, k_u);
assert!(energy > 0.0);
assert!(energy < 1.0); }
#[test]
fn test_default_domain_wall() {
let dw = DomainWall::default();
assert_eq!(dw.wall_type, WallType::Bloch);
assert_eq!(dw.width, 10.0e-9);
assert_eq!(dw.center, 0.0);
assert_eq!(dw.normal.x, 1.0);
assert_eq!(dw.normal.y, 0.0);
assert_eq!(dw.normal.z, 0.0);
}
#[test]
fn test_wall_types() {
let bloch = DomainWall::new(0.0, 10.0e-9, WallType::Bloch);
let neel = DomainWall::new(0.0, 10.0e-9, WallType::Neel);
let m_bloch = bloch.magnetization_at(0.0);
let m_neel = neel.magnetization_at(0.0);
assert!(m_bloch.y.abs() > m_neel.y.abs());
assert!(m_neel.z.abs() > m_bloch.z.abs());
}
}