2022-08-24 22:27:40 +00:00
|
|
|
use crate::cross::vec::Vec3;
|
|
|
|
use crate::geom::{Coord as _, Index, Meters};
|
|
|
|
use crate::real::Real;
|
|
|
|
use coremem_cross::dim::DimSlice;
|
|
|
|
use coremem_cross::vec::Vec3u;
|
|
|
|
|
|
|
|
use std::borrow::Cow;
|
|
|
|
use std::ops::Deref;
|
|
|
|
|
|
|
|
use rand;
|
|
|
|
|
|
|
|
mod fields;
|
|
|
|
mod time_varying;
|
|
|
|
mod vector_field;
|
|
|
|
pub use fields::{Fields, FieldMags};
|
|
|
|
pub use time_varying::{
|
|
|
|
Exp,
|
|
|
|
Gated,
|
|
|
|
Pulse,
|
|
|
|
Scaled,
|
|
|
|
Shifted,
|
|
|
|
Sinusoid,
|
|
|
|
Summed,
|
|
|
|
TimeVarying,
|
2022-08-24 22:43:54 +00:00
|
|
|
TimeVaryingExt,
|
2022-08-24 22:27:40 +00:00
|
|
|
UnitEH,
|
|
|
|
};
|
|
|
|
pub use vector_field::{
|
|
|
|
CurlVectorField,
|
|
|
|
RegionGated,
|
|
|
|
VectorField,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
pub trait Stimulus<R: Real>: Sync {
|
|
|
|
/// Return the (E, H) field which should be added PER-SECOND to the provided position/time.
|
|
|
|
fn at(&self, t_sec: R, feat_size: R, loc: Index) -> Fields<R>;
|
|
|
|
|
|
|
|
/// compute the value of this stimulus across all the simulation space
|
|
|
|
fn rendered<'a>(
|
|
|
|
&'a self, scale: R, t_sec: R, feature_size: R, dim: Vec3u
|
|
|
|
) -> Cow<'a, RenderedStimulus<R>> {
|
|
|
|
Cow::Owned(render_stim(self, scale, t_sec, feature_size, dim))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn render_stim<R: Real, S: Stimulus<R> + ?Sized>(
|
|
|
|
stim: &S, scale: R, t_sec: R, feature_size: R, dim: Vec3u
|
|
|
|
) -> RenderedStimulus<R> {
|
|
|
|
let dim_len = dim.product_sum_usize();
|
|
|
|
let mut e = Vec::new();
|
|
|
|
e.resize_with(dim_len, Default::default);
|
|
|
|
let mut h = Vec::new();
|
|
|
|
h.resize_with(dim_len, Default::default);
|
|
|
|
|
|
|
|
rayon::scope(|s| {
|
|
|
|
let mut undispatched_e = &mut e[..];
|
|
|
|
let mut undispatched_h = &mut h[..];
|
|
|
|
for z in 0..dim.z() {
|
|
|
|
for y in 0..dim.y() {
|
|
|
|
let (this_e, this_h);
|
|
|
|
(this_e, undispatched_e) = undispatched_e.split_at_mut(dim.x() as usize);
|
|
|
|
(this_h, undispatched_h) = undispatched_h.split_at_mut(dim.x() as usize);
|
|
|
|
s.spawn(move |_| {
|
|
|
|
for (x, (out_e, out_h)) in this_e.iter_mut().zip(this_h.iter_mut()).enumerate() {
|
|
|
|
|
|
|
|
let Fields { e, h } = stim.at(t_sec, feature_size, Index::new(x as u32, y, z));
|
|
|
|
*out_e = e * scale;
|
|
|
|
*out_h = h * scale;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let field_e = DimSlice::new(dim, e);
|
|
|
|
let field_h = DimSlice::new(dim, h);
|
|
|
|
RenderedStimulus::new(
|
|
|
|
field_e, field_h, scale, feature_size, t_sec
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct RenderedStimulus<R> {
|
|
|
|
e: DimSlice<Vec<Vec3<R>>>,
|
|
|
|
h: DimSlice<Vec<Vec3<R>>>,
|
|
|
|
scale: R,
|
|
|
|
feature_size: R,
|
|
|
|
t_sec: R,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<R> RenderedStimulus<R> {
|
|
|
|
pub fn new(
|
|
|
|
e: DimSlice<Vec<Vec3<R>>>,
|
|
|
|
h: DimSlice<Vec<Vec3<R>>>,
|
|
|
|
scale: R,
|
|
|
|
feature_size: R,
|
|
|
|
t_sec: R,
|
|
|
|
) -> Self {
|
|
|
|
Self { e, h, scale, feature_size, t_sec }
|
|
|
|
}
|
|
|
|
pub fn e<'a>(&'a self) -> DimSlice<&'a [Vec3<R>]> {
|
|
|
|
self.e.as_ref()
|
|
|
|
}
|
|
|
|
pub fn h<'a>(&'a self) -> DimSlice<&'a [Vec3<R>]> {
|
|
|
|
self.h.as_ref()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<R: Real> RenderedStimulus<R> {
|
|
|
|
pub fn scale(&self) -> R {
|
|
|
|
self.scale
|
|
|
|
}
|
|
|
|
pub fn feature_size(&self) -> R {
|
|
|
|
self.feature_size
|
|
|
|
}
|
|
|
|
pub fn time(&self) -> R {
|
|
|
|
self.t_sec
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: is this necessary?
|
|
|
|
impl<R: Real> VectorField<R> for RenderedStimulus<R> {
|
|
|
|
fn at(&self, _feat_size: R, loc: Index) -> Fields<R> {
|
|
|
|
Fields::new_eh(self.e[loc.into()], self.h[loc.into()])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<R: Real> Stimulus<R> for RenderedStimulus<R> {
|
|
|
|
fn at(&self, _t_sec: R, _feat_size: R, loc: Index) -> Fields<R> {
|
|
|
|
Fields::new_eh(self.e[loc.into()], self.h[loc.into()])
|
|
|
|
}
|
|
|
|
fn rendered<'a>(
|
|
|
|
&'a self, scale: R, t_sec: R, feature_size: R, dim: Vec3u
|
|
|
|
) -> Cow<'a, RenderedStimulus<R>> {
|
|
|
|
if (self.scale, self.t_sec, self.feature_size, self.e.dim()) == (scale, t_sec, feature_size, dim) {
|
|
|
|
Cow::Borrowed(self)
|
|
|
|
} else {
|
|
|
|
Cow::Owned(render_stim(self, scale, t_sec, feature_size, dim))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<R: Real> Stimulus<R> for Fields<R> {
|
|
|
|
fn at(&self, _t_sec: R, _feat_size: R, _loc: Index) -> Fields<R> {
|
|
|
|
*self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// a VectorField type whose amplitude is modulated by a TimeVarying component.
|
|
|
|
/// users will almost always use this as their stimulus implementation
|
|
|
|
pub struct ModulatedVectorField<V, T> {
|
|
|
|
fields: V,
|
|
|
|
modulation: T,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<V, T> ModulatedVectorField<V, T> {
|
|
|
|
pub fn new(fields: V, modulation: T) -> Self {
|
|
|
|
Self { fields, modulation }
|
|
|
|
}
|
|
|
|
pub fn into_inner(self) -> (V, T) {
|
|
|
|
(self.fields, self.modulation)
|
|
|
|
}
|
|
|
|
pub fn fields(&self) -> &V {
|
|
|
|
&self.fields
|
|
|
|
}
|
|
|
|
pub fn modulation(&self) -> &T {
|
|
|
|
&self.modulation
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<R: Real, V: VectorField<R> + Sync, T: TimeVarying<R> + Sync> Stimulus<R> for ModulatedVectorField<V, T> {
|
|
|
|
fn at(&self, t_sec: R, feat_size: R, loc: Index) -> Fields<R> {
|
|
|
|
self.fields.at(feat_size, loc).elem_mul(self.modulation.at(t_sec))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// used as a MapVisitor in order to evaluate each Stimulus in a List at a specific time/place.
|
|
|
|
// struct StimulusEvaluator {
|
|
|
|
// fields: Fields,
|
|
|
|
// t_sec: f32,
|
|
|
|
// feat_size: f32,
|
|
|
|
// loc: Index,
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// impl<S: Stimulus> Visitor<&S> for &mut StimulusEvaluator {
|
|
|
|
// fn visit(&mut self, next: &S) {
|
|
|
|
// self.fields += next.at(self.t_sec, self.feat_size, self.loc);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// impl<L: Sync> Stimulus for L
|
|
|
|
// where
|
|
|
|
// for<'a, 'b> &'a L: Visit<&'b mut StimulusEvaluator>,
|
|
|
|
// {
|
|
|
|
// fn at(&self, t_sec: f32, pos: Meters) -> Fields {
|
|
|
|
// let mut ev = StimulusEvaluator { t_sec, pos, fields: Fields::default()};
|
|
|
|
// self.visit(&mut ev);
|
|
|
|
// ev.fields
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// conflicts with List implementation
|
|
|
|
// impl<T: Stimulus> Stimulus for &T {
|
|
|
|
// fn at(&self, t_sec: f32, feat_size: f32, loc: Index) -> Fields {
|
|
|
|
// (*self).at(t_sec, feat_size, loc)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
pub struct StimuliVec<S>(Vec<S>);
|
|
|
|
|
|
|
|
pub type DynStimuli<R> = StimuliVec<Box<dyn Stimulus<R> + Send>>;
|
|
|
|
|
|
|
|
impl<S> Default for StimuliVec<S> {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self(Vec::new())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<S> Deref for StimuliVec<S> {
|
|
|
|
type Target = Vec<S>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<S> StimuliVec<S> {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self::default()
|
|
|
|
}
|
|
|
|
pub fn from_vec(stim: Vec<S>) -> Self {
|
|
|
|
Self(stim)
|
|
|
|
}
|
|
|
|
pub fn push(&mut self, a: S) {
|
|
|
|
self.0.push(a)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<R: Real, S: Stimulus<R>> Stimulus<R> for StimuliVec<S> {
|
|
|
|
fn at(&self, t_sec: R, feat_size: R, loc: Index) -> Fields<R> {
|
|
|
|
self.0.iter().map(|i| i.at(t_sec, feat_size, loc))
|
|
|
|
.fold(Fields::default(), core::ops::Add::add)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<R: Real> Stimulus<R> for Box<dyn Stimulus<R> + Send> {
|
|
|
|
fn at(&self, t_sec: R, feat_size: R, loc: Index) -> Fields<R> {
|
|
|
|
(**self).at(t_sec, feat_size, loc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct NoopStimulus;
|
|
|
|
impl<R: Real> Stimulus<R> for NoopStimulus {
|
|
|
|
fn at(&self, _t_sec: R, _feat_size: R, _loc: Index) -> Fields<R> {
|
|
|
|
Fields::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct RngStimulus {
|
|
|
|
seed: u64,
|
|
|
|
e_scale: f32,
|
|
|
|
h_scale: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RngStimulus {
|
|
|
|
pub fn new(seed: u64) -> Self {
|
|
|
|
Self { seed, e_scale: 1e15, h_scale: 1e15 }
|
|
|
|
}
|
|
|
|
pub fn new_e(seed: u64) -> Self {
|
|
|
|
Self { seed, e_scale: 1e15, h_scale: 0.0 }
|
|
|
|
}
|
|
|
|
fn gen(&self, t_sec: f32, pos: Meters, scale: f32, salt: u64) -> Vec3<f32> {
|
|
|
|
use rand::{Rng as _, SeedableRng as _};
|
|
|
|
let seed = self.seed
|
|
|
|
^ (t_sec.to_bits() as u64)
|
|
|
|
^ ((pos.x().to_bits() as u64) << 8)
|
|
|
|
^ ((pos.y().to_bits() as u64) << 16)
|
|
|
|
^ ((pos.z().to_bits() as u64) << 24)
|
|
|
|
^ (salt << 32);
|
|
|
|
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
|
|
|
|
Vec3::new(
|
|
|
|
rng.gen_range(-scale..=scale),
|
|
|
|
rng.gen_range(-scale..=scale),
|
|
|
|
rng.gen_range(-scale..=scale),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<R: Real> Stimulus<R> for RngStimulus {
|
|
|
|
fn at(&self, t_sec: R, feat_size: R, loc: Index) -> Fields<R> {
|
|
|
|
Fields {
|
|
|
|
e: self.gen(t_sec.cast(), loc.to_meters(feat_size.cast()), self.e_scale, 0).cast(),
|
|
|
|
h: self.gen(t_sec.cast(), loc.to_meters(feat_size.cast()), self.h_scale, 0x7de3).cast(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|