diff --git a/examples/em_reflection.rs b/examples/em_reflection.rs index 7b94025..1b4b53d 100644 --- a/examples/em_reflection.rs +++ b/examples/em_reflection.rs @@ -7,7 +7,7 @@ fn main() { let height = 401; let size = Index((width, height, 1).into()); let mut driver: Driver = Driver::new(size, 1e-6 /* feature size */); - driver.add_y4m_renderer("em_reflection.y4m"); + driver.add_y4m_renderer("em_reflection.y4m", 1); // driver.add_term_renderer(); // driver.add_pml_boundary(Index((20, 40, 0).into())); diff --git a/examples/minimal_torus.rs b/examples/minimal_torus.rs index 5e844f4..b194e8a 100644 --- a/examples/minimal_torus.rs +++ b/examples/minimal_torus.rs @@ -18,6 +18,7 @@ fn main() { 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 conductivity = 1.0e9f32; + let steps_per_frame = 400; let from_m = |m: f32| (m/feat_size).round() as u32; let m_to_um = |m: f32| (m * 1e6).round() as u32; @@ -28,7 +29,6 @@ fn main() { let depth_px = from_m(depth); let size_px = Index((width_px, width_px, depth_px).into()); let mut driver: Driver = Driver::new(size_px, feat_size); - driver.set_steps_per_frame(400); driver.set_steps_per_stim(1); let base = "minimal_torus-6"; let ferro_region = Torus::new_xy(Meters::new(half_width, half_width, half_depth), ferro_major, ferro_minor); @@ -117,7 +117,7 @@ fn main() { ); let _ = std::fs::create_dir_all(&prefix); - driver.add_serializer_renderer(&*format!("{}/frame-", prefix)); + driver.add_serializer_renderer(&*format!("{}/frame-", prefix), steps_per_frame); driver.step_until(duration); } diff --git a/examples/toroid25d.rs b/examples/toroid25d.rs index d55beb0..7fd6739 100644 --- a/examples/toroid25d.rs +++ b/examples/toroid25d.rs @@ -25,6 +25,7 @@ fn main() { let conductivity = 1.0e5; let half_width = width * 0.5; let half_depth = depth * 0.5; + let steps_per_frame = 160; let duration = 1e-9; @@ -32,13 +33,6 @@ fn main() { let depth_px = from_m(depth); let size_px = Index((width_px, width_px, depth_px).into()); let mut driver: Driver = Driver::new(size_px, feat_size); - //driver.set_steps_per_frame(8); - //driver.set_steps_per_frame(20); - //driver.set_steps_per_frame(40); - //driver.set_steps_per_frame(80); - //driver.set_steps_per_frame(120); - driver.set_steps_per_frame(160); - //driver.set_steps_per_frame(200); let base = "toroid25d-9"; let _ = std::fs::create_dir(base); let prefix = format!("{}/{}-flt{}-{}-feat{}um-{}mA-{}ps--radii{}um-{}um-{}um-{}um", @@ -55,13 +49,13 @@ fn main() { m_to_um(ferro_depth), ); let _ = std::fs::create_dir(&prefix); - //driver.add_y4m_renderer(&*format!("{}.y4m", prefix)); - //driver.add_plotly_renderer(&*format!("{}/frame-", prefix)); - driver.add_serializer_renderer(&*format!("{}/frame-", prefix)); + //driver.add_y4m_renderer(&*format!("{}.y4m", prefix), steps_per_frame); + //driver.add_plotly_renderer(&*format!("{}/frame-", prefix), steps_per_frame); + driver.add_serializer_renderer(&*format!("{}/frame-", prefix), steps_per_frame); let conductor_region = CylinderZ::new( Vec2::new(half_width, half_width), conductor_outer_rad); - // driver.add_term_renderer(); + // driver.add_term_renderer(steps_per_frame); driver.add_measurement(meas::Label(format!("Conductivity: {}, Imax: {:.2e}", conductivity, peak_current))); //driver.add_measurement(meas::Current(conductor_region.clone())); driver.add_measurement(meas::MagnetizationAt( diff --git a/examples/wrapped_torus.rs b/examples/wrapped_torus.rs index be2f053..1f7dbeb 100644 --- a/examples/wrapped_torus.rs +++ b/examples/wrapped_torus.rs @@ -6,27 +6,27 @@ use coremem::stim::{CurlStimulus, Sinusoid1, TimeVarying1 as _}; fn main() { coremem::init_logging(); let feat_size = 10e-6f32; // feature size - let duration = 3.0e-9; - let width = 4800e-6; - let height = 2200e-6; - let depth = 1800e-6; + let duration = 60.0e-9; + let width = 3400e-6; + let height = 1800e-6; + let depth = 1400e-6; let buffer = 200e-6; + let ferro_spacing = 600e-6; let ferro_major = 320e-6; let ferro_minor = 60e-6; let wire_minor = 40e-6; let wire_major = 160e-6; let peak_current1 = 7.5e6; - let peak_current2 = 5e5; - let current_duration = 0.4e-9; // half-wavelength of the sine wave + //let peak_current2 = 5e5; + let peak_current2 = 7.5e6; + let current_duration = 1.0e-9; // half-wavelength of the sine wave let current_break = 0.2e-9; // time between 'set' pulse and 'clear' pulse let drive_conductivity = 5e6f32; let sense_conductivity = 5e3f32; let from_m = |m: f32| (m/feat_size).round() as u32; let m_to_um = |m: f32| (m * 1e6).round() as u32; - // let half_width = width * 0.5; - let q1_width = width * 0.25; - let q3_width = width * 0.75; + let half_width = width * 0.5; let half_height = height * 0.5; let half_depth = depth * 0.5; @@ -35,40 +35,33 @@ fn main() { let depth_px = from_m(depth); let size_px = Index((width_px, height_px, depth_px).into()); let mut driver: Driver = Driver::new(size_px, feat_size); - driver.set_steps_per_frame(500); - // driver.set_steps_per_stim(10); - let base = "wrapped_torus-29-mbferromagnet-0.3899B-310_000M-longer-current"; + driver.set_steps_per_stim(10); + let base = "wrapped_torus-30-current-decay"; - let ferro1_region = Torus::new_xy(Meters::new(q1_width, half_height, half_depth), ferro_major, ferro_minor); - let drive1_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); + let ferro1_center = half_width - ferro_major - 0.5*ferro_spacing; + let ferro1_region = Torus::new_xy(Meters::new(ferro1_center, half_height, half_depth), ferro_major, ferro_minor); + let drive1_region = Torus::new_xz(Meters::new(ferro1_center - ferro_major, half_height, half_depth), wire_major, wire_minor); + let sense1_region = Torus::new_xz(Meters::new(ferro1_center + 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::MBFerromagnet::new(-0.3899, 0.3900, 310_000.0).into()); + driver.fill_region(&ferro1_region, mat::db::linear_iron().into()); + //driver.fill_region(&ferro1_region, mat::MBFerromagnet::new(-0.3899, 0.3900, 310_000.0).into()); driver.fill_region(&drive1_region, mat::db::conductor(drive_conductivity).into()); driver.fill_region(&sense1_region, mat::db::conductor(sense_conductivity).into()); - 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 sense2_region = Torus::new_xz(Meters::new(q3_width + ferro_major, half_height, half_depth), wire_major, wire_minor); + let ferro2_center = half_width + ferro_major + 0.5*ferro_spacing; + let ferro2_region = Torus::new_xy(Meters::new(ferro2_center, half_height, half_depth), ferro_major, ferro_minor); + let drive2_region = Torus::new_xz(Meters::new(ferro2_center - ferro_major, half_height, half_depth), wire_major, wire_minor); + let sense2_region = Torus::new_xz(Meters::new(ferro2_center + ferro_major, half_height, half_depth), wire_major, wire_minor); driver.fill_region(&ferro2_region, mat::MBFerromagnet::new(-0.3899, 0.3900, 310_000.0).into()); driver.fill_region(&drive2_region, mat::db::conductor(drive_conductivity).into()); driver.fill_region(&sense2_region, mat::db::conductor(sense_conductivity).into()); - let boundary_xy = q1_width - ferro_major - ferro_minor - buffer; + let boundary_xy = ferro1_center - ferro_major - ferro_minor - buffer; let boundary_z = half_depth - wire_major - wire_minor - buffer; println!("boundary: {}um; {}um", m_to_um(boundary_xy), m_to_um(boundary_z)); driver.add_pml_boundary(Meters::new(boundary_xy, boundary_xy, boundary_z)); - // J=\sigma E - // dJ/dt = \sigma dE/dT - // dE/dt = dJ/dt / \sigma - // dE/dt = dI/dt / (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) - let peak_stim1 = peak_current1/current_duration / (drive1_region.cross_section() * drive_conductivity); - let mut add_drive_pulse = |region: &Torus, start, duration, amp| { let wave = Sinusoid1::from_wavelength(amp, duration * 2.0) .half_cycle() @@ -81,16 +74,24 @@ fn main() { )); }; + // J=\sigma E + // dJ/dt = \sigma dE/dT + // dE/dt = dJ/dt / \sigma + // dE/dt = dI/dt / (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) + let peak_stim1 = peak_current1/current_duration / (drive1_region.cross_section() * drive_conductivity); + add_drive_pulse(&drive1_region, 0.0, current_duration, peak_stim1); - add_drive_pulse(&drive1_region, current_duration + current_break, current_duration, -4.0*peak_stim1); - add_drive_pulse(&drive1_region, 2.0*(current_duration + current_break), current_duration, -4.0*peak_stim1); - add_drive_pulse(&drive1_region, 3.0*(current_duration + current_break), current_duration, peak_stim1); + // add_drive_pulse(&drive1_region, current_duration + current_break, current_duration, -4.0*peak_stim1); + // add_drive_pulse(&drive1_region, 2.0*(current_duration + current_break), current_duration, -4.0*peak_stim1); + // add_drive_pulse(&drive1_region, 3.0*(current_duration + current_break), current_duration, peak_stim1); let peak_stim2 = peak_current2/current_duration / (drive2_region.cross_section() * drive_conductivity); add_drive_pulse(&drive2_region, 0.0, current_duration, peak_stim2); - add_drive_pulse(&drive2_region, current_duration + current_break, current_duration, -4.0*peak_stim2); - add_drive_pulse(&drive2_region, 2.0*(current_duration + current_break), current_duration, -4.0*peak_stim2); - add_drive_pulse(&drive2_region, 3.0*(current_duration + current_break), current_duration, peak_stim2); + // add_drive_pulse(&drive2_region, current_duration + current_break, current_duration, -4.0*peak_stim2); + // add_drive_pulse(&drive2_region, 2.0*(current_duration + current_break), current_duration, -4.0*peak_stim2); + // add_drive_pulse(&drive2_region, 3.0*(current_duration + current_break), current_duration, peak_stim2); driver.add_measurement(meas::CurrentLoop::new("sense1", sense1_region.clone())); driver.add_measurement(meas::Current::new("sense1", sense1_region.clone())); @@ -125,7 +126,7 @@ fn main() { ); let _ = std::fs::create_dir_all(&prefix); - driver.add_serializer_renderer(&*format!("{}/frame-", prefix)); + driver.add_serializer_renderer(&*format!("{}/frame-", prefix), 5000); driver.step_until(duration); } diff --git a/src/driver.rs b/src/driver.rs index b363da0..1145573 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -20,7 +20,6 @@ pub struct Driver> { // TODO: use Rayon's thread pool? render_pool: ThreadPool, render_channel: (SyncSender<()>, Receiver<()>), - steps_per_frame: u64, time_spent_stepping: Duration, time_spent_on_stimuli: Duration, time_spent_prepping_render: Duration, @@ -39,7 +38,6 @@ impl Driver { renderer: Arc::new(MultiRenderer::new()), render_pool: ThreadPool::new(3), render_channel: sync_channel(0), - steps_per_frame: 1, time_spent_stepping: Default::default(), time_spent_on_stimuli: Default::default(), time_spent_prepping_render: Default::default(), @@ -67,10 +65,6 @@ impl> Driver { self.measurements.push(Box::new(m)); } - pub fn set_steps_per_frame(&mut self, steps_per_frame: u64) { - self.steps_per_frame = steps_per_frame; - } - pub fn set_steps_per_stim(&mut self, steps_per_stim: u64) { self.stimuli.frame_interval = steps_per_stim; } @@ -88,30 +82,30 @@ impl + Clone + Send + Sync + 'static> Driver { } fn add_renderer> + 'static>( - &mut self, renderer: Rend, name: &str + &mut self, renderer: Rend, name: &str, step_frequency: u64 ) { - info!("render to {}", name); - self.renderer.push(renderer); + info!("render to {} at f={}", name, step_frequency); + self.renderer.push(renderer, step_frequency); } - pub fn add_y4m_renderer>(&mut self, output: S) { + pub fn add_y4m_renderer>(&mut self, output: S, step_frequency: u64) { let output = output.into(); let name = output.to_string_lossy().into_owned(); - self.add_renderer(render::Y4MRenderer::new(output), &*name); + self.add_renderer(render::Y4MRenderer::new(output), &*name, step_frequency); } - pub fn add_plotly_renderer(&mut self, out_base: &str) { - self.add_renderer(render::PlotlyRenderer::new(out_base), out_base); + pub fn add_plotly_renderer(&mut self, out_base: &str, step_frequency: u64) { + self.add_renderer(render::PlotlyRenderer::new(out_base), out_base, step_frequency); } - pub fn add_term_renderer(&mut self) { - self.add_renderer(render::ColorTermRenderer, "terminal"); + pub fn add_term_renderer(&mut self, step_frequency: u64) { + self.add_renderer(render::ColorTermRenderer, "terminal", step_frequency); } } impl + Serialize + Clone + Send + Sync + 'static> Driver { - pub fn add_serializer_renderer(&mut self, out_base: &str) { - self.add_renderer(render::SerializerRenderer::new(out_base), out_base); + pub fn add_serializer_renderer(&mut self, out_base: &str, step_frequency: u64) { + self.add_renderer(render::SerializerRenderer::new(out_base), out_base, step_frequency); } } @@ -146,7 +140,7 @@ impl + Clone + Default + Send + trace!("stimuli end"); } - if self.state.step_no() % self.steps_per_frame == 0 { + if self.renderer.any_work_for_frame(self.state.step_no()) { self.render(); } diff --git a/src/render.rs b/src/render.rs index 94c4264..490eabf 100644 --- a/src/render.rs +++ b/src/render.rs @@ -592,8 +592,14 @@ impl Renderer for PlotlyRenderer { } } +struct MultiRendererElement { + step_frequency: u64, + renderer: Box>, +} + + pub struct MultiRenderer { - renderers: RwLock>>>, + renderers: RwLock>>, } impl Default for MultiRenderer { @@ -608,13 +614,19 @@ impl MultiRenderer { pub fn new() -> Self { Default::default() } - pub fn push + 'static>(&self, r: R) { - self.renderers.write().unwrap().push(Box::new(r)); + pub fn push + 'static>(&self, renderer: R, step_frequency: u64) { + self.renderers.write().unwrap().push(MultiRendererElement { + step_frequency, + renderer: Box::new(renderer), + }); } - pub fn with + 'static>(self, r: R) -> Self { - self.push(r); + pub fn with + 'static>(self, renderer: R, step_frequency: u64) -> Self { + self.push(renderer, step_frequency); self } + pub fn any_work_for_frame(&self, frame: u64) -> bool { + self.renderers.read().unwrap().iter().any(|m| frame % m.step_frequency == 0) + } } impl Renderer for MultiRenderer { @@ -629,7 +641,9 @@ impl Renderer for MultiRenderer { fn render_with_image(&self, state: &S, im: &RgbImage, measurements: &[Box], config: RenderConfig) { for r in &*self.renderers.read().unwrap() { - r.render_with_image(state, im, measurements, config); + if state.step_no() % r.step_frequency == 0 { + r.renderer.render_with_image(state, im, measurements, config); + } } } }