use pyo3::prelude::*;
use super::vector::PyVector3;
use crate::constants::GAMMA;
use crate::dynamics::calc_dm_dt;
use crate::effect::InverseSpinHall;
use crate::material::{Ferromagnet, SpinInterface};
use crate::transport::spin_pumping_current;
use crate::vector3::Vector3;
#[pyclass(name = "SpinPumpingSimulation")]
pub struct PySpinPumpingSimulation {
ferromagnet: Ferromagnet,
interface: SpinInterface,
detector: InverseSpinHall,
magnetization: Vector3<f64>,
external_field: Vector3<f64>,
sample_length: f64,
}
#[pymethods]
impl PySpinPumpingSimulation {
#[new]
pub fn new() -> Self {
Self {
ferromagnet: Ferromagnet::yig(),
interface: SpinInterface::yig_pt(),
detector: InverseSpinHall::platinum(),
magnetization: Vector3::new(1.0, 0.0, 0.0),
external_field: Vector3::new(0.0, 0.0, 0.1),
sample_length: 5e-3, }
}
pub fn set_sample_length(&mut self, length: f64) {
self.sample_length = length;
}
pub fn set_field(&mut self, hx: f64, hy: f64, hz: f64) {
self.external_field = Vector3::new(hx, hy, hz);
}
pub fn set_magnetization(&mut self, mx: f64, my: f64, mz: f64) {
self.magnetization = Vector3::new(mx, my, mz).normalize();
}
pub fn set_fmr_conditions(&mut self, _frequency: f64, field: f64) {
self.external_field = Vector3::new(0.0, 0.0, field);
self.magnetization = Vector3::new(1.0, 0.0, 0.0);
}
pub fn run(&mut self, duration: f64, n_steps: usize) -> PyResult<PyObject> {
let dt = duration / n_steps as f64;
let alpha = self.ferromagnet.alpha;
let mut times = Vec::with_capacity(n_steps + 1);
let mut mx_vals = Vec::with_capacity(n_steps + 1);
let mut my_vals = Vec::with_capacity(n_steps + 1);
let mut mz_vals = Vec::with_capacity(n_steps + 1);
let mut js_vals = Vec::with_capacity(n_steps + 1);
let mut voltage_vals = Vec::with_capacity(n_steps + 1);
let mut time = 0.0;
let mut m = self.magnetization;
let h = self.external_field;
let js_flow = self.interface.normal;
for _ in 0..=n_steps {
times.push(time);
mx_vals.push(m.x);
my_vals.push(m.y);
mz_vals.push(m.z);
let dm_dt = calc_dm_dt(m, h, GAMMA, alpha);
let js = spin_pumping_current(&self.interface, m, dm_dt);
let js_mag = js.magnitude();
js_vals.push(js_mag);
let e_field = self.detector.convert(js_flow, js);
let voltage = e_field.magnitude() * self.sample_length;
voltage_vals.push(voltage);
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);
m = (m + (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (dt / 6.0)).normalize();
time += dt;
}
let peak_voltage = voltage_vals.iter().cloned().fold(0.0_f64, f64::max);
let avg_voltage: f64 = voltage_vals.iter().sum::<f64>() / voltage_vals.len() as f64;
let peak_js = js_vals.iter().cloned().fold(0.0_f64, f64::max);
Python::with_gil(|py| {
let dict = pyo3::types::PyDict::new(py);
dict.set_item("times", times)?;
dict.set_item("mx", mx_vals)?;
dict.set_item("my", my_vals)?;
dict.set_item("mz", mz_vals)?;
dict.set_item("spin_current", js_vals)?;
dict.set_item("voltage", voltage_vals)?;
dict.set_item("peak_voltage", peak_voltage)?;
dict.set_item("avg_voltage", avg_voltage)?;
dict.set_item("peak_spin_current", peak_js)?;
Ok(dict.into())
})
}
pub fn get_magnetization(&self) -> PyVector3 {
PyVector3::from_inner(self.magnetization)
}
pub fn __repr__(&self) -> String {
format!(
"SpinPumpingSimulation(material=YIG, detector=Pt, field={:.3} T)",
self.external_field.magnitude()
)
}
}
impl Default for PySpinPumpingSimulation {
fn default() -> Self {
Self::new()
}
}