Stabilize time as f32.

This commit is contained in:
2021-06-07 18:25:11 -07:00
parent 901d2334f1
commit 8779371f42
12 changed files with 288 additions and 261 deletions

View File

@@ -4,7 +4,7 @@ use coremem::stim::{CurlStimulus, Sinusoid1, TimeVarying1 as _};
fn main() { fn main() {
coremem::init_logging(); coremem::init_logging();
let feat_size = 10e-6; // feature size let feat_size = 10e-6f32; // feature size
let duration = 6e-9; let duration = 6e-9;
let width = 2200e-6; let width = 2200e-6;
let depth = 1800e-6; let depth = 1800e-6;
@@ -16,10 +16,10 @@ fn main() {
let peak_current = 5e11; let peak_current = 5e11;
let current_duration = 1.0e-11; // half-wavelength of the sine wave let current_duration = 1.0e-11; // half-wavelength of the sine wave
let current_break = 0.5e-11; // time between 'set' pulse and 'clear' pulse let current_break = 0.5e-11; // time between 'set' pulse and 'clear' pulse
let conductivity = 1.0e9; let conductivity = 1.0e9f32;
let from_m = |m: Flt| (m/feat_size).round() as u32; let from_m = |m: f32| (m/feat_size).round() as u32;
let m_to_um = |m: Flt| (m * 1e6).round() as u32; let m_to_um = |m: f32| (m * 1e6).round() as u32;
let half_width = width * 0.5; let half_width = width * 0.5;
let half_depth = depth * 0.5; let half_depth = depth * 0.5;
@@ -49,8 +49,8 @@ fn main() {
); );
driver.fill_region(&ferro_region, mat::db::minimal_square_ferrite().into()); driver.fill_region(&ferro_region, mat::db::minimal_square_ferrite().into());
driver.fill_region(&drive_region, mat::db::conductor(conductivity).into()); driver.fill_region(&drive_region, mat::db::conductor(conductivity as _).into());
driver.fill_region(&sense_region, mat::db::conductor(conductivity).into()); driver.fill_region(&sense_region, mat::db::conductor(conductivity as _).into());
// driver.add_pml_boundary(Meters((boundary_xy, boundary_xy, boundary_z).into())); // driver.add_pml_boundary(Meters((boundary_xy, boundary_xy, boundary_z).into()));
driver.add_classical_boundary(Meters::new(boundary_xy, boundary_xy, boundary_z)); driver.add_classical_boundary(Meters::new(boundary_xy, boundary_xy, boundary_z));
@@ -61,10 +61,10 @@ fn main() {
// if I = k*sin(w t) then dE/dt = k*w sin(w t) / (A*\sigma) // if I = k*sin(w t) then dE/dt = k*w sin(w t) / (A*\sigma)
// i.e. dE/dt is proportional to I/(A*\sigma), multiplied by w (or, divided by wavelength) // i.e. dE/dt is proportional to I/(A*\sigma), multiplied by w (or, divided by wavelength)
let peak_stim = peak_current/current_duration / (drive_region.cross_section() * conductivity); let peak_stim = peak_current/current_duration / (drive_region.cross_section() * conductivity);
let pos_wave = Sinusoid1::from_wavelength(peak_stim, current_duration * 2.0) let pos_wave = Sinusoid1::from_wavelength(peak_stim as _, current_duration * 2.0)
.half_cycle(); .half_cycle();
let neg_wave = Sinusoid1::from_wavelength(-peak_stim, current_duration * 2.0) let neg_wave = Sinusoid1::from_wavelength(-peak_stim as _, current_duration * 2.0)
.half_cycle() .half_cycle()
.shifted(current_duration + current_break); .shifted(current_duration + current_break);

View File

@@ -6,11 +6,11 @@ use log::trace;
fn main() { fn main() {
coremem::init_logging(); coremem::init_logging();
for p in 0..20 { for p in 0..20 {
let ferro_depth = 10e-6 * (20-p) as Flt; let ferro_depth = 10e-6 * (20-p) as f32;
let feat_size = 10e-6; // feature size let feat_size = 10e-6f32; // feature size
let from_m = |m| (m/feat_size) as u32; let from_m = |m| (m/feat_size) as u32;
let m_to_um = |px| (px * 1e6) as u32; let m_to_um = |px| (px * 1e6) as u32;
let to_m = |px| px as Flt * feat_size; let to_m = |px| px as f32 * feat_size;
let width = 2500e-6; let width = 2500e-6;
let depth = 200e-6; let depth = 200e-6;
let conductor_inner_rad = 0e-6; let conductor_inner_rad = 0e-6;
@@ -91,7 +91,7 @@ fn main() {
Meters::new(half_width + 0.5 * (ferro_inner_rad + ferro_outer_rad), half_width, half_depth) Meters::new(half_width + 0.5 * (ferro_inner_rad + ferro_outer_rad), half_width, half_depth)
)); ));
let center = Vec2::new(half_width as Flt, half_width as Flt); let center = Vec2::new(half_width, half_width);
for z_px in 0..depth_px { for z_px in 0..depth_px {
for y_px in 0..width_px { for y_px in 0..width_px {
@@ -99,9 +99,9 @@ fn main() {
let loc = Index((x_px, y_px, z_px).into()); let loc = Index((x_px, y_px, z_px).into());
let d = Vec2::new(to_m(x_px), to_m(y_px)) - center; let d = Vec2::new(to_m(x_px), to_m(y_px)) - center;
let r = d.mag(); let r = d.mag();
if (conductor_inner_rad as Flt..conductor_outer_rad as Flt).contains(&r) { if (conductor_inner_rad..conductor_outer_rad).contains(&r) {
driver.state.put_material(loc, mat::db::conductor(conductivity).into()); driver.state.put_material(loc, mat::db::conductor(conductivity).into());
} else if (ferro_inner_rad as Flt..ferro_outer_rad as Flt).contains(&r) { } else if (ferro_inner_rad..ferro_outer_rad).contains(&r) {
let half_depth_px = from_m(half_depth); let half_depth_px = from_m(half_depth);
let ferro_depth_px = from_m(ferro_depth); let ferro_depth_px = from_m(ferro_depth);
if (half_depth_px-ferro_depth_px/2 .. half_depth_px+(ferro_depth_px+1)/2).contains(&z_px) { if (half_depth_px-ferro_depth_px/2 .. half_depth_px+(ferro_depth_px+1)/2).contains(&z_px) {

View File

@@ -4,7 +4,7 @@ use coremem::stim::{CurlStimulus, Sinusoid1, TimeVarying1 as _};
fn main() { fn main() {
coremem::init_logging(); coremem::init_logging();
let feat_size = 10e-6; // feature size let feat_size = 10e-6f32; // feature size
let duration = 0.5e-9; let duration = 0.5e-9;
let width = 4800e-6; let width = 4800e-6;
let height = 2200e-6; let height = 2200e-6;
@@ -17,11 +17,11 @@ fn main() {
let peak_current = 5e6; let peak_current = 5e6;
let current_duration = 0.1e-9; // half-wavelength of the sine wave let current_duration = 0.1e-9; // half-wavelength of the sine wave
let current_break = 0.1e-9; // time between 'set' pulse and 'clear' pulse let current_break = 0.1e-9; // time between 'set' pulse and 'clear' pulse
let drive_conductivity = 5.0; let drive_conductivity = 5.0f32;
let sense_conductivity = 5.0; let sense_conductivity = 5.0f32;
let from_m = |m: Flt| (m/feat_size).round() as u32; let from_m = |m: f32| (m/feat_size).round() as u32;
let m_to_um = |m: Flt| (m * 1e6).round() as u32; let m_to_um = |m: f32| (m * 1e6).round() as u32;
let half_width = width * 0.5; let half_width = width * 0.5;
let q1_width = width * 0.25; let q1_width = width * 0.25;
let q3_width = width * 0.75; let q3_width = width * 0.75;
@@ -42,16 +42,16 @@ fn main() {
let sense1_region = Torus::new_xz(Meters::new(q1_width + ferro_major, half_height, half_depth), wire_major, wire_minor); let sense1_region = Torus::new_xz(Meters::new(q1_width + ferro_major, half_height, half_depth), wire_major, wire_minor);
driver.fill_region(&ferro1_region, mat::db::linear_iron().into()); driver.fill_region(&ferro1_region, mat::db::linear_iron().into());
driver.fill_region(&drive1_region, mat::db::conductor(drive_conductivity).into()); driver.fill_region(&drive1_region, mat::db::conductor(drive_conductivity as _).into());
driver.fill_region(&sense1_region, mat::db::conductor(sense_conductivity).into()); driver.fill_region(&sense1_region, mat::db::conductor(sense_conductivity as _).into());
let ferro2_region = Torus::new_xy(Meters::new(q3_width, half_height, half_depth), ferro_major, ferro_minor); let ferro2_region = Torus::new_xy(Meters::new(q3_width, half_height, half_depth), ferro_major, ferro_minor);
let drive2_region = Torus::new_xz(Meters::new(q3_width - ferro_major, half_height, half_depth), wire_major, wire_minor); let drive2_region = Torus::new_xz(Meters::new(q3_width - ferro_major, half_height, half_depth), wire_major, wire_minor);
let sense2_region = Torus::new_xz(Meters::new(q3_width + ferro_major, half_height, half_depth), wire_major, wire_minor); let sense2_region = Torus::new_xz(Meters::new(q3_width + ferro_major, half_height, half_depth), wire_major, wire_minor);
driver.fill_region(&ferro2_region, mat::db::ferroxcube_3r1().into()); driver.fill_region(&ferro2_region, mat::db::ferroxcube_3r1().into());
driver.fill_region(&drive2_region, mat::db::conductor(drive_conductivity).into()); driver.fill_region(&drive2_region, mat::db::conductor(drive_conductivity as _).into());
driver.fill_region(&sense2_region, mat::db::conductor(sense_conductivity).into()); driver.fill_region(&sense2_region, mat::db::conductor(sense_conductivity as _).into());
let boundary_xy = q1_width - ferro_major - ferro_minor - buffer; let boundary_xy = q1_width - ferro_major - ferro_minor - buffer;
let boundary_z = half_depth - wire_major - wire_minor - buffer; let boundary_z = half_depth - wire_major - wire_minor - buffer;
@@ -65,10 +65,10 @@ fn main() {
// if I = k*sin(w t) then dE/dt = k*w sin(w t) / (A*\sigma) // if I = k*sin(w t) then dE/dt = k*w sin(w t) / (A*\sigma)
// i.e. dE/dt is proportional to I/(A*\sigma), multiplied by w (or, divided by wavelength) // i.e. dE/dt is proportional to I/(A*\sigma), multiplied by w (or, divided by wavelength)
let peak_stim = peak_current/current_duration / (drive1_region.cross_section() * drive_conductivity); let peak_stim = peak_current/current_duration / (drive1_region.cross_section() * drive_conductivity);
let pos_wave = Sinusoid1::from_wavelength(peak_stim, current_duration * 2.0) let pos_wave = Sinusoid1::from_wavelength(peak_stim as Flt, current_duration * 2.0)
.half_cycle(); .half_cycle();
let neg_wave = Sinusoid1::from_wavelength(-4.0*peak_stim, current_duration * 2.0) let neg_wave = Sinusoid1::from_wavelength(-4.0*peak_stim as Flt, current_duration * 2.0)
.half_cycle() .half_cycle()
.shifted(current_duration + current_break); .shifted(current_duration + current_break);

View File

@@ -1,18 +1,18 @@
use coremem::*; use coremem::*;
use coremem::geom::*; use coremem::geom::*;
use coremem::real::Real as _; use coremem::real::{Real as _, ToFloat as _};
use coremem::stim::AbstractStimulus; use coremem::stim::AbstractStimulus;
use rand::rngs::StdRng; use rand::rngs::StdRng;
use rand::{Rng as _, SeedableRng as _}; use rand::{Rng as _, SeedableRng as _};
fn energy<R: Region>(s: &dyn GenericSim, reg: &R) -> Flt { fn energy<R: Region>(s: &dyn GenericSim, reg: &R) -> f32 {
let e = Real::half() * s.map_sum_over_enumerated(reg, |_pos: Index, cell| { let e = f64::half() * s.map_sum_over_enumerated(reg, |_pos: Index, cell| {
cell.e().mag_sq() cell.e().mag_sq().to_f64()
}); });
Flt::from_primitive(e) e.cast()
} }
fn energy_now_and_then<R: Region>(state: &mut StaticSim, reg: &R, frames: u32) -> (Flt, Flt) { fn energy_now_and_then<R: Region>(state: &mut StaticSim, reg: &R, frames: u32) -> (f32, f32) {
let energy_0 = energy(state, reg); let energy_0 = energy(state, reg);
for _ in 0..frames { for _ in 0..frames {
state.step(); state.step();
@@ -22,13 +22,14 @@ fn energy_now_and_then<R: Region>(state: &mut StaticSim, reg: &R, frames: u32) -
} }
struct PmlStim<F> { struct PmlStim<F> {
/// Maps index -> (stim vector, stim frequency)
f: F, f: F,
t_end: Flt, t_end: f32,
feat_size: Flt, feat_size: f32,
} }
impl<F: Fn(Index) -> (Vec3, Flt) + Sync> AbstractStimulus for PmlStim<F> { impl<F: Fn(Index) -> (Vec3, f32) + Sync> AbstractStimulus for PmlStim<F> {
fn at(&self, t_sec: Flt, pos: Meters) -> Vec3 { fn at(&self, t_sec: f32, pos: Meters) -> Vec3 {
let angle = (t_sec as Flt)/self.t_end*2.0*consts::PI; let angle = t_sec/self.t_end*f32::two_pi();
let gate = 0.5*(1.0 - angle.cos()); let gate = 0.5*(1.0 - angle.cos());
let (e, hz) = (self.f)(pos.to_index(self.feat_size)); let (e, hz) = (self.f)(pos.to_index(self.feat_size));
let sig_angle = angle*hz; let sig_angle = angle*hz;
@@ -39,11 +40,11 @@ impl<F: Fn(Index) -> (Vec3, Flt) + Sync> AbstractStimulus for PmlStim<F> {
/// Apply some stimulus, and then let it decay and measure the ratio of energy left in the system /// Apply some stimulus, and then let it decay and measure the ratio of energy left in the system
fn apply_stim_full_interior<F>(state: &mut StaticSim, frames: u32, f: F) fn apply_stim_full_interior<F>(state: &mut StaticSim, frames: u32, f: F)
where F: Fn(Index) -> (Vec3, Flt) + Sync // returns (E vector, omega) where F: Fn(Index) -> (Vec3, f32) + Sync // returns (E vector, omega)
{ {
let stim = PmlStim { let stim = PmlStim {
f, f,
t_end: (frames as Flt) * state.timestep(), t_end: (frames as f32) * state.timestep(),
feat_size: state.feature_size(), feat_size: state.feature_size(),
}; };
for _t in 0..frames { for _t in 0..frames {
@@ -55,7 +56,7 @@ where F: Fn(Index) -> (Vec3, Flt) + Sync // returns (E vector, omega)
fn apply_stim_over_region<R, F>(state: &mut StaticSim, frames: u32, reg: R, f: F) fn apply_stim_over_region<R, F>(state: &mut StaticSim, frames: u32, reg: R, f: F)
where where
R: Region, R: Region,
F: Fn(Index) -> (Vec3, Flt) + Sync, F: Fn(Index) -> (Vec3, f32) + Sync,
{ {
let feat = state.feature_size(); let feat = state.feature_size();
apply_stim_full_interior(state, frames, |idx| { apply_stim_full_interior(state, frames, |idx| {
@@ -84,7 +85,7 @@ fn apply_chaotic_stim_over_region<R: Region>(state: &mut StaticSim, frames: u32,
}) })
} }
fn chaotic_pml_test(state: &mut StaticSim, boundary: u32, padding: u32, frames: u32) -> Flt { fn chaotic_pml_test(state: &mut StaticSim, boundary: u32, padding: u32, frames: u32) -> f32 {
let feat = state.feature_size(); let feat = state.feature_size();
{ {
let upper_left_idx = Index::unit()*padding; let upper_left_idx = Index::unit()*padding;
@@ -102,12 +103,12 @@ fn chaotic_pml_test(state: &mut StaticSim, boundary: u32, padding: u32, frames:
energy_1/energy_0 energy_1/energy_0
} }
fn state_for_pml(size: Index, boundary: Index, feat_size: Flt, sc_coeff: Flt, cond_coeff: Flt, pow: Flt) -> StaticSim { fn state_for_pml(size: Index, boundary: Index, feat_size: f32, sc_coeff: f32, cond_coeff: f32, pow: f32) -> StaticSim {
let mut state = StaticSim::new(size, feat_size); let mut state = StaticSim::new(size, feat_size);
let timestep = state.timestep(); let timestep = state.timestep();
state.fill_boundary_using(boundary, |boundary_ness| { state.fill_boundary_using(boundary, |boundary_ness| {
let b = boundary_ness.elem_pow(Real::from_primitive(pow)); let b = boundary_ness.elem_pow(pow);
let coord_stretch = b * Real::from_primitive(sc_coeff / timestep); let coord_stretch = b * sc_coeff / timestep;
let conductivity = Vec3::unit() * (b.mag() * cond_coeff / timestep); let conductivity = Vec3::unit() * (b.mag() * cond_coeff / timestep);
unimplemented!() unimplemented!()
// Static { // Static {
@@ -168,7 +169,7 @@ fn main() {
0.5, 0.5,
] { ] {
//for cond_coeff in vec![0.0, 1.0, 1e3, 1e6, 1e9] { //for cond_coeff in vec![0.0, 1.0, 1e3, 1e6, 1e9] {
for cond_coeff in vec![0.0, 0.5*consts::EPS0] { for cond_coeff in vec![0.0, 0.5*f32::eps0()] {
for frames in vec![400, 1200] { for frames in vec![400, 1200] {
for pml_width in vec![1, 2, 4, 8, 16] { for pml_width in vec![1, 2, 4, 8, 16] {
for feat_size in vec![1e-6] { for feat_size in vec![1e-6] {

View File

@@ -34,7 +34,7 @@ pub struct Driver<M=GenericMaterial> {
} }
impl<M: Default> Driver<M> { impl<M: Default> Driver<M> {
pub fn new<C: Coord>(size: C, feature_size: Flt) -> Self { pub fn new<C: Coord>(size: C, feature_size: f32) -> Self {
Driver { Driver {
state: SimState::new(size.to_index(feature_size), feature_size), state: SimState::new(size.to_index(feature_size), feature_size),
renderer: Arc::new(MultiRenderer::new()), renderer: Arc::new(MultiRenderer::new()),
@@ -180,7 +180,7 @@ impl<M: Material + Clone + Default + Send + Sync + 'static> Driver<M> {
} }
} }
pub fn step_until(&mut self, deadline: Flt) { pub fn step_until(&mut self, deadline: f32) {
while self.dyn_state().time() < deadline { while self.dyn_state().time() < deadline {
self.step(); self.step();
} }
@@ -194,9 +194,9 @@ impl<M: Material + From<mat::Pml>> Driver<M> {
pub fn add_pml_boundary<C: Coord>(&mut self, thickness: C) { pub fn add_pml_boundary<C: Coord>(&mut self, thickness: C) {
let timestep = self.state.timestep(); let timestep = self.state.timestep();
self.state.fill_boundary_using(thickness, |boundary_ness| { self.state.fill_boundary_using(thickness, |boundary_ness| {
let b = boundary_ness.elem_pow(flt::Real::three()); let b = boundary_ness.elem_pow(3.0);
let conductivity = b * (flt::Real::half() / timestep); let conductivity = b * (0.5 / timestep);
Pml::new(conductivity).into() Pml::new(conductivity.cast()).into()
}); });
} }
} }
@@ -205,8 +205,8 @@ impl<M: Material + From<mat::Conductor>> Driver<M> {
pub fn add_classical_boundary<C: Coord>(&mut self, thickness: C) { pub fn add_classical_boundary<C: Coord>(&mut self, thickness: C) {
let timestep = self.state.timestep(); let timestep = self.state.timestep();
self.state.fill_boundary_using(thickness, |boundary_ness| { self.state.fill_boundary_using(thickness, |boundary_ness| {
let b = boundary_ness.elem_pow(flt::Real::three()); let b = boundary_ness.elem_pow(3.0);
let cond = b * (flt::Real::half() / timestep); let cond = b * (0.5 / timestep);
let iso_cond = cond.x() + cond.y() + cond.z(); let iso_cond = cond.x() + cond.y() + cond.z();
let iso_conductor = mat::db::conductor(Flt::from_primitive(iso_cond)); let iso_conductor = mat::db::conductor(Flt::from_primitive(iso_cond));
@@ -223,8 +223,8 @@ struct StimuliAdapter {
} }
impl AbstractStimulus for StimuliAdapter { impl AbstractStimulus for StimuliAdapter {
fn at(&self, t_sec: Flt, pos: Meters) -> Vec3 { fn at(&self, t_sec: f32, pos: Meters) -> Vec3 {
self.stim.at(t_sec, pos) * flt::Real::from_f64(self.frame_interval as f64) self.stim.at(t_sec, pos) * flt::Real::from_primitive(self.frame_interval)
} }
} }

View File

@@ -1,4 +1,3 @@
use crate::flt::{Flt, Real};
use crate::geom::{Meters, Vec2, Vec3}; use crate::geom::{Meters, Vec2, Vec3};
use crate::real::Real as _; use crate::real::Real as _;
@@ -16,16 +15,16 @@ dyn_clone::clone_trait_object!(Region);
#[derive(Copy, Clone, Serialize, Deserialize)] #[derive(Copy, Clone, Serialize, Deserialize)]
pub struct CylinderZ { pub struct CylinderZ {
center: Vec2, center: Vec2<f32>,
radius: Real, radius: f32,
} }
impl CylinderZ { impl CylinderZ {
// TODO: should be unit-typed // TODO: should be unit-typed
pub fn new(center: Vec2, radius: Flt) -> Self { pub fn new(center: Vec2<f32>, radius: f32) -> Self {
Self { Self {
center, center,
radius: radius.into() radius,
} }
} }
} }
@@ -49,24 +48,24 @@ pub struct Torus {
/// Unit-length vector describing the axis of the torus /// Unit-length vector describing the axis of the torus
normal: Meters, normal: Meters,
/// Distance from origin to the "center" of the solid part of the torus /// Distance from origin to the "center" of the solid part of the torus
major_rad: Real, major_rad: f32,
/// Distance from a center-point of the torus to its surface /// Distance from a center-point of the torus to its surface
minor_rad: Real, minor_rad: f32,
} }
impl Torus { impl Torus {
pub fn new(center: Meters, normal: Meters, major_rad: Flt, minor_rad: Flt) -> Self { pub fn new(center: Meters, normal: Meters, major_rad: f32, minor_rad: f32) -> Self {
Self { Self {
center, center,
normal, normal,
major_rad: Real::from_inner(major_rad), major_rad,
minor_rad: Real::from_inner(minor_rad), minor_rad,
} }
} }
pub fn new_xy(center: Meters, major_rad: Flt, minor_rad: Flt) -> Self { pub fn new_xy(center: Meters, major_rad: f32, minor_rad: f32) -> Self {
Self::new(center, Meters(Vec3::new(0.0, 0.0, 1.0)), major_rad, minor_rad) Self::new(center, Meters(Vec3::new(0.0, 0.0, 1.0)), major_rad, minor_rad)
} }
pub fn new_xz(center: Meters, major_rad: Flt, minor_rad: Flt) -> Self { pub fn new_xz(center: Meters, major_rad: f32, minor_rad: f32) -> Self {
Self::new(center, Meters(Vec3::new(0.0, 1.0, 0.0)), major_rad, minor_rad) Self::new(center, Meters(Vec3::new(0.0, 1.0, 0.0)), major_rad, minor_rad)
} }
pub fn center(&self) -> Meters { pub fn center(&self) -> Meters {
@@ -75,9 +74,8 @@ impl Torus {
pub fn axis(&self) -> Meters { pub fn axis(&self) -> Meters {
self.normal self.normal
} }
pub fn cross_section(&self) -> Flt { pub fn cross_section(&self) -> f32 {
use crate::consts::PI; self.minor_rad * self.minor_rad * f32::pi()
(self.minor_rad * self.minor_rad * PI).into()
} }
} }
@@ -102,21 +100,21 @@ impl Region for Torus {
p_on_plane.with_mag(self.major_rad) p_on_plane.with_mag(self.major_rad)
}; };
let distance_to_circle = (rel_p - q).mag(); let distance_to_circle = (rel_p - q).mag();
distance_to_circle < self.minor_rad.into_inner() distance_to_circle < self.minor_rad
} }
} }
#[derive(Copy, Clone, Serialize, Deserialize)] #[derive(Copy, Clone, Serialize, Deserialize)]
pub struct Sphere { pub struct Sphere {
center: Meters, center: Meters,
rad: Real, rad: f32,
} }
impl Sphere { impl Sphere {
pub fn new(center: Meters, rad: Flt) -> Self { pub fn new(center: Meters, rad: f32) -> Self {
Self { Self {
center, center,
rad: Real::from_inner(rad), rad,
} }
} }
} }
@@ -124,7 +122,7 @@ impl Sphere {
#[typetag::serde] #[typetag::serde]
impl Region for Sphere { impl Region for Sphere {
fn contains(&self, p: Meters) -> bool { fn contains(&self, p: Meters) -> bool {
(*p - *self.center).mag() < self.rad.into_inner() (*p - *self.center).mag() < self.rad
} }
} }
@@ -140,23 +138,23 @@ impl Cube {
pub fn new(lower: Meters, upper: Meters) -> Self { pub fn new(lower: Meters, upper: Meters) -> Self {
Self { lower, upper } Self { lower, upper }
} }
pub fn x_range(&self) -> Range<Flt> { pub fn x_range(&self) -> Range<f32> {
Flt::from_primitive(self.lower.x())..Flt::from_primitive(self.upper.x()) self.lower.x()..self.upper.x()
} }
pub fn y_range(&self) -> Range<Flt> { pub fn y_range(&self) -> Range<f32> {
Flt::from_primitive(self.lower.y())..Flt::from_primitive(self.upper.y()) self.lower.y()..self.upper.y()
} }
pub fn z_range(&self) -> Range<Flt> { pub fn z_range(&self) -> Range<f32> {
Flt::from_primitive(self.lower.z())..Flt::from_primitive(self.upper.z()) self.lower.z()..self.upper.z()
} }
} }
#[typetag::serde] #[typetag::serde]
impl Region for Cube { impl Region for Cube {
fn contains(&self, p: Meters) -> bool { fn contains(&self, p: Meters) -> bool {
self.x_range().contains(&Flt::from_primitive(p.x())) && self.x_range().contains(&p.x()) &&
self.y_range().contains(&Flt::from_primitive(p.y())) && self.y_range().contains(&p.y()) &&
self.z_range().contains(&Flt::from_primitive(p.z())) self.z_range().contains(&p.z())
} }
} }

View File

@@ -1,20 +1,19 @@
use crate::flt::{Flt, Real}; use crate::real::ToFloat;
use crate::real::{Real as _, ToFloat};
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use super::{Vec3, Vec3u}; use super::{Vec3, Vec3u};
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::ops::{Add, Deref, Div, Mul, Sub}; use std::ops::{Add, Deref, Div, Mul, Sub};
pub trait Coord: Copy + Clone { pub trait Coord: Copy + Clone {
fn to_meters(&self, feature_size: Flt) -> Meters; fn to_meters(&self, feature_size: f32) -> Meters;
fn to_index(&self, feature_size: Flt) -> Index; fn to_index(&self, feature_size: f32) -> Index;
fn from_meters(other: Meters, feature_size: Flt) -> Self; fn from_meters(other: Meters, feature_size: f32) -> Self;
fn from_index(other: Index, feature_size: Flt) -> Self; fn from_index(other: Index, feature_size: f32) -> Self;
fn from_either(i: Index, m: Meters) -> Self; fn from_either(i: Index, m: Meters) -> Self;
} }
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Meters(pub Vec3); pub struct Meters(pub Vec3<f32>);
impl Meters { impl Meters {
pub fn new<R: ToFloat>(x: R, y: R, z: R) -> Self { pub fn new<R: ToFloat>(x: R, y: R, z: R) -> Self {
@@ -23,16 +22,16 @@ impl Meters {
} }
impl Coord for Meters { impl Coord for Meters {
fn to_meters(&self, _feature_size: Flt) -> Meters { fn to_meters(&self, _feature_size: f32) -> Meters {
*self *self
} }
fn to_index(&self, feature_size: Flt) -> Index { fn to_index(&self, feature_size: f32) -> Index {
Index((self.0 / Real::from_f64(feature_size)).round().into()) Index((self.0 / feature_size).round().into())
} }
fn from_meters(other: Meters, _feature_size: Flt) -> Self { fn from_meters(other: Meters, _feature_size: f32) -> Self {
other other
} }
fn from_index(other: Index, feature_size: Flt) -> Self { fn from_index(other: Index, feature_size: f32) -> Self {
other.to_meters(feature_size) other.to_meters(feature_size)
} }
fn from_either(_i: Index, m: Meters) -> Self { fn from_either(_i: Index, m: Meters) -> Self {
@@ -41,8 +40,8 @@ impl Coord for Meters {
} }
impl Deref for Meters { impl Deref for Meters {
type Target = Vec3; type Target = Vec3<f32>;
fn deref(&self) -> &Vec3 { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
} }
@@ -80,16 +79,16 @@ impl Index {
} }
impl Coord for Index { impl Coord for Index {
fn to_meters(&self, feature_size: Flt) -> Meters { fn to_meters(&self, feature_size: f32) -> Meters {
Meters(Vec3::from(self.0) * Real::from_f64(feature_size)) Meters(Vec3::from(self.0) * feature_size)
} }
fn to_index(&self, _feature_size: Flt) -> Index { fn to_index(&self, _feature_size: f32) -> Index {
*self *self
} }
fn from_meters(other: Meters, feature_size: Flt) -> Self { fn from_meters(other: Meters, feature_size: f32) -> Self {
other.to_index(feature_size) other.to_index(feature_size)
} }
fn from_index(other: Index, _feature_size: Flt) -> Self { fn from_index(other: Index, _feature_size: f32) -> Self {
other other
} }
fn from_either(i: Index, _m: Meters) -> Self { fn from_either(i: Index, _m: Meters) -> Self {

View File

@@ -1,5 +1,5 @@
use crate::geom::Vec3; use crate::geom::Vec3;
use crate::real::ToFloat as _; use crate::real::Real;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use std::fmt::{self, Display}; use std::fmt::{self, Display};
@@ -40,8 +40,8 @@ impl From<(u32, u32, u32)> for Vec3u {
} }
} }
impl From<Vec3> for Vec3u { impl<R: Real> From<Vec3<R>> for Vec3u {
fn from(v: Vec3) -> Self { fn from(v: Vec3<R>) -> Self {
Self::new(v.x().to_f64() as _, v.y().to_f64() as _, v.z().to_f64() as _) Self::new(v.x().to_f64() as _, v.y().to_f64() as _, v.z().to_f64() as _)
} }
} }

View File

@@ -1,6 +1,5 @@
use crate::flt::{Flt, Real};
use crate::geom::{Meters, Region, Torus, Vec3, WorldRegion}; use crate::geom::{Meters, Region, Torus, Vec3, WorldRegion};
use crate::real::Real as _; use crate::real::{Real as _, ToFloat as _};
use crate::sim::GenericSim; use crate::sim::GenericSim;
use common_macros::b_tree_map; use common_macros::b_tree_map;
use dyn_clone::{self, DynClone}; use dyn_clone::{self, DynClone};
@@ -84,19 +83,19 @@ impl Current {
region: Box::new(r) region: Box::new(r)
} }
} }
fn data(&self, state: &dyn GenericSim) -> (Real, Vec3) { fn data(&self, state: &dyn GenericSim) -> (f32, Vec3<f32>) {
let FieldSample(volume, current_mag, current_vec) = state.map_sum_over_enumerated(&*self.region, |coord: Meters, _cell| { let FieldSample(volume, current_mag, current_vec) = state.map_sum_over_enumerated(&*self.region, |coord: Meters, _cell| {
let current = state.current(coord); let current = state.current(coord);
FieldSample(1, current.mag(), current) FieldSample(1, current.mag().cast(), current.cast())
}); });
let mean_current_mag = current_mag / (Real::from_u32(volume)); let mean_current_mag = current_mag.to_f32() / (f32::from_primitive(volume));
let mean_current_vec = current_vec / (Real::from_u32(volume)); let mean_current_vec = current_vec.cast::<f32>() / (f32::from_primitive(volume));
(mean_current_mag, mean_current_vec) (mean_current_mag, mean_current_vec.cast())
} }
} }
#[derive(Default)] #[derive(Default)]
struct FieldSample(u32, Real, Vec3); struct FieldSample(u32, f64, Vec3<f64>);
impl std::iter::Sum for FieldSample { impl std::iter::Sum for FieldSample {
fn sum<I>(iter: I) -> Self fn sum<I>(iter: I) -> Self
@@ -172,16 +171,16 @@ impl CurrentLoop {
region: r, region: r,
} }
} }
fn data(&self, state: &dyn GenericSim) -> Real { fn data(&self, state: &dyn GenericSim) -> f32 {
let FieldSample(volume, directed_current, _current_vec) = state.map_sum_over_enumerated(&self.region, |coord: Meters, _cell| { let FieldSample(volume, directed_current, _current_vec) = state.map_sum_over_enumerated(&self.region, |coord: Meters, _cell| {
let normal = self.region.axis(); let normal = self.region.axis();
let to_coord = *coord - *self.region.center(); let to_coord = *coord - *self.region.center();
let tangent = normal.cross(to_coord).norm(); let tangent = normal.cross(to_coord).norm();
let current = state.current(coord); let current = state.current(coord);
let directed_current = current.dot(tangent); let directed_current = current.dot(tangent.cast());
FieldSample(1, directed_current, current) FieldSample(1, directed_current.cast(), current.cast())
}); });
let mean_directed_current = directed_current / Real::from_u32(volume); let mean_directed_current = directed_current.cast::<f32>() / f32::from_primitive(volume);
let cross_section = self.region.cross_section() / (state.feature_size() * state.feature_size()); let cross_section = self.region.cross_section() / (state.feature_size() * state.feature_size());
let cross_sectional_current = mean_directed_current * cross_section; let cross_sectional_current = mean_directed_current * cross_section;
cross_sectional_current cross_sectional_current
@@ -217,7 +216,7 @@ impl MagneticLoop {
region: r, region: r,
} }
} }
fn data(&self, state: &dyn GenericSim) -> (Real, Real, Real) { fn data(&self, state: &dyn GenericSim) -> (f32, f32, f32) {
let FieldSamples([ let FieldSamples([
FieldSample(volume, directed_m, _m_vec), FieldSample(volume, directed_m, _m_vec),
FieldSample(_, directed_b, _b_vec), FieldSample(_, directed_b, _b_vec),
@@ -229,23 +228,23 @@ impl MagneticLoop {
let tangent = normal.cross(to_coord).norm(); let tangent = normal.cross(to_coord).norm();
let m = cell.m(); let m = cell.m();
let directed_m = m.dot(tangent); let directed_m = m.dot(tangent.cast());
let b = cell.b(); let b = cell.b();
let directed_b = b.dot(tangent); let directed_b = b.dot(tangent.cast());
let h = cell.h(); let h = cell.h();
let directed_h = h.dot(tangent); let directed_h = h.dot(tangent.cast());
FieldSamples([ FieldSamples([
FieldSample(1, directed_m, m), FieldSample(1, directed_m.cast(), m.cast()),
FieldSample(1, directed_b, b), FieldSample(1, directed_b.cast(), b.cast()),
FieldSample(1, directed_h, h), FieldSample(1, directed_h.cast(), h.cast()),
]) ])
}); });
// let cross_section = self.region.cross_section() / (state.feature_size() * state.feature_size()); // let cross_section = self.region.cross_section() / (state.feature_size() * state.feature_size());
let mean_directed_m = directed_m / Real::from_u32(volume); let mean_directed_m = directed_m.cast::<f32>() / f32::from_primitive(volume);
// let cross_sectional_m = mean_directed_m * cross_section; // let cross_sectional_m = mean_directed_m * cross_section;
let mean_directed_b = directed_b / Real::from_u32(volume); let mean_directed_b = directed_b.cast::<f32>() / f32::from_primitive(volume);
// let cross_sectional_b = mean_directed_b * cross_section; // let cross_sectional_b = mean_directed_b * cross_section;
let mean_directed_h = directed_h / Real::from_u32(volume); let mean_directed_h = directed_h.cast::<f32>() / f32::from_primitive(volume);
// let cross_sectional_h = mean_directed_h * cross_section; // let cross_sectional_h = mean_directed_h * cross_section;
// format!( // format!(
// "M({}): {:.2e}; B({}): {:.2e}; H({}): {:.2e}", // "M({}): {:.2e}; B({}): {:.2e}; H({}): {:.2e}",
@@ -292,13 +291,13 @@ impl MagneticFlux {
region: Box::new(r) region: Box::new(r)
} }
} }
fn data(&self, state: &dyn GenericSim) -> Vec3 { fn data(&self, state: &dyn GenericSim) -> Vec3<f32> {
let FieldSample(volume, _directed_mag, mag_vec) = state.map_sum_over(&*self.region, |cell| { let FieldSample(volume, _directed_mag, mag_vec) = state.map_sum_over(&*self.region, |cell| {
let b = cell.b(); let b = cell.b();
let mag = b.mag(); let mag = b.mag();
FieldSample(1, mag, b) FieldSample(1, mag.cast(), b.cast())
}); });
let mean_mag = mag_vec / Real::from_u32(volume); let mean_mag = mag_vec.cast() / f32::from_primitive(volume);
mean_mag mean_mag
} }
} }
@@ -331,13 +330,13 @@ impl Magnetization {
region: Box::new(r) region: Box::new(r)
} }
} }
fn data(&self, state: &dyn GenericSim) -> Vec3 { fn data(&self, state: &dyn GenericSim) -> Vec3<f32> {
let FieldSample(volume, _directed_mag, mag_vec) = state.map_sum_over(&*self.region, |cell| { let FieldSample(volume, _directed_mag, mag_vec) = state.map_sum_over(&*self.region, |cell| {
let m = cell.m(); let m = cell.m();
let mag = m.mag(); let mag = m.mag();
FieldSample(1, mag, m) FieldSample(1, mag.cast(), m.cast())
}); });
let mean_mag = mag_vec / Real::from_u32(volume); let mean_mag = mag_vec.cast() / f32::from_primitive(volume);
mean_mag mean_mag
} }
} }
@@ -357,7 +356,7 @@ impl AbstractMeasurement for Magnetization {
} }
fn loc(v: Meters) -> String { fn loc(v: Meters) -> String {
format!("{:.0} um", *v * Real::from_u64(1_000_000)) format!("{:.0} um", *v * f32::from_primitive(1_000_000))
} }
/// M /// M
@@ -447,7 +446,7 @@ impl Energy {
region: Box::new(region), region: Box::new(region),
} }
} }
fn data(&self, state: &dyn GenericSim) -> Real { fn data(&self, state: &dyn GenericSim) -> f32 {
// Potential energy stored in a E/M field: // Potential energy stored in a E/M field:
// https://en.wikipedia.org/wiki/Magnetic_energy // https://en.wikipedia.org/wiki/Magnetic_energy
// https://en.wikipedia.org/wiki/Electric_potential_energy#Energy_stored_in_an_electrostatic_field_distribution // https://en.wikipedia.org/wiki/Electric_potential_energy#Energy_stored_in_an_electrostatic_field_distribution
@@ -456,11 +455,11 @@ impl Energy {
// U(E) = 1/2 \int E . D dV // U(E) = 1/2 \int E . D dV
#[allow(non_snake_case)] #[allow(non_snake_case)]
let dV = state.feature_volume(); let dV = state.feature_volume();
let e = Real::from_primitive(0.5 * dV) * state.map_sum_over(&*self.region, |cell| { let e = f64::from_primitive(0.5 * dV) * state.map_sum_over(&*self.region, |cell| {
// E . D = E . (E + P) = E.E since we don't model polarization fields // E . D = E . (E + P) = E.E since we don't model polarization fields
cell.h().dot(cell.b()) + cell.e().mag_sq() cell.h().dot(cell.b()).to_f64() + cell.e().mag_sq().to_f64()
}); });
e e.cast()
} }
} }
@@ -494,15 +493,15 @@ impl Power {
region: Box::new(region), region: Box::new(region),
} }
} }
fn data(&self, state: &dyn GenericSim) -> Flt { fn data(&self, state: &dyn GenericSim) -> f32 {
// Power is P = IV = A*J*V = L^2*J.(LE) = L^3 J.E // Power is P = IV = A*J*V = L^2*J.(LE) = L^3 J.E
// where L is feature size. // where L is feature size.
#[allow(non_snake_case)] #[allow(non_snake_case)]
let dV = Real::from_primitive(state.feature_volume()); let dV = state.feature_volume();
let power = dV * state.map_sum_over(&*self.region, |cell| { let power = f64::from_primitive(dV) * state.map_sum_over(&*self.region, |cell| {
cell.current_density().dot(cell.e()) cell.current_density().dot(cell.e()).to_f64()
}); });
Flt::from_primitive(power) power.cast()
} }
} }

View File

@@ -16,6 +16,11 @@ pub trait ToFloat {
/// This exists to allow configuration over # of bits (f32 v.s. f64) as well as /// This exists to allow configuration over # of bits (f32 v.s. f64) as well as
/// constraints. /// constraints.
pub trait Real: ToFloat + decorum_Real + IntrinsicOrd + AddAssign + MulAssign + fmt::LowerExp + fmt::Display { pub trait Real: ToFloat + decorum_Real + IntrinsicOrd + AddAssign + MulAssign + fmt::LowerExp + fmt::Display {
// TODO: fold with from_<blah>
fn from_primitive<P: ToFloat>(p: P) -> Self {
Self::from_f64(p.to_f64())
}
fn from_f32(f: f32) -> Self { fn from_f32(f: f32) -> Self {
Self::from_f64(f as _) Self::from_f64(f as _)
} }
@@ -23,15 +28,14 @@ pub trait Real: ToFloat + decorum_Real + IntrinsicOrd + AddAssign + MulAssign +
Self::from_f32(f as _) Self::from_f32(f as _)
} }
fn from_u64(u: u64) -> Self { fn from_u64(u: u64) -> Self {
Self::from_f64(u as _) Self::from_primitive(u)
} }
fn from_u32(u: u32) -> Self { fn from_u32(u: u32) -> Self {
Self::from_u64(u as _) Self::from_primitive(u)
} }
fn from_primitive<P: ToFloat>(p: P) -> Self { fn cast<R: Real>(&self) -> R {
// TODO: fold with from_<blah> R::from_primitive(*self)
Self::from_f64(p.to_f64())
} }
fn tenth() -> Self { fn tenth() -> Self {
@@ -52,6 +56,23 @@ pub trait Real: ToFloat + decorum_Real + IntrinsicOrd + AddAssign + MulAssign +
fn ten() -> Self { fn ten() -> Self {
Self::from_f32(10.0) Self::from_f32(10.0)
} }
fn pi() -> Self {
Self::from_f64(std::f64::consts::PI)
}
fn two_pi() -> Self {
Self::from_f64(std::f64::consts::PI * 2.0)
}
fn c() -> Self {
Self::from_primitive(299792458)
}
fn eps0() -> Self {
Self::from_primitive(8.854187812813e-12) // F⋅m1
}
fn mu0() -> Self {
Self::from_primitive(1.2566370621219e-6) // H/m
}
} }
impl ToFloat for f32 { impl ToFloat for f32 {
@@ -61,12 +82,12 @@ impl ToFloat for f32 {
} }
impl Real for f32 { impl Real for f32 {
fn from_primitive<P: ToFloat>(p: P) -> Self {
Self::from_f32(p.to_f32())
}
fn from_f32(f: f32) -> Self { fn from_f32(f: f32) -> Self {
f f
} }
fn from_u64(u: u64) -> Self {
Self::from_f32(u as _)
}
} }
impl ToFloat for f64 { impl ToFloat for f64 {
@@ -88,12 +109,12 @@ impl ToFloat for decorum::R32 {
} }
impl Real for decorum::R32 { impl Real for decorum::R32 {
fn from_primitive<P: ToFloat>(p: P) -> Self {
Self::from_f32(p.to_f32())
}
fn from_f32(f: f32) -> Self { fn from_f32(f: f32) -> Self {
Self::from_inner(f) Self::from_inner(f)
} }
fn from_u64(u: u64) -> Self {
Self::from_f32(u as _)
}
} }
impl ToFloat for decorum::R64 { impl ToFloat for decorum::R64 {
@@ -115,12 +136,12 @@ impl ToFloat for decorum::N32 {
} }
impl Real for decorum::N32 { impl Real for decorum::N32 {
fn from_primitive<P: ToFloat>(p: P) -> Self {
Self::from_f32(p.to_f32())
}
fn from_f32(f: f32) -> Self { fn from_f32(f: f32) -> Self {
Self::from_inner(f) Self::from_inner(f)
} }
fn from_u64(u: u64) -> Self {
Self::from_f32(u as _)
}
} }
impl ToFloat for decorum::N64 { impl ToFloat for decorum::N64 {
@@ -135,6 +156,15 @@ impl Real for decorum::N64 {
} }
} }
impl ToFloat for i32 {
fn to_f32(&self) -> f32 {
*self as _
}
fn to_f64(&self) -> f64 {
*self as _
}
}
impl ToFloat for u32 { impl ToFloat for u32 {
fn to_f32(&self) -> f32 { fn to_f32(&self) -> f32 {
*self as _ *self as _

View File

@@ -1,7 +1,7 @@
use crate::{flt::{Flt, Real}, consts}; use crate::{flt::{Flt, Real}, consts};
use crate::geom::{Coord, Cube, Index, InvertedRegion, Meters, Region, Vec3, Vec3u}; use crate::geom::{Coord, Cube, Index, InvertedRegion, Meters, Region, Vec3, Vec3u};
use crate::mat::{self, GenericMaterial, Material, MaterialExt as _}; use crate::mat::{self, GenericMaterial, Material, MaterialExt as _};
use crate::real::{self, Real as _}; use crate::real::{self, Real as _, ToFloat as _};
use crate::stim::AbstractStimulus; use crate::stim::AbstractStimulus;
use dyn_clone::{self, DynClone}; use dyn_clone::{self, DynClone};
use log::trace; use log::trace;
@@ -201,16 +201,16 @@ pub trait GenericSim: Send + Sync + DynClone {
fn impulse_h_meters(&mut self, pos: Meters, amount: Vec3); fn impulse_h_meters(&mut self, pos: Meters, amount: Vec3);
fn impulse_b_meters(&mut self, pos: Meters, amount: Vec3); fn impulse_b_meters(&mut self, pos: Meters, amount: Vec3);
fn size(&self) -> Index; fn size(&self) -> Index;
fn feature_size(&self) -> Flt; fn feature_size(&self) -> f32;
fn feature_volume(&self) -> Flt { fn feature_volume(&self) -> f32 {
let f = self.feature_size(); let f = self.feature_size();
f*f*f f*f*f
} }
fn volume(&self) -> Flt { fn volume(&self) -> f32 {
let s = self.size().to_meters(self.feature_size()); let s = self.size().to_meters(self.feature_size());
Flt::from_primitive(s.x() * s.y() * s.z()) s.x() * s.y() * s.z()
} }
fn timestep(&self) -> Flt; fn timestep(&self) -> f32;
fn step(&mut self); fn step(&mut self);
fn step_no(&self) -> u64; fn step_no(&self) -> u64;
@@ -223,8 +223,8 @@ pub trait GenericSim: Send + Sync + DynClone {
fn depth(&self) -> u32 { fn depth(&self) -> u32 {
self.size().z() self.size().z()
} }
fn time(&self) -> Flt { fn time(&self) -> f32 {
self.timestep() * self.step_no() as Flt self.timestep() * self.step_no() as f32
} }
fn apply_stimulus(&mut self, stim: &dyn AbstractStimulus); fn apply_stimulus(&mut self, stim: &dyn AbstractStimulus);
@@ -389,16 +389,16 @@ impl<M> SimState<M> {
pub fn depth(&self) -> u32 { pub fn depth(&self) -> u32 {
self.size().z() self.size().z()
} }
pub fn feature_size(&self) -> Flt { pub fn feature_size(&self) -> f32 {
self.feature_size.into() self.feature_size.to_f32()
} }
pub fn timestep(&self) -> Flt { pub fn timestep(&self) -> f32 {
self.timestep.into() self.timestep.to_f32()
} }
} }
impl<M: Default> SimState<M> { impl<M: Default> SimState<M> {
pub fn new(size: Index, feature_size: Flt) -> Self { pub fn new(size: Index, feature_size: f32) -> Self {
// XXX this has to be multiplied by a Courant Number in order to ensure stability. // XXX this has to be multiplied by a Courant Number in order to ensure stability.
// For 3d, we need 3 full timesteps in order to communicate information across the corner // For 3d, we need 3 full timesteps in order to communicate information across the corner
// of a grid. The distance traveled during that time is \sqrt{3}. Hence each timestep needs // of a grid. The distance traveled during that time is \sqrt{3}. Hence each timestep needs
@@ -406,8 +406,8 @@ impl<M: Default> SimState<M> {
// In 2d, this would be \sqrt{2}/2. // In 2d, this would be \sqrt{2}/2.
// It's an upper limit though; it should be safe to go lower. // It's an upper limit though; it should be safe to go lower.
let courant = 0.577; let courant = 0.577;
let feature_size = Real::from(feature_size); let feature_size = Real::from_primitive(feature_size);
let timestep = feature_size / consts::real::C() * courant; let timestep = feature_size / Real::c() * Real::from_primitive(courant);
Self { Self {
cells: Array3::default((size.z() as _, size.y() as _, size.x() as _)), cells: Array3::default((size.z() as _, size.y() as _, size.x() as _)),
e: Array3::default((size.z() as _, size.y() as _, size.x() as _)), e: Array3::default((size.z() as _, size.y() as _, size.x() as _)),
@@ -456,12 +456,12 @@ impl<M: Material + Clone + Send + Sync + 'static> SimState<M> {
let feature_size = self.feature_size(); let feature_size = self.feature_size();
let t_sec = self.time(); let t_sec = self.time();
let timestep = self.timestep(); let timestep = self.timestep;
trace!("apply_stimulus begin"); trace!("apply_stimulus begin");
Zip::from(ndarray::indices_of(&self.e)).and(&mut self.e).par_apply( Zip::from(ndarray::indices_of(&self.e)).and(&mut self.e).par_apply(
|(z, y, x), e| { |(z, y, x), e| {
let pos_meters = Index((x as u32, y as u32, z as u32).into()).to_meters(feature_size); let pos_meters = Index((x as u32, y as u32, z as u32).into()).to_meters(feature_size);
let value = stim.at(t_sec, pos_meters) * Real::from_primitive(timestep); let value = stim.at(t_sec, pos_meters) * timestep;
*e += value; *e += value;
}); });
trace!("apply_stimulus end"); trace!("apply_stimulus end");
@@ -507,7 +507,7 @@ impl<M: Material> SimState<M> {
pub fn fill_boundary_using<C, F>(&mut self, thickness: C, f: F) pub fn fill_boundary_using<C, F>(&mut self, thickness: C, f: F)
where where
C: Coord, C: Coord,
F: Fn(Vec3) -> M, F: Fn(Vec3<f32>) -> M,
{ {
// TODO: maybe this function belongs on the Driver? // TODO: maybe this function belongs on the Driver?
let feat = self.feature_size(); let feat = self.feature_size();
@@ -517,23 +517,23 @@ impl<M: Material> SimState<M> {
let region = InvertedRegion::new(Cube::new(upper_left.to_meters(feat), lower_right.to_meters(feat))); let region = InvertedRegion::new(Cube::new(upper_left.to_meters(feat), lower_right.to_meters(feat)));
self.fill_region_using(&region, |loc: Index| { self.fill_region_using(&region, |loc: Index| {
let depth_x = if loc.x() < upper_left.x() { let depth_x = if loc.x() < upper_left.x() {
(upper_left.x() - loc.x()) as Flt / upper_left.x() as Flt (upper_left.x() - loc.x()) as f32 / upper_left.x() as f32
} else if loc.x() > lower_right.x() { } else if loc.x() > lower_right.x() {
(loc.x() - lower_right.x()) as Flt / upper_left.x() as Flt (loc.x() - lower_right.x()) as f32 / upper_left.x() as f32
} else { } else {
0.0 0.0
}; };
let depth_y = if loc.y() < upper_left.y() { let depth_y = if loc.y() < upper_left.y() {
(upper_left.y() - loc.y()) as Flt / upper_left.y() as Flt (upper_left.y() - loc.y()) as f32 / upper_left.y() as f32
} else if loc.y() > lower_right.y() { } else if loc.y() > lower_right.y() {
(loc.y() - lower_right.y()) as Flt / upper_left.y() as Flt (loc.y() - lower_right.y()) as f32 / upper_left.y() as f32
} else { } else {
0.0 0.0
}; };
let depth_z = if loc.z() < upper_left.z() { let depth_z = if loc.z() < upper_left.z() {
(upper_left.z() - loc.z()) as Flt / upper_left.z() as Flt (upper_left.z() - loc.z()) as f32 / upper_left.z() as f32
} else if loc.z() > lower_right.z() { } else if loc.z() > lower_right.z() {
(loc.z() - lower_right.z()) as Flt / upper_left.z() as Flt (loc.z() - lower_right.z()) as f32 / upper_left.z() as f32
} else { } else {
0.0 0.0
}; };
@@ -584,11 +584,11 @@ impl<M: Material + Clone + Send + Sync + 'static> GenericSim for SimState<M> {
self.cells.shape()[0] as _, self.cells.shape()[0] as _,
)) ))
} }
fn feature_size(&self) -> Flt { fn feature_size(&self) -> f32 {
self.feature_size.into() self.feature_size.to_f32()
} }
fn timestep(&self) -> Flt { fn timestep(&self) -> f32 {
self.timestep() self.timestep.to_f32()
} }
fn step(&mut self) { fn step(&mut self) {
SimState::step(self) SimState::step(self)
@@ -601,34 +601,34 @@ impl<M: Material + Clone + Send + Sync + 'static> GenericSim for SimState<M> {
#[allow(unused)] #[allow(unused)]
impl<M> SimState<M> { impl<M> SimState<M> {
fn get_mat<C: Coord>(&self, c: C) -> &M { fn get_mat<C: Coord>(&self, c: C) -> &M {
let at = c.to_index(self.feature_size.into()); let at = c.to_index(self.feature_size.cast());
&self.cells[at.row_major_idx()] &self.cells[at.row_major_idx()]
} }
fn get_mat_mut<C: Coord>(&mut self, c: C) -> &mut M { fn get_mat_mut<C: Coord>(&mut self, c: C) -> &mut M {
let at = c.to_index(self.feature_size.into()); let at = c.to_index(self.feature_size.cast());
&mut self.cells[at.row_major_idx()] &mut self.cells[at.row_major_idx()]
} }
fn get_e<C: Coord>(&self, c: C) -> &Vec3 { fn get_e<C: Coord>(&self, c: C) -> &Vec3 {
let at = c.to_index(self.feature_size.into()); let at = c.to_index(self.feature_size.cast());
&self.e[at.row_major_idx()] &self.e[at.row_major_idx()]
} }
fn get_e_mut<C: Coord>(&mut self, c: C) -> &mut Vec3 { fn get_e_mut<C: Coord>(&mut self, c: C) -> &mut Vec3 {
let at = c.to_index(self.feature_size.into()); let at = c.to_index(self.feature_size.cast());
&mut self.e[at.row_major_idx()] &mut self.e[at.row_major_idx()]
} }
fn get_h<C: Coord>(&self, c: C) -> &Vec3 { fn get_h<C: Coord>(&self, c: C) -> &Vec3 {
let at = c.to_index(self.feature_size.into()); let at = c.to_index(self.feature_size.cast());
&self.h[at.row_major_idx()] &self.h[at.row_major_idx()]
} }
fn get_h_mut<C: Coord>(&mut self, c: C) -> &mut Vec3 { fn get_h_mut<C: Coord>(&mut self, c: C) -> &mut Vec3 {
let at = c.to_index(self.feature_size.into()); let at = c.to_index(self.feature_size.cast());
&mut self.h[at.row_major_idx()] &mut self.h[at.row_major_idx()]
} }
fn get_hcell<'a, C: Coord>(&'a mut self, c: C) -> HCell<'a, M> { fn get_hcell<'a, C: Coord>(&'a mut self, c: C) -> HCell<'a, M> {
let at = c.to_index(self.feature_size.into()); let at = c.to_index(self.feature_size.cast());
let idx = at.row_major_idx(); let idx = at.row_major_idx();
HCell { HCell {
e: &self.e[idx], e: &self.e[idx],
@@ -1487,14 +1487,14 @@ mod test {
assert_vec3_eq(actual_df_dt, df_dt(tm)); assert_vec3_eq(actual_df_dt, df_dt(tm));
} }
fn energy(s: &dyn GenericSim) -> Flt { fn energy(s: &dyn GenericSim) -> f32 {
0.5 * s.feature_volume() * s.map_sum_enumerated(|_pos: Index, cell| { 0.5 * s.feature_volume() * s.map_sum_enumerated(|_pos: Index, cell| {
// println!("{}: {}", _pos, cell.e()); // println!("{}: {}", _pos, cell.e());
Flt::from_primitive(cell.e().mag_sq()) cell.e().mag_sq().to_f64()
}) }).to_f32()
} }
fn energy_now_and_then<S: GenericSim>(state: &mut S, frames: u32) -> (Flt, Flt) { fn energy_now_and_then<S: GenericSim>(state: &mut S, frames: u32) -> (f32, f32) {
let energy_0 = energy(state); let energy_0 = energy(state);
for _f in 0..frames { for _f in 0..frames {
// println!("e({}) = {}", _f, energy(state)); // println!("e({}) = {}", _f, energy(state));
@@ -1521,7 +1521,7 @@ mod test {
fn energy_conservation_over_time() { fn energy_conservation_over_time() {
let mut state = StaticSim::new(Index((2001, 1, 1).into()), 1e-6); let mut state = StaticSim::new(Index((2001, 1, 1).into()), 1e-6);
for t in 0..100 { for t in 0..100 {
let angle = (t as Flt)/100.0*2.0*consts::PI; let angle = (t as Flt)/100.0*Flt::two_pi();
let sig = angle.sin(); let sig = angle.sin();
let gate = 0.5*(1.0 - angle.cos()); let gate = 0.5*(1.0 - angle.cos());
//println!("{}", g.exp()); //println!("{}", g.exp());
@@ -1539,7 +1539,7 @@ mod test {
fn sane_boundary_conditions() { fn sane_boundary_conditions() {
let mut state = StaticSim::new(Index((21, 21, 21).into()), 1e-6); let mut state = StaticSim::new(Index((21, 21, 21).into()), 1e-6);
for t in 0..10 { for t in 0..10 {
let angle = (t as Flt)/10.0*2.0*consts::PI; let angle = (t as Flt)/10.0*Flt::two_pi();
let sig = angle.sin(); let sig = angle.sin();
let gate = 0.5*(1.0 - angle.cos()); let gate = 0.5*(1.0 - angle.cos());
let dyn_state = &mut state as &mut dyn GenericSim; let dyn_state = &mut state as &mut dyn GenericSim;
@@ -1554,11 +1554,11 @@ mod test {
/// Fill the world with the provided material and a stimulus. /// Fill the world with the provided material and a stimulus.
/// Measure energy at the start, and then again after advancing many steps. /// Measure energy at the start, and then again after advancing many steps.
/// Return these two measurements (energy(t=0), energy(t=~=1000)) /// Return these two measurements (energy(t=0), energy(t=~=1000))
fn conductor_test<M: Into<mat::Static>>(mat: M) -> (Flt, Flt) { fn conductor_test<M: Into<mat::Static>>(mat: M) -> (f32, f32) {
let mut state = StaticSim::new(Index((201, 1, 1).into()), 1e-6); let mut state = StaticSim::new(Index((201, 1, 1).into()), 1e-6);
state.fill_region(&WorldRegion, mat.into()); state.fill_region(&WorldRegion, mat.into());
for t in 0..100 { for t in 0..100 {
let angle = (t as Flt)/100.0*2.0*consts::PI; let angle = (t as Flt)/100.0*Flt::two_pi();
let sig = angle.sin(); let sig = angle.sin();
let gate = 0.5*(1.0 - angle.cos()); let gate = 0.5*(1.0 - angle.cos());
let dyn_state = &mut state as &mut dyn GenericSim; let dyn_state = &mut state as &mut dyn GenericSim;
@@ -1605,8 +1605,8 @@ mod test {
let mut state = SimState::new(size, 1e-6); let mut state = SimState::new(size, 1e-6);
let timestep = state.timestep(); let timestep = state.timestep();
state.fill_boundary_using(size/4, |boundary_ness| { state.fill_boundary_using(size/4, |boundary_ness| {
let b = boundary_ness.elem_pow(Real::three()); let b = boundary_ness.elem_pow(3.0);
let conductivity = b * (Real::half() / timestep); let conductivity = b * (0.5 / timestep);
// K scales up to 11; dc_shift scales from 0.05: // K scales up to 11; dc_shift scales from 0.05:
// https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.461.4606&rep=rep1&type=pdf // https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.461.4606&rep=rep1&type=pdf
// let propagation = Vec3::unit() + b * 10.0; // let propagation = Vec3::unit() + b * 10.0;
@@ -1614,7 +1614,7 @@ mod test {
// //
// let propagation = Vec3::unit(); // let propagation = Vec3::unit();
// let dc_shift = Vec3::unit() * 1e-6; // let dc_shift = Vec3::unit() * 1e-6;
Pml::new(conductivity) Pml::new(conductivity.cast())
}); });
state state
} }
@@ -1624,8 +1624,8 @@ mod test {
let mut state = StaticSim::new(size, 1e-6); let mut state = StaticSim::new(size, 1e-6);
let timestep = state.timestep(); let timestep = state.timestep();
state.fill_boundary_using(size/4, |boundary_ness| { state.fill_boundary_using(size/4, |boundary_ness| {
let b = boundary_ness.elem_pow(Real::three()); let b = boundary_ness.elem_pow(3.0);
let conductivity = Vec3::unit() * (consts::real::EPS0() * b.mag() * Real::half() / timestep); let conductivity = Vec3::uniform(f32::eps0() * b.mag() * 0.5 / timestep);
Static { Static {
conductivity, ..Default::default() conductivity, ..Default::default()
} }
@@ -1635,14 +1635,15 @@ mod test {
/// Stimulus which applies a (possibly different) e*sin(wt)*gate(t) at every point /// Stimulus which applies a (possibly different) e*sin(wt)*gate(t) at every point
struct PmlStim<F> { struct PmlStim<F> {
/// Maps location -> (field direction, frequency)
f: F, f: F,
t_end: Flt, t_end: f32,
feat_size: Flt, feat_size: f32,
sqrt_vol: Flt, sqrt_vol: f32,
} }
impl<F: Fn(Index) -> (Vec3, Flt) + Sync> AbstractStimulus for PmlStim<F> { impl<F: Fn(Index) -> (Vec3, f32) + Sync> AbstractStimulus for PmlStim<F> {
fn at(&self, t_sec: Flt, pos: Meters) -> Vec3 { fn at(&self, t_sec: f32, pos: Meters) -> Vec3 {
let angle = (t_sec as Flt)/self.t_end*2.0*consts::PI; let angle = t_sec/self.t_end * f32::two_pi();
let gate = 0.5*(1.0 - angle.cos()); let gate = 0.5*(1.0 - angle.cos());
let (e, hz) = (self.f)(pos.to_index(self.feat_size)); let (e, hz) = (self.f)(pos.to_index(self.feat_size));
let sig_angle = angle*hz; let sig_angle = angle*hz;
@@ -1652,8 +1653,8 @@ mod test {
} }
/// Returns the energy ratio (Eend / Estart) /// Returns the energy ratio (Eend / Estart)
fn pml_test_full_interior<F, S: GenericSim>(state: &mut S, f: F) -> Flt fn pml_test_full_interior<F, S: GenericSim>(state: &mut S, f: F) -> f32
where F: Fn(Index) -> (Vec3, Flt) + Sync // returns (E vector, cycles (like Hz)) where F: Fn(Index) -> (Vec3, f32) + Sync // returns (E vector, cycles (like Hz))
{ {
let stim = PmlStim { let stim = PmlStim {
f, f,
@@ -1670,13 +1671,13 @@ mod test {
// sanity check the starting energy // sanity check the starting energy
assert_gt!(energy_0, 1e-11); assert_gt!(energy_0, 1e-11);
assert_lt!(energy_0, 1.0); assert_lt!(energy_0, 1.0);
(Real::from(energy_1) / Real::from(energy_0)).into_inner() energy_1 / energy_0
} }
fn pml_test_over_region<R, F, S>(state: &mut S, reg: R, f: F) -> Flt fn pml_test_over_region<R, F, S>(state: &mut S, reg: R, f: F) -> f32
where where
R: Region, R: Region,
F: Fn(Index) -> (Vec3, Flt) + Sync, F: Fn(Index) -> (Vec3, f32) + Sync,
S: GenericSim S: GenericSim
{ {
let feat = state.feature_size(); let feat = state.feature_size();
@@ -1688,13 +1689,13 @@ mod test {
} }
}) })
} }
fn pml_test_uniform_over_region<R: Region, S: GenericSim>(state: &mut S, reg: R, e: Vec3, hz: Flt) -> Flt { fn pml_test_uniform_over_region<R: Region, S: GenericSim>(state: &mut S, reg: R, e: Vec3, hz: f32) -> f32 {
pml_test_over_region(state, reg, |_idx| { pml_test_over_region(state, reg, |_idx| {
(e, hz) (e, hz)
}) })
} }
fn pml_test_at<S: GenericSim>(state: &mut S, e: Vec3, center: Index) -> Flt { fn pml_test_at<S: GenericSim>(state: &mut S, e: Vec3, center: Index) -> f32 {
pml_test_full_interior(state, |idx| { pml_test_full_interior(state, |idx| {
if idx == center { if idx == center {
(e, 1.0) (e, 1.0)
@@ -1770,17 +1771,17 @@ mod test {
let mut state = SimState::new(size, 1e-6); let mut state = SimState::new(size, 1e-6);
let timestep = state.timestep(); let timestep = state.timestep();
state.fill_boundary_using(size/4, |boundary_ness| { state.fill_boundary_using(size/4, |boundary_ness| {
let b = boundary_ness.elem_pow(Real::three()); let b = boundary_ness.elem_pow(3.0);
let coord_stretch = b * (Real::half() / timestep); let coord_stretch = b * (0.5 / timestep);
// let coord_stretch = b * 0.5; // let coord_stretch = b * 0.5;
// Force the stretch to be only along one axis // Force the stretch to be only along one axis
let coord_stretch = match coord_stretch.to_tuple() { let coord_stretch: Vec3<_> = match coord_stretch.to_tuple() {
(x, y, z) if x >= y && x >= z => (x, Real::zero(), Real::zero()), (x, y, z) if x >= y && x >= z => (x, 0.0, 0.0),
(x, y, z) if y >= x && y >= z => (Real::zero(), y, Real::zero()), (x, y, z) if y >= x && y >= z => (0.0, y, 0.0),
(x, y, z) if z >= x && z >= y => (Real::zero(), Real::zero(), z), (x, y, z) if z >= x && z >= y => (0.0, 0.0, z),
_ => unreachable!(), _ => unreachable!(),
}.into(); }.into();
Pml::new(coord_stretch) Pml::new(coord_stretch.cast())
}); });
state state
} }
@@ -1792,11 +1793,11 @@ mod test {
let mut state = SimState::new(size, 1e-6); let mut state = SimState::new(size, 1e-6);
let timestep = state.timestep(); let timestep = state.timestep();
state.fill_boundary_using(size/4, |boundary_ness| { state.fill_boundary_using(size/4, |boundary_ness| {
let b = boundary_ness.elem_pow(Real::three()); let b = boundary_ness.elem_pow(3.0);
let cs = b * (Real::half() / timestep); let cs = b * (0.5 / timestep);
// let cs = b * 0.5; // let cs = b * 0.5;
// permute the stretching so that it shouldn't interfere with the wave // permute the stretching so that it shouldn't interfere with the wave
let coord_stretch = shuffle(cs); let coord_stretch = shuffle(cs.cast());
Pml::new(coord_stretch) Pml::new(coord_stretch)
}); });
let center = Index(state.size().0 / 2); let center = Index(state.size().0 / 2);

View File

@@ -1,27 +1,26 @@
use crate::consts;
use crate::flt::{self, Flt}; use crate::flt::{self, Flt};
use crate::real::*; use crate::real::*;
use crate::geom::{Meters, Region, Vec3}; use crate::geom::{Meters, Region, Vec3};
pub trait AbstractStimulus: Sync { pub trait AbstractStimulus: Sync {
/// Return the E field which should be added PER-SECOND to the provided position/time. /// Return the E field which should be added PER-SECOND to the provided position/time.
fn at(&self, t_sec: Flt, pos: Meters) -> Vec3; fn at(&self, t_sec: f32, pos: Meters) -> Vec3;
} }
// impl<T: AbstractStimulus> AbstractStimulus for &T { // impl<T: AbstractStimulus> AbstractStimulus for &T {
// fn at(&self, t_sec: Flt, pos: Meters) -> Vec3 { // fn at(&self, t_sec: f32, pos: Meters) -> Vec3 {
// (*self).at(t_sec, pos) // (*self).at(t_sec, pos)
// } // }
// } // }
impl<T: AbstractStimulus> AbstractStimulus for Vec<T> { impl<T: AbstractStimulus> AbstractStimulus for Vec<T> {
fn at(&self, t_sec: Flt, pos: Meters) -> Vec3 { fn at(&self, t_sec: f32, pos: Meters) -> Vec3 {
self.iter().map(|s| s.at(t_sec, pos)).sum() self.iter().map(|s| s.at(t_sec, pos)).sum()
} }
} }
impl AbstractStimulus for Box<dyn AbstractStimulus> { impl AbstractStimulus for Box<dyn AbstractStimulus> {
fn at(&self, t_sec: Flt, pos: Meters) -> Vec3 { fn at(&self, t_sec: f32, pos: Meters) -> Vec3 {
(**self).at(t_sec, pos) (**self).at(t_sec, pos)
} }
} }
@@ -42,7 +41,7 @@ impl<R, T> Stimulus<R, T> {
} }
impl<R: Region + Sync, T: TimeVarying3 + Sync> AbstractStimulus for Stimulus<R, T> { impl<R: Region + Sync, T: TimeVarying3 + Sync> AbstractStimulus for Stimulus<R, T> {
fn at(&self, t_sec: Flt, pos: Meters) -> Vec3 { fn at(&self, t_sec: f32, pos: Meters) -> Vec3 {
if self.region.contains(pos) { if self.region.contains(pos) {
self.stim.at(t_sec) self.stim.at(t_sec)
} else { } else {
@@ -68,12 +67,12 @@ impl<R, T> CurlStimulus<R, T> {
} }
impl<R: Region + Sync, T: TimeVarying1 + Sync> AbstractStimulus for CurlStimulus<R, T> { impl<R: Region + Sync, T: TimeVarying1 + Sync> AbstractStimulus for CurlStimulus<R, T> {
fn at(&self, t_sec: Flt, pos: Meters) -> Vec3 { fn at(&self, t_sec: f32, pos: Meters) -> Vec3 {
if self.region.contains(pos) { if self.region.contains(pos) {
let amount = self.stim.at(t_sec); let amount = self.stim.at(t_sec);
let from_center_to_point = *pos - *self.center; let from_center_to_point = *pos - *self.center;
let rotational = from_center_to_point.cross(*self.axis); let rotational: Vec3 = from_center_to_point.cross(*self.axis).cast();
let impulse = rotational.with_mag(flt::Real::from_primitive(amount)); let impulse = rotational.with_mag(amount.cast());
impulse impulse
} else { } else {
Vec3::zero() Vec3::zero()
@@ -84,22 +83,22 @@ impl<R: Region + Sync, T: TimeVarying1 + Sync> AbstractStimulus for CurlStimulus
pub trait TimeVarying1: Sized { pub trait TimeVarying1: Sized {
/// Retrieve the E impulse to apply PER-SECOND at the provided time (in seconds). /// Retrieve the E impulse to apply PER-SECOND at the provided time (in seconds).
fn at(&self, t_sec: Flt) -> Flt; fn at(&self, t_sec: f32) -> Flt;
fn shifted(self, new_start: Flt) -> Shifted<Self> { fn shifted(self, new_start: f32) -> Shifted<Self> {
Shifted::new(self, new_start) Shifted::new(self, new_start)
} }
fn gated(self, from: Flt, to: Flt) -> Gated<Self> { fn gated(self, from: f32, to: f32) -> Gated<Self> {
Gated::new(self, from, to) Gated::new(self, from, to)
} }
} }
pub trait TimeVarying3: Sized { pub trait TimeVarying3: Sized {
/// Retrieve the E impulse to apply PER-SECOND at the provided time (in seconds). /// Retrieve the E impulse to apply PER-SECOND at the provided time (in seconds).
fn at(&self, t_sec: Flt) -> Vec3; fn at(&self, t_sec: f32) -> Vec3;
fn shifted(self, new_start: Flt) -> Shifted<Self> { fn shifted(self, new_start: f32) -> Shifted<Self> {
Shifted::new(self, new_start) Shifted::new(self, new_start)
} }
fn gated(self, from: Flt, to: Flt) -> Gated<Self> { fn gated(self, from: f32, to: f32) -> Gated<Self> {
Gated::new(self, from, to) Gated::new(self, from, to)
} }
} }
@@ -107,26 +106,26 @@ pub trait TimeVarying3: Sized {
#[derive(Clone)] #[derive(Clone)]
pub struct Sinusoid<A> { pub struct Sinusoid<A> {
amp: A, amp: A,
omega: Flt, omega: f32,
} }
pub type Sinusoid1 = Sinusoid<Flt>; pub type Sinusoid1 = Sinusoid<Flt>;
pub type Sinusoid3 = Sinusoid<Vec3>; pub type Sinusoid3 = Sinusoid<Vec3>;
impl<A> Sinusoid<A> { impl<A> Sinusoid<A> {
pub fn new(amp: A, freq: Flt) -> Self { pub fn new(amp: A, freq: f32) -> Self {
Self { Self {
amp, amp,
omega: freq * consts::TWO_PI, omega: freq * f32::two_pi(),
} }
} }
pub fn from_wavelength(amp: A, lambda: Flt) -> Self { pub fn from_wavelength(amp: A, lambda: f32) -> Self {
Self::new(amp, 1.0/lambda) Self::new(amp, 1.0/lambda)
} }
pub fn freq(&self) -> Flt { pub fn freq(&self) -> f32 {
self.omega / consts::TWO_PI self.omega / f32::two_pi()
} }
pub fn wavelength(&self) -> Flt { pub fn wavelength(&self) -> f32 {
1.0 / self.freq() 1.0 / self.freq()
} }
pub fn one_cycle(self) -> Gated<Self> { pub fn one_cycle(self) -> Gated<Self> {
@@ -140,13 +139,13 @@ impl<A> Sinusoid<A> {
} }
impl TimeVarying1 for Sinusoid1 { impl TimeVarying1 for Sinusoid1 {
fn at(&self, t_sec: Flt) -> Flt { fn at(&self, t_sec: f32) -> Flt {
self.amp * (t_sec * self.omega).sin() self.amp * (t_sec * self.omega).cast::<Flt>().sin()
} }
} }
impl TimeVarying3 for Sinusoid3 { impl TimeVarying3 for Sinusoid3 {
fn at(&self, t_sec: Flt) -> Vec3 { fn at(&self, t_sec: f32) -> Vec3 {
self.amp * flt::Real::from_primitive(t_sec * self.omega).sin() self.amp * flt::Real::from_primitive(t_sec * self.omega).sin()
} }
} }
@@ -154,18 +153,18 @@ impl TimeVarying3 for Sinusoid3 {
#[derive(Clone)] #[derive(Clone)]
pub struct Gated<T> { pub struct Gated<T> {
inner: T, inner: T,
start: Flt, start: f32,
end: Flt, end: f32,
} }
impl<T> Gated<T> { impl<T> Gated<T> {
pub fn new(inner: T, start: Flt, end: Flt) -> Self { pub fn new(inner: T, start: f32, end: f32) -> Self {
Self { inner, start, end } Self { inner, start, end }
} }
} }
impl<T: TimeVarying1> TimeVarying1 for Gated<T> { impl<T: TimeVarying1> TimeVarying1 for Gated<T> {
fn at(&self, t_sec: Flt) -> Flt { fn at(&self, t_sec: f32) -> Flt {
if (self.start..self.end).contains(&t_sec) { if (self.start..self.end).contains(&t_sec) {
self.inner.at(t_sec) self.inner.at(t_sec)
} else { } else {
@@ -175,7 +174,7 @@ impl<T: TimeVarying1> TimeVarying1 for Gated<T> {
} }
impl<T: TimeVarying3> TimeVarying3 for Gated<T> { impl<T: TimeVarying3> TimeVarying3 for Gated<T> {
fn at(&self, t_sec: Flt) -> Vec3 { fn at(&self, t_sec: f32) -> Vec3 {
if (self.start..self.end).contains(&t_sec) { if (self.start..self.end).contains(&t_sec) {
self.inner.at(t_sec) self.inner.at(t_sec)
} else { } else {
@@ -187,23 +186,23 @@ impl<T: TimeVarying3> TimeVarying3 for Gated<T> {
#[derive(Clone)] #[derive(Clone)]
pub struct Shifted<T> { pub struct Shifted<T> {
inner: T, inner: T,
start_at: Flt, start_at: f32,
} }
impl<T> Shifted<T> { impl<T> Shifted<T> {
pub fn new(inner: T, start_at: Flt) -> Self { pub fn new(inner: T, start_at: f32) -> Self {
Self { inner, start_at } Self { inner, start_at }
} }
} }
impl<T: TimeVarying1> TimeVarying1 for Shifted<T> { impl<T: TimeVarying1> TimeVarying1 for Shifted<T> {
fn at(&self, t_sec: Flt) -> Flt { fn at(&self, t_sec: f32) -> Flt {
self.inner.at(t_sec - self.start_at) self.inner.at(t_sec - self.start_at)
} }
} }
impl<T: TimeVarying3> TimeVarying3 for Shifted<T> { impl<T: TimeVarying3> TimeVarying3 for Shifted<T> {
fn at(&self, t_sec: Flt) -> Vec3 { fn at(&self, t_sec: f32) -> Vec3 {
self.inner.at(t_sec - self.start_at) self.inner.at(t_sec - self.start_at)
} }
} }