diff --git a/crates/applications/multi_core_inverter/src/main.rs b/crates/applications/multi_core_inverter/src/main.rs index 01dc641..ed74ab5 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::{CurlVectorField, Exp, Gated, ModulatedVectorField, Shifted, StimExt as _, StimuliVec}; +use coremem::stim::{CurlVectorField, Exp, Gated, ModulatedVectorField, Shifted, StimExt as _}; use coremem::Driver; type R = real::R32; @@ -248,7 +248,6 @@ fn main() { }) .collect(); assert_eq!(stim.len(), 5); - let stim = StimuliVec::from_vec(stim); let wire_mat = IsomorphicConductor::new(1e6f32.cast::()); @@ -304,7 +303,10 @@ fn main() { let duration = Seconds(params.clock_phase_duration * (num_cycles + 3) as f32); // let stim = DynStimuli::from_vec(stim.map(MapIntoBoxStimulus).into_vec()); - let mut driver = driver.with_stimulus(stim); + let mut driver = driver.with_concrete_stimulus(); + for s in stim { + driver.add_stimulus(s); + } let prefix = "out/applications/multi_core_inverter/20-4ns-1e7A/"; let _ = std::fs::create_dir_all(&prefix); diff --git a/crates/coremem/src/driver.rs b/crates/coremem/src/driver.rs index 5b582d5..833d398 100644 --- a/crates/coremem/src/driver.rs +++ b/crates/coremem/src/driver.rs @@ -6,8 +6,9 @@ use crate::real::Real; use crate::render::{self, MultiRenderer, Renderer}; use crate::sim::AbstractSim; use crate::sim::units::{Frame, Time}; -use crate::stim::{Stimulus, DynStimuli}; +use crate::stim::{DynStimuli, Stimulus, StimuliVec}; use coremem_cross::compound::list; +use coremem_cross::step::SimMeta; use log::{info, trace}; use serde::{Deserialize, Serialize}; @@ -17,7 +18,7 @@ use std::sync::mpsc::{sync_channel, SyncSender, Receiver}; use std::time::Instant; use threadpool::ThreadPool; -pub struct Driver { +pub struct Driver { state: S, renderer: Arc>, // TODO: use Rayon's thread pool? @@ -56,26 +57,32 @@ impl Driver { pub fn add_measurement + 'static>(&mut self, m: Meas) { self.measurements.push(Arc::new(m)); } + + pub fn add_stimulus(&mut self, s: SNew) + where Stim: Pushable + { + self.stimuli.push(s) + } } -impl Driver { +impl Driver { pub fn new(state: S) -> Self { - Self::new_with_stim(state, DynStimuli::default()) - } - pub fn add_stimulus(&mut self, s: Stim) { - self.stimuli.push(Box::new(s)) + Self::new_with_stim(state, DriverStimulusDynVec::default()) } } impl Driver { - pub fn new_unboxed_stim(state: S) -> Self { + pub fn new_list_stim(state: S) -> Self { Self::new_with_stim(state, list::Empty::default()) } } impl Driver { - pub fn with_add_stimulus(self, s: E) -> Driver> - where Stim: list::Prependable + /// add a stimulus onto a list of non-monomorphized stimuli. + /// this necessarily must return a new Self. + /// (well, with enough tuning we could actually Box just the first reference... + pub fn with_add_stimulus(self, s: E) -> Driver> + where Stim: list::Appendable { Driver { state: self.state, @@ -83,7 +90,7 @@ impl Driver { render_pool: self.render_pool, render_channel: self.render_channel, measurements: self.measurements, - stimuli: self.stimuli.prepend(s), + stimuli: self.stimuli.append(s), sim_end_time: self.sim_end_time, diag: self.diag, last_diag_time: self.last_diag_time, @@ -102,6 +109,9 @@ impl Driver { last_diag_time: self.last_diag_time, } } + pub fn with_concrete_stimulus(self) -> Driver> { + self.with_stimulus(DriverStimulusVec::::default()) + } } impl Driver { @@ -199,7 +209,7 @@ where impl Driver where S: AbstractSim + Clone + Default + Send + 'static, - Stim: Stimulus, + Stim: DriverStimulus, { fn render(&mut self) { self.diag.instrument_render_prep(|| { @@ -238,7 +248,8 @@ where } trace!("step begin"); self.diag.instrument_step(can_step as u64, || { - self.state.step_multiple(can_step, &self.stimuli); + let stim = self.stimuli.optimized_for(self.state.meta()); + self.state.step_multiple(can_step, stim.as_ref()); }); trace!("step end"); if self.last_diag_time.elapsed().as_secs_f64() >= 5.0 { @@ -290,3 +301,70 @@ where self.sim_end_time = None; } } + +pub enum ValueOrRef<'a, T> { + Value(T), + Ref(&'a T), +} +impl<'a, T> AsRef for ValueOrRef<'a, T> { + fn as_ref(&self) -> &T { + match self { + ValueOrRef::Value(x) => &x, + ValueOrRef::Ref(x) => x, + } + } +} + +/// gives an opportunity to optimize a Stimulus for a specific setting +/// before passing it off to the simulation. +pub trait DriverStimulus { + type Optimized: Stimulus; + fn optimized_for<'a>( + &'a self, meta: SimMeta + ) -> ValueOrRef<'a, Self::Optimized>; +} + +pub trait Pushable { + fn push(&mut self, t: T); +} + +pub struct DriverStimulusVec(StimuliVec); + +impl Default for DriverStimulusVec { + fn default() -> Self { + Self(Default::default()) + } +} + +impl DriverStimulus for DriverStimulusVec { + type Optimized = StimuliVec; + fn optimized_for<'a>( + &'a self, _meta: SimMeta + ) -> ValueOrRef<'a, Self::Optimized> { + ValueOrRef::Ref(&self.0) + } +} + +impl Pushable for DriverStimulusVec { + fn push(&mut self, t: T) { + self.0.push(t) + } +} + +#[derive(Default)] +pub struct DriverStimulusDynVec(DynStimuli); + +impl DriverStimulus for DriverStimulusDynVec { + type Optimized = DynStimuli; + fn optimized_for<'a>( + &'a self, _meta: SimMeta + ) -> ValueOrRef<'a, Self::Optimized> { + ValueOrRef::Ref(&self.0) + } +} + +impl Pushable for DriverStimulusDynVec { + fn push(&mut self, t: T) { + self.0.push(Box::new(t)) + } +}