From ffda00b796e44a700d402402eb2cae5d6ce97188 Mon Sep 17 00:00:00 2001 From: colin Date: Thu, 18 Aug 2022 20:47:02 -0700 Subject: [PATCH] stim: convert CurlStimulus to a CurlVectorField and use ModulatedVectorField this opens the door to caching the vector field stuff. --- crates/applications/buffer_proto5/src/main.rs | 20 +-- .../multi_core_inverter/src/main.rs | 69 +++++----- crates/applications/sr_latch/src/main.rs | 8 +- crates/coremem/src/stim.rs | 120 +++++++++--------- 4 files changed, 106 insertions(+), 111 deletions(-) diff --git a/crates/applications/buffer_proto5/src/main.rs b/crates/applications/buffer_proto5/src/main.rs index 792e60c..d8782b8 100644 --- a/crates/applications/buffer_proto5/src/main.rs +++ b/crates/applications/buffer_proto5/src/main.rs @@ -22,7 +22,7 @@ use coremem::real::{R32, Real as _}; use coremem::render::CsvRenderer; use coremem::sim::spirv::{SpirvSim, WgpuBackend}; use coremem::sim::units::{Seconds, Time as _}; -use coremem::stim::{CurlStimulus, Exp1, Gated, Sinusoid, StimExt as _}; +use coremem::stim::{CurlVectorField, Exp1, Gated, ModulatedVectorField, Sinusoid, StimExt as _}; use log::{error, info, warn}; use serde::{Deserialize, Serialize}; @@ -441,25 +441,25 @@ fn run_sim(id: u32, p: Params, g: Geometries) -> Results { let wave = Sinusoid::from_wavelength(amp, duration * 2.0) .half_cycle() .shifted(start); - driver.add_stimulus(CurlStimulus::new( - region.clone(), - wave.clone(), + driver.add_stimulus(ModulatedVectorField::new( + CurlVectorField::new(region.clone()), + wave, )); }; let add_drive_square_pulse = |driver: &mut Driver<_>, region: &Torus, start: f32, duration: f32, amp: f32| { let wave = Gated::new(amp, start, start+duration); - driver.add_stimulus(CurlStimulus::new( - region.clone(), - wave.clone(), + driver.add_stimulus(ModulatedVectorField::new( + CurlVectorField::new(region.clone()), + wave, )); }; let add_drive_exp_pulse = |driver: &mut Driver<_>, region: &Torus, start: f32, duration: f32, amp: f32| { let wave = Exp1::new_at(amp, start, 0.5*duration); - driver.add_stimulus(CurlStimulus::new( - region.clone(), - wave.clone(), + driver.add_stimulus(ModulatedVectorField::new( + CurlVectorField::new(region.clone()), + wave, )); }; diff --git a/crates/applications/multi_core_inverter/src/main.rs b/crates/applications/multi_core_inverter/src/main.rs index 5251616..01dc641 100644 --- a/crates/applications/multi_core_inverter/src/main.rs +++ b/crates/applications/multi_core_inverter/src/main.rs @@ -43,7 +43,7 @@ use coremem::meas; use coremem::real::{self, Real as _}; use coremem::sim::spirv::{self, SpirvSim}; use coremem::sim::units::Seconds; -use coremem::stim::{Stimulus, CurlStimulus, DynStimuli, Exp, Gated, Shifted}; +use coremem::stim::{CurlVectorField, Exp, Gated, ModulatedVectorField, Shifted, StimExt as _, StimuliVec}; use coremem::Driver; type R = real::R32; @@ -76,25 +76,23 @@ enum ClockState { ReleaseLow, Float, } +type ClockSegment = Shifted>>; impl ClockState { - fn stimulus(&self, params: &Params, cycle: u32, ctl: u32) - -> Option> + fn time_stimulus(&self, params: &Params, cycle: u32) + -> Option { use ClockState::*; match self { - HoldHigh => Some(Box::new(params.control_signal_hold(cycle, ctl, DriveDirection::High))), - ReleaseHigh => Some(Box::new(params.control_signal_release(cycle, ctl, DriveDirection::High))), - HoldLow => Some(Box::new(params.control_signal_hold(cycle, ctl, DriveDirection::Low))), - ReleaseLow => Some(Box::new(params.control_signal_release(cycle, ctl, DriveDirection::Low))), + HoldHigh => Some(params.control_signal_hold(cycle, DriveDirection::High)), + ReleaseHigh => Some(params.control_signal_release(cycle, DriveDirection::High)), + HoldLow => Some(params.control_signal_hold(cycle, DriveDirection::Low)), + ReleaseLow => Some(params.control_signal_release(cycle, DriveDirection::Low)), Float => None, } } } -type HoldStim = Gated>; -type ReleaseStim = Shifted>>>; - #[derive(Copy, Clone)] struct Params { input_magnitude: f32, @@ -146,30 +144,18 @@ impl Params { Torus::new_xz(Meters::new(self.couplingx(n), self.sy, self.sz), self.coupling_major, self.coupling_minor) } - fn control_signal_hold(&self, cycle: u32, core: u32, ty: DriveDirection) -> HoldStim { - let region = self.ctl(core); - let area = region.cross_section(); - let amp = ty.as_signed_f32() * self.input_magnitude / area; - let base_stim = CurlStimulus::new( - region.clone(), - amp, - ); + fn control_signal_hold(&self, cycle: u32, ty: DriveDirection) -> ClockSegment { // simple square wave: + let amp = ty.as_signed_f32() * self.input_magnitude; let start = self.clock_phase_duration * cycle as f32; - Gated::new(base_stim, start, start + self.clock_phase_duration) + Exp::new_at(amp, start, 100.0 /* very long decay */) } - fn control_signal_release(&self, cycle: u32, core: u32, ty: DriveDirection) -> ReleaseStim { - let region = self.ctl(core); - let area = region.cross_section(); - let amp = ty.as_signed_f32() * self.input_magnitude / area; - let base_stim = CurlStimulus::new( - region.clone(), - amp, - ); + fn control_signal_release(&self, cycle: u32, ty: DriveDirection) -> ClockSegment { // decaying exponential wave: + let amp = ty.as_signed_f32() * self.input_magnitude; let start = self.clock_phase_duration * cycle as f32; - Exp::new_at(base_stim, start, self.clock_decay) + Exp::new_at(amp, start, self.clock_decay) } } @@ -245,21 +231,24 @@ fn main() { ]; let num_cycles = drive_map.len() as u32; let num_cores = drive_map[0].len() as u32; - let stim: Vec<_> = drive_map.into_iter() + let mut core_drivers = vec![Vec::default(); num_cores as usize]; + for (cycle, cores) in drive_map.into_iter().enumerate() { + for (core, clock) in cores.into_iter().enumerate() { + core_drivers[core as usize].extend(clock.time_stimulus(¶ms, cycle as u32)); + } + } + let stim: Vec<_> = core_drivers.into_iter() .enumerate() - .flat_map(|(cycle, cores)| { - cores.into_iter() - .enumerate() - .flat_map(move |(core, clock_state)| { - clock_state.stimulus(¶ms, cycle as u32, core as u32) - }) + .map(|(core, time_varying)| { + let region = params.ctl(core as u32); + let area = region.cross_section(); + let amp = 1.0 / area; + let v_field = CurlVectorField::new(region.clone()); + ModulatedVectorField::new(v_field, time_varying.scaled(amp)) }) .collect(); - // temporary sanity checks - assert_eq!(num_cycles, 14); - assert_eq!(num_cores, 5); - assert_eq!(stim.len(), 14*5 - 12); - let stim = DynStimuli::from_vec(stim); + assert_eq!(stim.len(), 5); + let stim = StimuliVec::from_vec(stim); let wire_mat = IsomorphicConductor::new(1e6f32.cast::()); diff --git a/crates/applications/sr_latch/src/main.rs b/crates/applications/sr_latch/src/main.rs index be8f32c..b46fb76 100644 --- a/crates/applications/sr_latch/src/main.rs +++ b/crates/applications/sr_latch/src/main.rs @@ -7,7 +7,7 @@ use coremem::{Driver, mat, meas}; use coremem::geom::{Coord as _, Meters, Torus}; use coremem::sim::spirv::{SpirvSim, WgpuBackend}; use coremem::sim::units::Seconds; -use coremem::stim::{CurlStimulus, Sinusoid, StimExt as _}; +use coremem::stim::{CurlVectorField, ModulatedVectorField, Sinusoid, StimExt as _}; fn main() { @@ -81,9 +81,9 @@ fn main() { let wave = Sinusoid::from_wavelength(amp, duration * 2.0) .half_cycle() .shifted(start); - driver.add_stimulus(CurlStimulus::new( - region.clone(), - wave.clone(), + driver.add_stimulus(ModulatedVectorField::new( + CurlVectorField::new(region.clone()), + wave, )); }; diff --git a/crates/coremem/src/stim.rs b/crates/coremem/src/stim.rs index 796ad31..7a0b0f6 100644 --- a/crates/coremem/src/stim.rs +++ b/crates/coremem/src/stim.rs @@ -217,22 +217,28 @@ where // } -/// generic stimuli collection which monomorphizes everything by boxing it. -#[derive(Default)] -pub struct DynStimuli(Vec>); +pub struct StimuliVec(Vec); -impl DynStimuli { +pub type DynStimuli = StimuliVec>; + +impl Default for StimuliVec { + fn default() -> Self { + Self(Vec::new()) + } +} + +impl StimuliVec { pub fn new() -> Self { Self::default() } - pub fn from_vec(stim: Vec>) -> Self { + pub fn from_vec(stim: Vec) -> Self { Self(stim) } - pub fn push(&mut self, a: Box) { + pub fn push(&mut self, a: S) { self.0.push(a) } } -impl Stimulus for DynStimuli { +impl Stimulus for StimuliVec { fn at(&self, t_sec: f32, feat_size: f32, loc: Index) -> Fields { self.0.iter().map(|i| i.at(t_sec, feat_size, loc)) .fold(Fields::default(), core::ops::Add::add) @@ -243,12 +249,12 @@ impl Stimulus for DynStimuli { } } } -// -// impl Stimulus for Box { -// fn at(&self, t_sec: f32, pos: Meters) -> Fields { -// (**self).at(t_sec, pos) -// } -// } + +impl Stimulus for Box { + fn at(&self, t_sec: f32, feat_size: f32, loc: Index) -> Fields { + (**self).at(t_sec, feat_size, loc) + } +} pub struct NoopStimulus; impl Stimulus for NoopStimulus { @@ -331,32 +337,27 @@ impl VectorField for RegionGated { } } -/// Apply a time-varying stimulus across some region. -/// The stimulus seen at each point is based on its angle about the specified ray. +/// apply a stimulus across some region. +/// the stimulus seen at each point is based on its angle about the specified ray. +/// the stimulus has equal E and H vectors. if you want just one, filter it one with `Scaled`. #[derive(Clone)] -pub struct CurlStimulus { - region: R, - stim: T, +pub struct CurlVectorField { + region: R } -impl CurlStimulus { - pub fn new(region: R, stim: T) -> Self { - Self { region, stim } +impl CurlVectorField { + pub fn new(region: R) -> Self { + Self { region } } } -impl Stimulus for CurlStimulus { - fn at(&self, t_sec: f32, feat_size: f32, loc: Index) -> Fields { +impl VectorField for CurlVectorField { + fn at(&self, feat_size: f32, loc: Index) -> Fields { let pos = loc.to_meters(feat_size); if self.region.contains(pos) { - let FieldMags { e, h } = self.stim.at(t_sec); - let rotational = self.region.cross_section_normal(pos); - let impulse_e = rotational.with_mag(e.cast()).unwrap_or_default(); - let impulse_h = rotational.with_mag(h.cast()).unwrap_or_default(); - Fields { - e: impulse_e, - h: impulse_h, - } + // TODO: do we *want* this to be normalized? + let rotational = self.region.cross_section_normal(pos).norm(); + Fields::new_eh(rotational, rotational) } else { Fields::default() } @@ -370,6 +371,9 @@ pub trait StimExt: Sized { fn gated(self, from: f32, to: f32) -> Gated { Gated::new(self, from, to) } + fn scaled(self, scale: T) -> Scaled { + Scaled::new(self, scale) + } } impl StimExt for T {} @@ -386,6 +390,12 @@ impl TimeVarying for f32 { } } +impl TimeVarying for Vec { + fn at(&self, t_sec: f32) -> FieldMags { + self.iter().fold(FieldMags::default(), |acc, i| acc + i.at(t_sec)) + } +} + /// E field which changes magnitude sinusoidally as a function of t #[derive(Clone)] pub struct Sinusoid { @@ -568,6 +578,21 @@ impl Stimulus for Shifted { } } + +pub struct Scaled(A, B); + +impl Scaled { + pub fn new(a: A, b: B) -> Self { + Self(a, b) + } +} + +impl TimeVarying for Scaled { + fn at(&self, t_sec: f32) -> FieldMags { + self.0.at(t_sec).elem_mul(self.1.at(t_sec)) + } +} + #[cfg(test)] mod test { use super::*; @@ -621,26 +646,10 @@ mod test { let region = MockRegion { normal: Vec3::new(1.0, 0.0, 0.0) }; - // stim acts as e: 1.0, h: 0.0 - let stim = CurlStimulus::new(region, 1.0); - assert_eq!(stim.at(0.0, 1.0, Index::new(0, 0, 0)), Fields { + let stim = CurlVectorField::new(region); + assert_eq!(stim.at(1.0, Index::new(0, 0, 0)), Fields { e: Vec3::new(1.0, 0.0, 0.0), - h: Vec3::new(0.0, 0.0, 0.0), - }); - } - - #[test] - fn curl_stimulus_scales_right() { - let region = MockRegion { - // the magnitude of this normal shouldn't influence the curl. - // though, maybe it would be more useful if it *did*? - normal: Vec3::new(5.0, 0.0, 0.0) - }; - // stim acts as e: -3.0, h: 0.0 - let stim = CurlStimulus::new(region, -3.0); - assert_eq!(stim.at(0.0, 1.0, Index::new(0, 0, 0)), Fields { - e: Vec3::new(-3.0, 0.0, 0.0), - h: Vec3::new(0.0, 0.0, 0.0), + h: Vec3::new(1.0, 0.0, 0.0), }); } @@ -649,12 +658,9 @@ mod test { let region = MockRegion { normal: Vec3::new(0.0, -1.0, 1.0) }; - // stim acts as e: 2.0, h: 0.0 - let stim = CurlStimulus::new(region, 2.0); - let Fields { e, h: _ } = stim.at(0.0, 1.0, Index::new(0, 0, 0)); - assert!(e.distance( - Vec3::new(0.0, -1.0, 1.0).with_mag(2.0).unwrap() - ) < 1e-6 - ); + let stim = CurlVectorField::new(region); + let Fields { e, h } = stim.at(1.0, Index::new(0, 0, 0)); + assert_eq!(e, h); + assert!(e.distance(Vec3::new(0.0, -1.0, 1.0).norm()) < 1e-6); } }