use pyo3::prelude::*;
use super::materials::PyFerromagnet;
use super::vector::PyVector3;
use crate::constants::GAMMA;
use crate::dynamics::calc_dm_dt;
use crate::material::Ferromagnet;
use crate::vector3::Vector3;
#[pyclass(name = "LlgSimulator")]
pub struct PyLlgSimulator {
material: Ferromagnet,
magnetization: Vector3<f64>,
external_field: Vector3<f64>,
time: f64,
}
#[pymethods]
impl PyLlgSimulator {
#[new]
pub fn new(material: &PyFerromagnet) -> Self {
Self {
material: material.inner().clone(),
magnetization: Vector3::new(1.0, 0.0, 0.0),
external_field: Vector3::new(0.0, 0.0, 0.0),
time: 0.0,
}
}
pub fn set_magnetization(&mut self, mx: f64, my: f64, mz: f64) {
self.magnetization = Vector3::new(mx, my, mz).normalize();
}
pub fn get_magnetization(&self) -> PyVector3 {
PyVector3::from_inner(self.magnetization)
}
pub fn set_external_field(&mut self, hx: f64, hy: f64, hz: f64) {
self.external_field = Vector3::new(hx, hy, hz);
}
pub fn get_external_field(&self) -> PyVector3 {
PyVector3::from_inner(self.external_field)
}
#[getter]
pub fn time(&self) -> f64 {
self.time
}
pub fn reset_time(&mut self) {
self.time = 0.0;
}
pub fn dm_dt(&self) -> PyVector3 {
let dm_dt = calc_dm_dt(
self.magnetization,
self.external_field,
GAMMA,
self.material.alpha,
);
PyVector3::from_inner(dm_dt)
}
pub fn step_rk4(&mut self, dt: f64) {
let m = self.magnetization;
let h = self.external_field;
let alpha = self.material.alpha;
let k1 = calc_dm_dt(m, h, GAMMA, alpha);
let k2 = calc_dm_dt((m + k1 * (dt / 2.0)).normalize(), h, GAMMA, alpha);
let k3 = calc_dm_dt((m + k2 * (dt / 2.0)).normalize(), h, GAMMA, alpha);
let k4 = calc_dm_dt((m + k3 * dt).normalize(), h, GAMMA, alpha);
self.magnetization = (m + (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (dt / 6.0)).normalize();
self.time += dt;
}
pub fn step_euler(&mut self, dt: f64) {
let dm_dt = calc_dm_dt(
self.magnetization,
self.external_field,
GAMMA,
self.material.alpha,
);
self.magnetization = (self.magnetization + dm_dt * dt).normalize();
self.time += dt;
}
#[pyo3(signature = (duration, n_steps, method = "rk4"))]
pub fn evolve(
&mut self,
duration: f64,
n_steps: usize,
method: &str,
) -> Vec<(f64, f64, f64, f64)> {
let dt = duration / n_steps as f64;
let mut trajectory = Vec::with_capacity(n_steps + 1);
trajectory.push((
self.time,
self.magnetization.x,
self.magnetization.y,
self.magnetization.z,
));
for _ in 0..n_steps {
match method {
"euler" => self.step_euler(dt),
_ => self.step_rk4(dt),
}
trajectory.push((
self.time,
self.magnetization.x,
self.magnetization.y,
self.magnetization.z,
));
}
trajectory
}
pub fn precession_frequency(&self) -> f64 {
GAMMA * self.external_field.magnitude()
}
pub fn precession_period(&self) -> f64 {
let omega = self.precession_frequency();
if omega > 0.0 {
2.0 * std::f64::consts::PI / omega
} else {
f64::INFINITY
}
}
pub fn __repr__(&self) -> String {
format!(
"LlgSimulator(m=({:.3}, {:.3}, {:.3}), H=({:.3}, {:.3}, {:.3}), t={:.2e}s)",
self.magnetization.x,
self.magnetization.y,
self.magnetization.z,
self.external_field.x,
self.external_field.y,
self.external_field.z,
self.time
)
}
}