2022-07-24 01:57:17 +00:00
|
|
|
use crate::geom::{Coord, Index, Meters, Region};
|
2022-07-25 01:31:11 +00:00
|
|
|
use crate::mat;
|
2020-09-08 03:14:41 +00:00
|
|
|
use crate::meas::{self, AbstractMeasurement};
|
2022-07-27 23:34:50 +00:00
|
|
|
use crate::real::Real;
|
2020-09-06 18:24:48 +00:00
|
|
|
use crate::render::{self, MultiRenderer, Renderer};
|
2022-07-29 05:32:29 +00:00
|
|
|
use crate::sim::AbstractSim;
|
2022-01-12 02:23:07 +00:00
|
|
|
use crate::sim::units::{Frame, Time};
|
2022-07-31 00:17:17 +00:00
|
|
|
use crate::stim::{self, AbstractStimulus};
|
2020-09-06 18:24:48 +00:00
|
|
|
|
2020-12-08 06:55:24 +00:00
|
|
|
use log::{info, trace};
|
2021-06-15 04:04:08 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2020-09-06 18:24:48 +00:00
|
|
|
use std::path::PathBuf;
|
2020-11-28 05:22:42 +00:00
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
use std::sync::mpsc::{sync_channel, SyncSender, Receiver};
|
|
|
|
use std::time::{Duration, Instant};
|
|
|
|
use threadpool::ThreadPool;
|
2020-09-06 18:24:48 +00:00
|
|
|
|
2022-07-28 08:55:22 +00:00
|
|
|
pub struct Driver<S> {
|
2022-07-28 09:01:46 +00:00
|
|
|
state: S,
|
2021-08-07 22:57:40 +00:00
|
|
|
renderer: Arc<MultiRenderer<S>>,
|
2020-12-16 21:53:39 +00:00
|
|
|
// TODO: use Rayon's thread pool?
|
2020-11-28 05:22:42 +00:00
|
|
|
render_pool: ThreadPool,
|
|
|
|
render_channel: (SyncSender<()>, Receiver<()>),
|
2022-08-12 01:27:30 +00:00
|
|
|
measurements: Vec<Arc<dyn AbstractMeasurement<S>>>,
|
|
|
|
stimuli: StimuliAdapter,
|
|
|
|
/// simulation end time
|
|
|
|
sim_end_time: Option<Frame>,
|
2022-08-12 01:58:22 +00:00
|
|
|
diag: SyncDiagnostics,
|
2022-08-12 01:38:36 +00:00
|
|
|
last_diag_time: Instant,
|
2022-08-12 01:27:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct Diagnostics {
|
2022-08-12 01:29:58 +00:00
|
|
|
frames_completed: u64,
|
2020-09-06 18:37:45 +00:00
|
|
|
time_spent_stepping: Duration,
|
2020-09-26 22:50:34 +00:00
|
|
|
time_spent_on_stimuli: Duration,
|
2020-12-18 06:22:25 +00:00
|
|
|
time_spent_prepping_render: Duration,
|
2020-11-28 05:22:42 +00:00
|
|
|
time_spent_blocked_on_render: Duration,
|
2022-08-12 01:58:22 +00:00
|
|
|
time_spent_rendering: Duration,
|
2020-11-28 05:22:42 +00:00
|
|
|
start_time: Instant,
|
2022-08-12 01:27:30 +00:00
|
|
|
}
|
|
|
|
|
2022-08-12 01:58:22 +00:00
|
|
|
#[derive(Clone)]
|
|
|
|
struct SyncDiagnostics(Arc<Mutex<Diagnostics>>);
|
|
|
|
|
2022-08-12 01:27:30 +00:00
|
|
|
impl Diagnostics {
|
|
|
|
fn new() -> Self {
|
|
|
|
Self {
|
2022-08-12 01:29:58 +00:00
|
|
|
frames_completed: 0,
|
2022-08-12 01:27:30 +00:00
|
|
|
time_spent_stepping: Default::default(),
|
|
|
|
time_spent_on_stimuli: Default::default(),
|
|
|
|
time_spent_prepping_render: Default::default(),
|
|
|
|
time_spent_blocked_on_render: Default::default(),
|
|
|
|
time_spent_rendering: Default::default(),
|
|
|
|
start_time: Instant::now(),
|
|
|
|
}
|
|
|
|
}
|
2022-08-12 01:41:41 +00:00
|
|
|
|
2022-08-12 01:36:35 +00:00
|
|
|
fn format(&self) -> String {
|
|
|
|
let step_time = self.time_spent_stepping.as_secs_f64();
|
|
|
|
let stim_time = self.time_spent_on_stimuli.as_secs_f64();
|
2022-08-12 01:58:22 +00:00
|
|
|
let render_time = self.time_spent_rendering.as_secs_f64();
|
2022-08-12 01:36:35 +00:00
|
|
|
let render_prep_time = self.time_spent_prepping_render.as_secs_f64();
|
|
|
|
let block_time = self.time_spent_blocked_on_render.as_secs_f64();
|
|
|
|
let overall_time = self.start_time.elapsed().as_secs_f64();
|
|
|
|
let fps = (self.frames_completed as f64) / overall_time;
|
|
|
|
format!("fps: {:6.2} (sim: {:.1}s, stim: {:.1}s, [render: {:.1}s], blocked: {:.1}s, render_prep: {:.1}s, other: {:.1}s)",
|
|
|
|
fps,
|
|
|
|
step_time,
|
|
|
|
stim_time,
|
|
|
|
render_time,
|
|
|
|
block_time,
|
|
|
|
render_prep_time,
|
|
|
|
overall_time - step_time - stim_time - block_time - render_prep_time,
|
|
|
|
)
|
|
|
|
}
|
2022-08-12 01:58:22 +00:00
|
|
|
}
|
2022-08-12 01:41:41 +00:00
|
|
|
|
2022-08-12 01:58:22 +00:00
|
|
|
impl SyncDiagnostics {
|
|
|
|
fn new() -> Self {
|
|
|
|
Self(Arc::new(Mutex::new(Diagnostics::new())))
|
|
|
|
}
|
|
|
|
fn format(&self) -> String {
|
|
|
|
self.0.lock().unwrap().format()
|
|
|
|
}
|
2022-08-12 02:04:12 +00:00
|
|
|
/// measure the duration of some arbitrary chunk of code.
|
|
|
|
/// used internally.
|
|
|
|
fn measure<F: FnOnce()>(f: F) -> Duration {
|
|
|
|
let start = Instant::now();
|
|
|
|
f();
|
|
|
|
start.elapsed()
|
|
|
|
}
|
2022-08-12 01:58:22 +00:00
|
|
|
|
2022-08-12 02:04:12 +00:00
|
|
|
/// record the duration of the sim step operation.
|
2022-08-12 01:58:22 +00:00
|
|
|
fn instrument_step<F: FnOnce()>(&self, frames: u64, f: F) {
|
2022-08-12 02:04:12 +00:00
|
|
|
let elapsed = Self::measure(f);
|
2022-08-12 01:58:22 +00:00
|
|
|
let mut me = self.0.lock().unwrap();
|
|
|
|
me.time_spent_stepping += elapsed;
|
|
|
|
me.frames_completed += frames as u64;
|
|
|
|
}
|
|
|
|
|
2022-08-12 02:04:12 +00:00
|
|
|
/// record the duration spent preparing for render (i.e. cloning stuff and moving it into a
|
|
|
|
/// render pool).
|
2022-08-12 01:58:22 +00:00
|
|
|
fn instrument_render_prep<F: FnOnce()>(&self, f: F) {
|
2022-08-12 02:04:12 +00:00
|
|
|
let elapsed = Self::measure(f);
|
2022-08-12 01:58:22 +00:00
|
|
|
self.0.lock().unwrap().time_spent_prepping_render += elapsed;
|
|
|
|
}
|
|
|
|
|
2022-08-12 02:04:12 +00:00
|
|
|
/// record the duration actually spent doing CPU render work
|
2022-08-12 01:58:22 +00:00
|
|
|
fn instrument_render_cpu_side<F: FnOnce()>(&self, f: F) {
|
2022-08-12 02:04:12 +00:00
|
|
|
let elapsed = Self::measure(f);
|
2022-08-12 01:58:22 +00:00
|
|
|
self.0.lock().unwrap().time_spent_rendering += elapsed;
|
2022-08-12 01:41:41 +00:00
|
|
|
}
|
2022-08-12 02:04:12 +00:00
|
|
|
|
|
|
|
/// record the duration spent blocking the simulation because the render queue is full.
|
|
|
|
fn instrument_render_blocked<F: FnOnce()>(&self, f: F) {
|
|
|
|
let elapsed = Self::measure(f);
|
|
|
|
self.0.lock().unwrap().time_spent_blocked_on_render += elapsed;
|
|
|
|
}
|
2020-09-06 18:24:48 +00:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:32:29 +00:00
|
|
|
impl<S: AbstractSim> Driver<S> {
|
2022-07-28 08:59:11 +00:00
|
|
|
pub fn new(state: S) -> Self {
|
2021-08-08 00:49:29 +00:00
|
|
|
Self {
|
|
|
|
state,
|
2020-11-28 05:22:42 +00:00
|
|
|
renderer: Arc::new(MultiRenderer::new()),
|
|
|
|
render_pool: ThreadPool::new(3),
|
|
|
|
render_channel: sync_channel(0),
|
2020-12-18 04:07:25 +00:00
|
|
|
measurements: vec![
|
2022-07-29 04:49:28 +00:00
|
|
|
Arc::new(meas::Time),
|
|
|
|
Arc::new(meas::Meta),
|
|
|
|
Arc::new(meas::Energy::world()),
|
|
|
|
Arc::new(meas::Power::world()),
|
2020-12-18 04:07:25 +00:00
|
|
|
],
|
2020-12-18 03:23:42 +00:00
|
|
|
stimuli: StimuliAdapter::new(),
|
2022-01-03 20:48:51 +00:00
|
|
|
sim_end_time: None,
|
2022-08-12 01:58:22 +00:00
|
|
|
diag: SyncDiagnostics::new(),
|
2022-08-12 01:38:36 +00:00
|
|
|
last_diag_time: Instant::now(),
|
2020-09-06 18:24:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-08 00:24:13 +00:00
|
|
|
pub fn add_stimulus<Stim: AbstractStimulus + 'static>(&mut self, s: Stim) {
|
2020-09-26 22:50:34 +00:00
|
|
|
self.stimuli.push(Box::new(s))
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:22:07 +00:00
|
|
|
pub fn add_measurement<Meas: AbstractMeasurement<S> + 'static>(&mut self, m: Meas) {
|
2022-07-29 04:49:28 +00:00
|
|
|
self.measurements.push(Arc::new(m));
|
2020-09-08 03:14:41 +00:00
|
|
|
}
|
|
|
|
|
2020-12-18 03:23:42 +00:00
|
|
|
pub fn set_steps_per_stim(&mut self, steps_per_stim: u64) {
|
|
|
|
self.stimuli.frame_interval = steps_per_stim;
|
|
|
|
}
|
2020-12-17 07:23:54 +00:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:32:29 +00:00
|
|
|
impl<S: AbstractSim> Driver<S> {
|
2021-08-16 00:59:17 +00:00
|
|
|
pub fn fill_region<Reg: Region, M: Into<S::Material> + Clone>(&mut self, region: &Reg, mat: M) {
|
2021-01-03 05:34:35 +00:00
|
|
|
self.state.fill_region(region, mat);
|
2020-12-17 07:23:54 +00:00
|
|
|
}
|
2022-07-29 05:31:47 +00:00
|
|
|
pub fn test_region_filled<Reg: Region, M>(&mut self, region: &Reg, mat: M) -> bool
|
|
|
|
where
|
|
|
|
M: Into<S::Material> + Clone,
|
|
|
|
S::Material: PartialEq
|
|
|
|
{
|
2021-12-21 08:51:12 +00:00
|
|
|
self.state.test_region_filled(region, mat)
|
|
|
|
}
|
2020-12-17 07:23:54 +00:00
|
|
|
}
|
2020-09-06 18:24:48 +00:00
|
|
|
|
2022-07-29 05:32:29 +00:00
|
|
|
impl<S: AbstractSim> Driver<S> {
|
2021-12-23 14:30:26 +00:00
|
|
|
pub fn size(&self) -> Index {
|
|
|
|
self.state.size()
|
|
|
|
}
|
2022-01-12 02:23:07 +00:00
|
|
|
pub fn timestep(&self) -> f32 {
|
|
|
|
self.state.timestep()
|
|
|
|
}
|
2022-01-18 04:23:55 +00:00
|
|
|
pub fn time(&self) -> f32 {
|
|
|
|
self.state.time()
|
|
|
|
}
|
2021-12-23 14:30:26 +00:00
|
|
|
}
|
|
|
|
|
2022-07-29 06:49:45 +00:00
|
|
|
impl<S: AbstractSim + 'static> Driver<S> {
|
2021-08-08 00:24:13 +00:00
|
|
|
fn add_renderer<Rend: Renderer<S> + 'static>(
|
2022-01-11 22:03:46 +00:00
|
|
|
&mut self, renderer: Rend, name: &str, step_frequency: u64, frame_limit: Option<u64>
|
2021-06-08 22:37:50 +00:00
|
|
|
) {
|
2022-01-11 22:03:46 +00:00
|
|
|
info!("render to {} at f={} until f={:?}", name, step_frequency, frame_limit);
|
|
|
|
self.renderer.push(renderer, step_frequency, frame_limit);
|
2020-09-06 18:24:48 +00:00
|
|
|
}
|
|
|
|
|
2022-01-11 22:03:46 +00:00
|
|
|
pub fn add_y4m_renderer<P: Into<PathBuf>>(&mut self, output: P, step_frequency: u64, frame_limit: Option<u64>) {
|
2020-09-13 23:29:18 +00:00
|
|
|
let output = output.into();
|
|
|
|
let name = output.to_string_lossy().into_owned();
|
2022-01-11 22:03:46 +00:00
|
|
|
self.add_renderer(render::Y4MRenderer::new(output), &*name, step_frequency, frame_limit);
|
2020-09-06 18:24:48 +00:00
|
|
|
}
|
|
|
|
|
2022-01-11 22:03:46 +00:00
|
|
|
pub fn add_term_renderer(&mut self, step_frequency: u64, frame_limit: Option<u64>) {
|
|
|
|
self.add_renderer(render::ColorTermRenderer, "terminal", step_frequency, frame_limit);
|
2020-09-06 18:24:48 +00:00
|
|
|
}
|
2021-06-15 07:47:59 +00:00
|
|
|
|
2022-01-11 22:03:46 +00:00
|
|
|
pub fn add_csv_renderer(&mut self, path: &str, step_frequency: u64, frame_limit: Option<u64>) {
|
|
|
|
self.add_renderer(render::CsvRenderer::new(path), path, step_frequency, frame_limit);
|
2021-06-15 07:47:59 +00:00
|
|
|
}
|
2020-12-17 07:59:57 +00:00
|
|
|
}
|
2020-09-06 18:24:48 +00:00
|
|
|
|
2022-07-29 06:49:45 +00:00
|
|
|
impl<S: AbstractSim + Serialize + 'static> Driver<S> {
|
2022-01-11 22:03:46 +00:00
|
|
|
pub fn add_serializer_renderer(&mut self, out_base: &str, step_frequency: u64, frame_limit: Option<u64>) {
|
2021-06-15 03:46:22 +00:00
|
|
|
let fmt_str = format!("{out_base}{{step_no}}.bc", out_base=out_base);
|
2022-07-29 20:27:05 +00:00
|
|
|
self.add_renderer(render::SerializerRenderer::new_generic(&*fmt_str), &*fmt_str, step_frequency, frame_limit);
|
2021-06-15 04:04:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:32:29 +00:00
|
|
|
impl<S: AbstractSim + Send + Sync + Serialize + for<'a> Deserialize<'a> + 'static> Driver<S> {
|
2022-02-02 02:31:47 +00:00
|
|
|
/// instruct the driver to periodically save the simulation state to the provided path.
|
|
|
|
/// also attempts to load an existing state file, returning `true` on success.
|
|
|
|
pub fn add_state_file(&mut self, state_file: &str, snapshot_frequency: u64) -> bool {
|
2021-06-15 04:04:08 +00:00
|
|
|
let ser = render::SerializerRenderer::new(state_file);
|
2022-07-28 09:04:49 +00:00
|
|
|
let loaded = ser.try_load().map(|s| {
|
|
|
|
self.state = s.state;
|
|
|
|
}).is_some();
|
2022-01-11 22:03:46 +00:00
|
|
|
self.add_renderer(ser, state_file, snapshot_frequency, None);
|
2022-02-02 02:31:47 +00:00
|
|
|
loaded
|
2020-11-28 06:11:53 +00:00
|
|
|
}
|
2020-12-08 06:51:00 +00:00
|
|
|
}
|
2020-11-28 06:11:53 +00:00
|
|
|
|
2022-07-29 06:49:45 +00:00
|
|
|
impl<S: AbstractSim + Clone + Default + Send + 'static> Driver<S> {
|
2020-12-08 06:51:00 +00:00
|
|
|
fn render(&mut self) {
|
2022-08-12 01:58:22 +00:00
|
|
|
self.diag.instrument_render_prep(|| {
|
|
|
|
let diag_handle = self.diag.clone();
|
|
|
|
let their_state = self.state.clone();
|
|
|
|
let their_measurements = self.measurements.clone();
|
|
|
|
let renderer = self.renderer.clone();
|
|
|
|
let sender = self.render_channel.0.clone();
|
|
|
|
self.render_pool.execute(move || {
|
|
|
|
// unblock the main thread (this limits the number of renders in-flight at any time
|
|
|
|
sender.send(()).unwrap();
|
|
|
|
trace!("render begin");
|
|
|
|
diag_handle.instrument_render_cpu_side(|| {
|
|
|
|
let meas: Vec<&dyn AbstractMeasurement<S>> = their_measurements.iter().map(|m| &**m).collect();
|
|
|
|
renderer.render(&their_state, &*meas, Default::default());
|
|
|
|
});
|
|
|
|
trace!("render end");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
self.diag.instrument_render_blocked(|| {
|
|
|
|
self.render_channel.1.recv().unwrap();
|
2020-12-08 06:51:00 +00:00
|
|
|
});
|
|
|
|
}
|
2021-08-08 07:37:13 +00:00
|
|
|
/// Return the number of steps actually stepped
|
|
|
|
fn step_at_most(&mut self, at_most: u32) -> u32 {
|
|
|
|
assert!(at_most != 0);
|
|
|
|
let start_step = self.state.step_no();
|
|
|
|
if self.stimuli.should_apply(start_step) {
|
2021-08-21 06:25:00 +00:00
|
|
|
self.stimuli.real_time = self.state.time();
|
|
|
|
self.stimuli.time_step = self.state.timestep();
|
2022-01-03 20:48:51 +00:00
|
|
|
trace!("updating stimuli");
|
2020-12-08 06:51:00 +00:00
|
|
|
}
|
|
|
|
|
2021-08-08 07:37:13 +00:00
|
|
|
if self.renderer.any_work_for_frame(start_step) {
|
2020-12-09 02:44:56 +00:00
|
|
|
self.render();
|
|
|
|
}
|
|
|
|
|
2022-05-04 02:50:32 +00:00
|
|
|
let mut can_step = 1;
|
2021-08-23 06:01:11 +00:00
|
|
|
while can_step < at_most && !self.renderer.any_work_for_frame(start_step + can_step as u64) {
|
|
|
|
can_step += 1;
|
|
|
|
}
|
2020-12-08 06:51:00 +00:00
|
|
|
trace!("step begin");
|
2022-08-12 01:41:41 +00:00
|
|
|
self.diag.instrument_step(can_step as u64, || {
|
|
|
|
self.state.step_multiple(can_step, &self.stimuli);
|
|
|
|
});
|
2020-12-08 06:51:00 +00:00
|
|
|
trace!("step end");
|
2022-08-12 01:38:36 +00:00
|
|
|
if self.last_diag_time.elapsed().as_secs_f64() >= 5.0 {
|
2022-08-12 01:27:30 +00:00
|
|
|
// TODO: make this a method on the Diagnostics.
|
2022-08-12 01:38:36 +00:00
|
|
|
self.last_diag_time = Instant::now();
|
2020-12-08 06:51:00 +00:00
|
|
|
let step = self.state.step_no();
|
2022-08-12 01:36:35 +00:00
|
|
|
let diagstr = self.diag.format();
|
2020-12-08 06:51:00 +00:00
|
|
|
let sim_time = self.state.time() as f64;
|
2022-01-03 20:48:51 +00:00
|
|
|
let percent_complete = match self.sim_end_time {
|
2022-01-12 02:23:07 +00:00
|
|
|
Some(t) => format!("[{:.1}%] ", 100.0 * self.state.time() / *t.to_seconds(self.timestep())),
|
2022-01-03 20:48:51 +00:00
|
|
|
None => "".to_owned(),
|
|
|
|
};
|
2020-12-08 06:51:00 +00:00
|
|
|
info!(
|
2022-08-12 01:36:35 +00:00
|
|
|
"{}t={:.2e} frame {:06} {}",
|
|
|
|
percent_complete, sim_time, step, diagstr
|
2020-12-08 06:51:00 +00:00
|
|
|
);
|
|
|
|
}
|
2021-08-08 07:37:13 +00:00
|
|
|
can_step as u32
|
|
|
|
}
|
|
|
|
pub fn step_multiple(&mut self, num_steps: u32) {
|
|
|
|
let mut steps_remaining = num_steps;
|
|
|
|
while steps_remaining != 0 {
|
|
|
|
steps_remaining -= self.step_at_most(steps_remaining);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn step(&mut self) {
|
|
|
|
self.step_multiple(1);
|
2020-12-08 06:51:00 +00:00
|
|
|
}
|
2020-11-30 02:25:34 +00:00
|
|
|
|
2022-02-02 02:31:47 +00:00
|
|
|
/// Returns the number of timesteps needed to reach the end time
|
|
|
|
pub fn steps_until<T: Time>(&mut self, sim_end_time: T) -> u64 {
|
|
|
|
let sim_end_step = sim_end_time.to_frame(self.state.timestep());
|
|
|
|
let start_step = self.state.step_no();
|
|
|
|
sim_end_step.saturating_sub(start_step)
|
|
|
|
}
|
|
|
|
|
2022-01-12 02:23:07 +00:00
|
|
|
pub fn step_until<T: Time>(&mut self, sim_end_time: T) {
|
|
|
|
let sim_end_time = sim_end_time.to_frame(self.state.timestep());
|
2022-01-03 20:48:51 +00:00
|
|
|
self.sim_end_time = Some(sim_end_time);
|
2022-04-29 06:05:32 +00:00
|
|
|
let mut stepped = false;
|
2022-07-29 02:19:57 +00:00
|
|
|
while self.state.step_no() < *sim_end_time {
|
2021-08-21 06:25:00 +00:00
|
|
|
self.step_multiple(100);
|
2022-04-29 06:05:32 +00:00
|
|
|
stepped = true;
|
|
|
|
}
|
|
|
|
if stepped {
|
|
|
|
// render the final frame -- unless we already *have*
|
|
|
|
self.render();
|
2020-12-08 06:51:00 +00:00
|
|
|
}
|
2020-12-16 21:53:39 +00:00
|
|
|
self.render_pool.join();
|
2022-01-03 20:48:51 +00:00
|
|
|
self.sim_end_time = None;
|
2020-12-08 06:51:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:32:29 +00:00
|
|
|
impl<S: AbstractSim> Driver<S> {
|
2022-01-29 03:24:36 +00:00
|
|
|
pub fn add_classical_boundary<C: Coord>(&mut self, thickness: C)
|
|
|
|
where S::Material: From<mat::IsomorphicConductor<f32>>
|
2022-05-02 01:49:07 +00:00
|
|
|
{
|
|
|
|
self.add_classical_boundary_explicit::<f32, _>(thickness)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// the CPU code is parameterized over `Real`: you'll need to use this interface to get access
|
|
|
|
/// to that, if using a CPU driver. otherwise, use `add_classical_boundary`
|
|
|
|
pub fn add_classical_boundary_explicit<R: Real, C: Coord>(&mut self, thickness: C)
|
|
|
|
where S::Material: From<mat::IsomorphicConductor<R>>
|
2021-08-16 00:59:17 +00:00
|
|
|
{
|
2021-06-03 23:45:59 +00:00
|
|
|
let timestep = self.state.timestep();
|
|
|
|
self.state.fill_boundary_using(thickness, |boundary_ness| {
|
2021-06-08 01:25:11 +00:00
|
|
|
let b = boundary_ness.elem_pow(3.0);
|
|
|
|
let cond = b * (0.5 / timestep);
|
2020-09-20 03:10:10 +00:00
|
|
|
|
2021-06-03 23:45:59 +00:00
|
|
|
let iso_cond = cond.x() + cond.y() + cond.z();
|
2022-05-02 01:49:07 +00:00
|
|
|
let iso_conductor = mat::IsomorphicConductor::new(iso_cond.cast());
|
2021-08-16 00:46:50 +00:00
|
|
|
iso_conductor
|
2021-06-03 23:45:59 +00:00
|
|
|
});
|
2020-09-20 03:02:11 +00:00
|
|
|
}
|
2020-09-06 18:24:48 +00:00
|
|
|
}
|
2020-12-18 03:23:42 +00:00
|
|
|
|
|
|
|
/// Adapts the stimuli to be applied only every so often, to improve perf
|
|
|
|
struct StimuliAdapter {
|
|
|
|
stim: Vec<Box<dyn AbstractStimulus>>,
|
|
|
|
/// How many frames to go between applications of the stimulus.
|
|
|
|
frame_interval: u64,
|
2021-08-21 06:25:00 +00:00
|
|
|
real_time: f32,
|
|
|
|
time_step: f32,
|
2020-12-18 03:23:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl AbstractStimulus for StimuliAdapter {
|
2022-07-31 00:17:17 +00:00
|
|
|
fn at(&self, t_sec: f32, pos: Meters) -> stim::Fields {
|
2022-05-04 02:50:32 +00:00
|
|
|
self.stim.at(t_sec, pos)
|
2021-08-23 06:01:11 +00:00
|
|
|
// TODO: remove this stuff (here only for testing)
|
2022-05-04 02:50:32 +00:00
|
|
|
/*
|
2021-08-24 06:12:03 +00:00
|
|
|
if true {
|
|
|
|
// interpolation unaware (i.e. let the Sim backend do it)
|
|
|
|
} else if false {
|
2021-08-23 01:28:38 +00:00
|
|
|
// delta-fn "interpolation"
|
|
|
|
self.stim.at(t_sec, pos) * (self.frame_interval as f32)
|
2021-08-24 06:12:03 +00:00
|
|
|
} else if false {
|
2021-08-23 01:28:38 +00:00
|
|
|
// step-fn "interpolation"
|
|
|
|
self.stim.at(self.real_time, pos)
|
|
|
|
} else {
|
|
|
|
// linear interpolation
|
|
|
|
let interp_width = self.frame_interval as f32 * self.time_step;
|
|
|
|
let prev = self.stim.at(self.real_time, pos);
|
|
|
|
let next = self.stim.at(self.real_time + interp_width, pos);
|
|
|
|
let interp = (t_sec - self.real_time) / interp_width;
|
|
|
|
prev * (1.0 - interp) + next * interp
|
|
|
|
}
|
2022-05-04 02:50:32 +00:00
|
|
|
*/
|
2020-12-18 03:23:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StimuliAdapter {
|
|
|
|
fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
stim: Default::default(),
|
|
|
|
frame_interval: 1,
|
2021-08-21 06:25:00 +00:00
|
|
|
real_time: 0.0,
|
|
|
|
time_step: 0.0,
|
2020-12-18 03:23:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
fn should_apply(&self, frame: u64) -> bool {
|
2021-08-08 07:37:13 +00:00
|
|
|
(frame % self.frame_interval == 0) && self.stim.len() != 0
|
2020-12-18 03:23:42 +00:00
|
|
|
}
|
|
|
|
fn push(&mut self, s: Box<dyn AbstractStimulus>) {
|
|
|
|
self.stim.push(s)
|
|
|
|
}
|
|
|
|
}
|