use super::fields::Fields;
use super::operator::{Hooks, Operator, StepContext};
use crate::axis::Axis;
pub type Amplitude = Box<dyn Fn(f64) -> f64 + Send>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SourceKind {
#[default]
SoftE,
HardE,
SoftH,
HardH,
}
impl SourceKind {
pub fn is_magnetic(self) -> bool {
matches!(self, SourceKind::SoftH | SourceKind::HardH)
}
pub fn is_hard(self) -> bool {
matches!(self, SourceKind::HardE | SourceKind::HardH)
}
}
pub struct EdgeSource {
kind: SourceKind,
edges: Vec<(Axis, [usize; 3], f32)>,
amplitude: Amplitude,
}
impl core::fmt::Debug for EdgeSource {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("EdgeSource")
.field("kind", &self.kind)
.field("edges", &self.edges)
.finish_non_exhaustive()
}
}
impl EdgeSource {
pub fn new(amplitude: impl Fn(f64) -> f64 + Send + 'static) -> Self {
Self {
kind: SourceKind::SoftE,
edges: Vec::new(),
amplitude: Box::new(amplitude),
}
}
pub fn kind(mut self, kind: SourceKind) -> Self {
self.kind = kind;
self
}
pub fn get_kind(&self) -> SourceKind {
self.kind
}
pub fn edge(mut self, axis: Axis, node: [usize; 3], weight: f64) -> Self {
self.edges.push((axis, node, weight as f32));
self
}
pub fn edges(&self) -> impl Iterator<Item = (Axis, [usize; 3], f64)> + '_ {
self.edges.iter().map(|&(a, n, w)| (a, n, w as f64))
}
fn apply(&self, arr: &mut super::fields::FieldArrays<'_>, t: f64) {
let a = (self.amplitude)(t) as f32;
if self.kind.is_hard() {
for &(axis, node, w) in &self.edges {
arr.set(axis, node, w * a);
}
} else {
for &(axis, node, w) in &self.edges {
*arr.get_mut(axis, node) += w * a;
}
}
}
}
impl Operator for EdgeSource {
fn hooks(&self) -> Hooks {
if self.kind.is_magnetic() {
Hooks::POST_H
} else {
Hooks::POST_E
}
}
fn post_h(&mut self, fields: &mut Fields<'_>, ctx: &StepContext<'_>) {
let t = match self.kind {
SourceKind::SoftH => ctx.time(),
SourceKind::HardH => ctx.h_time(),
_ => return,
};
self.apply(&mut fields.i, t);
}
fn post_e(&mut self, fields: &mut Fields<'_>, ctx: &StepContext<'_>) {
let t = match self.kind {
SourceKind::SoftE => ctx.h_time(),
SourceKind::HardE => ctx.e_time(),
_ => return,
};
self.apply(&mut fields.u, t);
}
}