use std::f64::consts::PI;
use crate::vector3::Vector3;
pub struct TopologicalCharge;
impl TopologicalCharge {
pub fn density(m: Vector3<f64>, dm_dx: Vector3<f64>, dm_dy: Vector3<f64>) -> f64 {
let cross = dm_dx.cross(&dm_dy);
m.dot(&cross) / (4.0 * PI)
}
}
pub fn calculate_skyrmion_number(magnetization: &[Vec<Vector3<f64>>], dx: f64, dy: f64) -> f64 {
let nx = magnetization.len();
if nx == 0 {
return 0.0;
}
let ny = magnetization[0].len();
if ny == 0 {
return 0.0;
}
let mut total_charge = 0.0;
for i in 1..nx - 1 {
for j in 1..ny - 1 {
let m = magnetization[i][j];
let dm_dx = (magnetization[i + 1][j] - magnetization[i - 1][j]) * (0.5 / dx);
let dm_dy = (magnetization[i][j + 1] - magnetization[i][j - 1]) * (0.5 / dy);
let charge_density = TopologicalCharge::density(m, dm_dx, dm_dy);
total_charge += charge_density * dx * dy;
}
}
total_charge
}
pub fn winding_number(path: &[Vector3<f64>]) -> f64 {
if path.len() < 3 {
return 0.0;
}
let mut total_angle = 0.0;
for i in 0..path.len() {
let m1 = path[i];
let m2 = path[(i + 1) % path.len()];
let angle1 = m1.y.atan2(m1.x);
let angle2 = m2.y.atan2(m2.x);
let mut delta = angle2 - angle1;
if delta > PI {
delta -= 2.0 * PI;
} else if delta < -PI {
delta += 2.0 * PI;
}
total_angle += delta;
}
total_angle / (2.0 * PI)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_charge_density_uniform() {
let m = Vector3::new(0.0, 0.0, 1.0);
let dm_dx = Vector3::new(0.0, 0.0, 0.0);
let dm_dy = Vector3::new(0.0, 0.0, 0.0);
let density = TopologicalCharge::density(m, dm_dx, dm_dy);
assert!(density.abs() < 1e-20);
}
#[test]
fn test_skyrmion_number_uniform() {
let mag = vec![vec![Vector3::new(0.0, 0.0, 1.0); 10]; 10];
let q = calculate_skyrmion_number(&mag, 1.0e-9, 1.0e-9);
assert!(q.abs() < 0.1); }
#[test]
fn test_winding_number_vortex() {
let n_points = 8;
let mut path = Vec::new();
for i in 0..n_points {
let angle = 2.0 * PI * (i as f64) / (n_points as f64);
path.push(Vector3::new(angle.cos(), angle.sin(), 0.0));
}
let w = winding_number(&path);
assert!((w - 1.0).abs() < 0.2); }
}