use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::types::PyDict;
use crate::dynamics::llb::{LlbMaterial, LlbResult, LlbSolver};
use crate::vector3::Vector3;
#[pyclass(name = "LlbMaterial")]
#[derive(Clone)]
pub struct PyLlbMaterial {
inner: LlbMaterial,
}
#[pymethods]
impl PyLlbMaterial {
#[new]
pub fn new(curie_temp: f64, alpha: f64, spin_s: f64, ms_0: f64) -> Self {
Self {
inner: LlbMaterial {
curie_temp,
alpha,
spin_s,
ms_0,
},
}
}
#[staticmethod]
pub fn iron() -> Self {
Self {
inner: LlbMaterial::iron(),
}
}
#[staticmethod]
pub fn nickel() -> Self {
Self {
inner: LlbMaterial::nickel(),
}
}
#[staticmethod]
pub fn cofeb() -> Self {
Self {
inner: LlbMaterial::cofeb(),
}
}
pub fn equilibrium_magnetization(&self, temperature: f64) -> f64 {
self.inner.equilibrium_magnetization(temperature)
}
pub fn alpha_parallel(&self, temperature: f64) -> f64 {
self.inner.alpha_parallel(temperature)
}
pub fn alpha_perp(&self, temperature: f64) -> f64 {
self.inner.alpha_perp(temperature)
}
#[getter]
pub fn curie_temp(&self) -> f64 {
self.inner.curie_temp
}
#[getter]
pub fn alpha(&self) -> f64 {
self.inner.alpha
}
#[getter]
pub fn spin_s(&self) -> f64 {
self.inner.spin_s
}
#[getter]
pub fn ms_0(&self) -> f64 {
self.inner.ms_0
}
pub fn __repr__(&self) -> String {
format!(
"LlbMaterial(curie_temp={:.1}, alpha={:.4}, spin_s={:.2}, ms_0={:.3e})",
self.inner.curie_temp, self.inner.alpha, self.inner.spin_s, self.inner.ms_0
)
}
}
impl PyLlbMaterial {
pub fn inner(&self) -> &LlbMaterial {
&self.inner
}
}
#[pyclass(name = "LlbSolver")]
pub struct PyLlbSolver {
inner: LlbSolver,
}
#[pymethods]
impl PyLlbSolver {
#[new]
pub fn new(material: &PyLlbMaterial, dt: f64, temperature: f64, h_ext: [f64; 3]) -> Self {
let h = Vector3::new(h_ext[0], h_ext[1], h_ext[2]);
Self {
inner: LlbSolver::new(material.inner.clone(), dt, temperature, h),
}
}
pub fn step(&self, m: [f64; 3]) -> PyResult<[f64; 3]> {
let m_vec = Vector3::new(m[0], m[1], m[2]);
let m_new = self
.inner
.step(&m_vec)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
Ok([m_new.x, m_new.y, m_new.z])
}
pub fn run(
&self,
py: Python<'_>,
m0: [f64; 3],
num_steps: usize,
record_every: usize,
) -> PyResult<PyObject> {
let m0_vec = Vector3::new(m0[0], m0[1], m0[2]);
let result: LlbResult = self
.inner
.run(m0_vec, num_steps, record_every)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
let mx: Vec<f64> = result.trajectory.iter().map(|v| v.x).collect();
let my: Vec<f64> = result.trajectory.iter().map(|v| v.y).collect();
let mz: Vec<f64> = result.trajectory.iter().map(|v| v.z).collect();
let dict = PyDict::new(py);
dict.set_item("mx", mx)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
dict.set_item("my", my)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
dict.set_item("mz", mz)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
dict.set_item("m_magnitude", result.m_magnitude)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
dict.set_item("time", result.time)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
dict.set_item("equilibrium_m", result.equilibrium_m)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
Ok(dict.into())
}
#[getter]
pub fn dt(&self) -> f64 {
self.inner.dt
}
#[getter]
pub fn temperature(&self) -> f64 {
self.inner.temperature
}
#[getter]
pub fn h_ext(&self) -> (f64, f64, f64) {
(self.inner.h_ext.x, self.inner.h_ext.y, self.inner.h_ext.z)
}
#[getter]
pub fn gamma(&self) -> f64 {
self.inner.gamma
}
pub fn __repr__(&self) -> String {
format!(
"LlbSolver(T={:.1} K, dt={:.2e} s, H=({:.3}, {:.3}, {:.3}) T)",
self.inner.temperature,
self.inner.dt,
self.inner.h_ext.x,
self.inner.h_ext.y,
self.inner.h_ext.z
)
}
}