2021-06-07 22:20:02 +00:00
|
|
|
use crate::geom::{Coord, Meters, Region, Vec3};
|
2021-06-09 22:46:45 +00:00
|
|
|
use crate::mat::{self, GenericMaterial, Material, Pml};
|
2020-09-08 03:14:41 +00:00
|
|
|
use crate::meas::{self, AbstractMeasurement};
|
2021-06-09 22:46:45 +00:00
|
|
|
use crate::real::{R32, Real};
|
2020-09-06 18:24:48 +00:00
|
|
|
use crate::render::{self, MultiRenderer, Renderer};
|
2020-10-03 20:58:44 +00:00
|
|
|
use crate::sim::{GenericSim, SimState};
|
2020-09-26 22:50:34 +00:00
|
|
|
use crate::stim::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
|
|
|
|
2021-06-09 22:46:45 +00:00
|
|
|
pub struct Driver<R=R32, M=GenericMaterial<R>> {
|
2021-06-09 22:58:38 +00:00
|
|
|
pub state: SimState<R, M>,
|
|
|
|
renderer: Arc<MultiRenderer<SimState<R, M>>>,
|
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<()>),
|
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,
|
|
|
|
time_spent_rendering: Arc<Mutex<Duration>>,
|
2020-09-08 03:14:41 +00:00
|
|
|
measurements: Vec<Box<dyn AbstractMeasurement>>,
|
2020-12-18 03:23:42 +00:00
|
|
|
stimuli: StimuliAdapter,
|
2020-11-28 05:22:42 +00:00
|
|
|
start_time: Instant,
|
|
|
|
last_diag_time: Instant,
|
2020-09-06 18:24:48 +00:00
|
|
|
}
|
|
|
|
|
2021-06-09 22:46:45 +00:00
|
|
|
impl<R: Real, M: Default> Driver<R, M> {
|
2021-06-08 01:25:11 +00:00
|
|
|
pub fn new<C: Coord>(size: C, feature_size: f32) -> Self {
|
2020-09-06 18:24:48 +00:00
|
|
|
Driver {
|
2020-09-27 00:05:15 +00:00
|
|
|
state: SimState::new(size.to_index(feature_size), feature_size),
|
2020-11-28 05:22:42 +00:00
|
|
|
renderer: Arc::new(MultiRenderer::new()),
|
|
|
|
render_pool: ThreadPool::new(3),
|
|
|
|
render_channel: sync_channel(0),
|
2020-09-19 05:22:54 +00:00
|
|
|
time_spent_stepping: Default::default(),
|
2020-09-26 22:50:34 +00:00
|
|
|
time_spent_on_stimuli: Default::default(),
|
2020-12-18 06:22:25 +00:00
|
|
|
time_spent_prepping_render: Default::default(),
|
2020-11-28 05:22:42 +00:00
|
|
|
time_spent_blocked_on_render: Default::default(),
|
2020-09-20 02:33:11 +00:00
|
|
|
time_spent_rendering: Default::default(),
|
2020-12-18 04:07:25 +00:00
|
|
|
measurements: vec![
|
|
|
|
Box::new(meas::Time),
|
|
|
|
Box::new(meas::Meta),
|
2020-12-20 00:45:55 +00:00
|
|
|
Box::new(meas::Energy::world()),
|
2020-12-18 21:48:35 +00:00
|
|
|
Box::new(meas::Power::world()),
|
2020-12-18 04:07:25 +00:00
|
|
|
],
|
2020-12-18 03:23:42 +00:00
|
|
|
stimuli: StimuliAdapter::new(),
|
2020-11-28 05:22:42 +00:00
|
|
|
start_time: Instant::now(),
|
|
|
|
last_diag_time: Instant::now(),
|
2020-09-06 18:24:48 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-08 06:51:00 +00:00
|
|
|
}
|
2020-09-06 18:24:48 +00:00
|
|
|
|
2021-06-09 22:46:45 +00:00
|
|
|
impl<R: Real, M: Material<R>> Driver<R, M> {
|
2020-09-26 22:50:34 +00:00
|
|
|
pub fn add_stimulus<S: AbstractStimulus + 'static>(&mut self, s: S) {
|
|
|
|
self.stimuli.push(Box::new(s))
|
|
|
|
}
|
|
|
|
|
2020-12-08 06:51:00 +00:00
|
|
|
pub fn add_measurement<Meas: AbstractMeasurement + 'static>(&mut self, m: Meas) {
|
2020-09-08 03:14:41 +00:00
|
|
|
self.measurements.push(Box::new(m));
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-06-09 22:46:45 +00:00
|
|
|
impl<R: Real, M: Material<R> + Clone> Driver<R, M> {
|
2021-06-08 22:37:50 +00:00
|
|
|
pub fn fill_region<Reg: Region>(&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
|
|
|
}
|
|
|
|
}
|
2020-09-06 18:24:48 +00:00
|
|
|
|
2021-06-15 03:46:22 +00:00
|
|
|
impl<R: Real, M: Material<R> + Send + Sync + 'static> Driver<R, M> {
|
2020-12-17 07:23:54 +00:00
|
|
|
pub fn dyn_state(&mut self) -> &mut dyn GenericSim {
|
|
|
|
&mut self.state
|
|
|
|
}
|
|
|
|
|
2021-06-09 22:58:38 +00:00
|
|
|
fn add_renderer<Rend: Renderer<SimState<R, M>> + 'static>(
|
2021-06-15 03:09:52 +00:00
|
|
|
&mut self, renderer: Rend, name: &str, step_frequency: u64
|
2021-06-08 22:37:50 +00:00
|
|
|
) {
|
2021-06-15 03:09:52 +00:00
|
|
|
info!("render to {} at f={}", name, step_frequency);
|
|
|
|
self.renderer.push(renderer, step_frequency);
|
2020-09-06 18:24:48 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 03:09:52 +00:00
|
|
|
pub fn add_y4m_renderer<S: Into<PathBuf>>(&mut self, output: S, step_frequency: u64) {
|
2020-09-13 23:29:18 +00:00
|
|
|
let output = output.into();
|
|
|
|
let name = output.to_string_lossy().into_owned();
|
2021-06-15 03:09:52 +00:00
|
|
|
self.add_renderer(render::Y4MRenderer::new(output), &*name, step_frequency);
|
2020-09-06 18:24:48 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 03:09:52 +00:00
|
|
|
pub fn add_term_renderer(&mut self, step_frequency: u64) {
|
|
|
|
self.add_renderer(render::ColorTermRenderer, "terminal", step_frequency);
|
2020-09-06 18:24:48 +00:00
|
|
|
}
|
2021-06-15 07:47:59 +00:00
|
|
|
|
|
|
|
pub fn add_csv_renderer(&mut self, path: &str, step_frequency: u64) {
|
|
|
|
self.add_renderer(render::CsvRenderer::new(path), path, step_frequency);
|
|
|
|
}
|
2020-12-17 07:59:57 +00:00
|
|
|
}
|
2020-09-06 18:24:48 +00:00
|
|
|
|
2021-06-15 03:46:22 +00:00
|
|
|
impl<R: Real + Send + Sync + Serialize, M: Material<R> + Serialize + Send + Sync + 'static> Driver<R, M> {
|
2021-06-15 03:09:52 +00:00
|
|
|
pub fn add_serializer_renderer(&mut self, out_base: &str, step_frequency: u64) {
|
2021-06-15 03:46:22 +00:00
|
|
|
let fmt_str = format!("{out_base}{{step_no}}.bc", out_base=out_base);
|
2021-06-15 04:04:08 +00:00
|
|
|
self.add_renderer(render::SerializerRenderer::new_static(&*fmt_str), &*fmt_str, step_frequency);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<R, M> Driver<R, M>
|
|
|
|
where
|
|
|
|
R: Real + Send + Sync + Serialize + for<'a> Deserialize<'a>,
|
|
|
|
M: Material<R> + Serialize + for<'a> Deserialize<'a> + Send + Sync + 'static,
|
|
|
|
{
|
|
|
|
pub fn add_state_file(&mut self, state_file: &str, snapshot_frequency: u64) {
|
|
|
|
let ser = render::SerializerRenderer::new(state_file);
|
|
|
|
if let Some(state) = ser.try_load() {
|
|
|
|
self.state = state.state;
|
|
|
|
}
|
|
|
|
self.add_renderer(ser, state_file, snapshot_frequency);
|
2020-11-28 06:11:53 +00:00
|
|
|
}
|
2020-12-08 06:51:00 +00:00
|
|
|
}
|
2020-11-28 06:11:53 +00:00
|
|
|
|
2021-06-09 22:46:45 +00:00
|
|
|
impl<R: Real + Send + Sync + 'static, M: Material<R> + Clone + Default + Send + Sync + 'static> Driver<R, M> {
|
2020-12-08 06:51:00 +00:00
|
|
|
fn render(&mut self) {
|
2020-12-18 06:22:25 +00:00
|
|
|
let prep_start = Instant::now();
|
2020-12-08 06:51:00 +00:00
|
|
|
let their_state = self.state.clone();
|
|
|
|
let their_measurements = self.measurements.clone();
|
|
|
|
let renderer = self.renderer.clone();
|
|
|
|
let time_spent_rendering = self.time_spent_rendering.clone();
|
|
|
|
let sender = self.render_channel.0.clone();
|
|
|
|
self.render_pool.execute(move || {
|
2020-12-16 21:53:39 +00:00
|
|
|
// unblock the main thread (this limits the number of renders in-flight at any time
|
2020-12-08 06:51:00 +00:00
|
|
|
sender.send(()).unwrap();
|
|
|
|
trace!("render begin");
|
|
|
|
let start_time = Instant::now();
|
2021-06-04 03:42:10 +00:00
|
|
|
renderer.render(&their_state, &*their_measurements, Default::default());
|
2020-12-08 06:51:00 +00:00
|
|
|
*time_spent_rendering.lock().unwrap() += start_time.elapsed();
|
|
|
|
trace!("render end");
|
|
|
|
});
|
2020-12-18 06:22:25 +00:00
|
|
|
self.time_spent_prepping_render += prep_start.elapsed();
|
2020-12-08 06:51:00 +00:00
|
|
|
let block_start = Instant::now();
|
|
|
|
self.render_channel.1.recv().unwrap();
|
|
|
|
self.time_spent_blocked_on_render += block_start.elapsed();
|
|
|
|
}
|
|
|
|
pub fn step(&mut self) {
|
2020-12-18 03:23:42 +00:00
|
|
|
if self.stimuli.should_apply(self.state.step_no()) {
|
2020-12-08 06:51:00 +00:00
|
|
|
trace!("stimuli begin");
|
|
|
|
let start_time = Instant::now();
|
|
|
|
self.state.apply_stimulus(&self.stimuli);
|
|
|
|
self.time_spent_on_stimuli += start_time.elapsed();
|
2020-12-18 03:23:42 +00:00
|
|
|
trace!("stimuli end");
|
2020-12-08 06:51:00 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 03:09:52 +00:00
|
|
|
if self.renderer.any_work_for_frame(self.state.step_no()) {
|
2020-12-09 02:44:56 +00:00
|
|
|
self.render();
|
|
|
|
}
|
|
|
|
|
2020-12-08 06:51:00 +00:00
|
|
|
trace!("step begin");
|
|
|
|
let start_time = Instant::now();
|
|
|
|
self.state.step();
|
|
|
|
self.time_spent_stepping += start_time.elapsed();
|
|
|
|
trace!("step end");
|
|
|
|
if self.last_diag_time.elapsed().as_secs_f64() >= 5.0 {
|
|
|
|
self.last_diag_time = Instant::now();
|
|
|
|
let step = self.state.step_no();
|
|
|
|
let step_time = self.time_spent_stepping.as_secs_f64();
|
|
|
|
let stim_time = self.time_spent_on_stimuli.as_secs_f64();
|
|
|
|
let render_time = self.time_spent_rendering.lock().unwrap().as_secs_f64();
|
2020-12-18 06:22:25 +00:00
|
|
|
let render_prep_time = self.time_spent_prepping_render.as_secs_f64();
|
2020-12-08 06:51:00 +00:00
|
|
|
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.state.step_no() as f64) / overall_time;
|
|
|
|
let sim_time = self.state.time() as f64;
|
|
|
|
info!(
|
2020-12-18 06:22:25 +00:00
|
|
|
"t={:.2e} frame {:06} fps: {:6.2} (sim: {:.1}s, stim: {:.1}s, [render: {:.1}s], blocked: {:.1}s, render_prep: {:.1}s, other: {:.1}s)",
|
2020-12-08 06:51:00 +00:00
|
|
|
sim_time,
|
|
|
|
step,
|
|
|
|
fps,
|
|
|
|
step_time,
|
|
|
|
stim_time,
|
|
|
|
render_time,
|
|
|
|
block_time,
|
2020-12-18 06:22:25 +00:00
|
|
|
render_prep_time,
|
|
|
|
overall_time - step_time - stim_time - block_time - render_prep_time,
|
2020-12-08 06:51:00 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2020-11-30 02:25:34 +00:00
|
|
|
|
2021-06-08 01:25:11 +00:00
|
|
|
pub fn step_until(&mut self, deadline: f32) {
|
2020-12-08 06:51:00 +00:00
|
|
|
while self.dyn_state().time() < deadline {
|
|
|
|
self.step();
|
|
|
|
}
|
2020-12-09 02:44:56 +00:00
|
|
|
// render the final frame
|
|
|
|
self.render();
|
2020-12-16 21:53:39 +00:00
|
|
|
self.render_pool.join();
|
2020-12-08 06:51:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-09 22:46:45 +00:00
|
|
|
impl<R: Real, M: Material<R> + From<mat::Pml<R>>> Driver<R, M> {
|
2021-04-24 23:02:19 +00:00
|
|
|
pub fn add_pml_boundary<C: Coord>(&mut self, thickness: C) {
|
2021-05-31 03:20:27 +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 conductivity = b * (0.5 / timestep);
|
2021-06-08 22:37:50 +00:00
|
|
|
Pml::new(conductivity).into()
|
2021-05-31 03:20:27 +00:00
|
|
|
});
|
2020-12-19 22:55:55 +00:00
|
|
|
}
|
2021-05-31 03:20:27 +00:00
|
|
|
}
|
|
|
|
|
2021-06-09 22:46:45 +00:00
|
|
|
impl<R: Real, M: Material<R> + From<mat::Conductor<R>>> Driver<R, M> {
|
2020-12-19 22:55:55 +00:00
|
|
|
pub fn add_classical_boundary<C: Coord>(&mut self, thickness: C) {
|
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();
|
2021-06-09 22:46:45 +00:00
|
|
|
let iso_conductor = mat::db::conductor(iso_cond);
|
2021-06-03 23:45:59 +00:00
|
|
|
iso_conductor.into()
|
|
|
|
});
|
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,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AbstractStimulus for StimuliAdapter {
|
2021-06-08 01:36:15 +00:00
|
|
|
fn at(&self, t_sec: f32, pos: Meters) -> Vec3<f32> {
|
|
|
|
self.stim.at(t_sec, pos) * (self.frame_interval as f32)
|
2020-12-18 03:23:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StimuliAdapter {
|
|
|
|
fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
stim: Default::default(),
|
|
|
|
frame_interval: 1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn should_apply(&self, frame: u64) -> bool {
|
|
|
|
frame % self.frame_interval == 0
|
|
|
|
}
|
|
|
|
fn push(&mut self, s: Box<dyn AbstractStimulus>) {
|
|
|
|
self.stim.push(s)
|
|
|
|
}
|
|
|
|
}
|