From d619383fb91e7ee0e18f2575911111fd1c9d9d78 Mon Sep 17 00:00:00 2001 From: Colin Date: Sun, 6 Dec 2020 19:54:02 -0800 Subject: [PATCH] Make Current measurement serializable Note that this REALLY slows it down, since it forces the Region to be Boxed. It might make sense at some point to consider introducing a RegionBitmap type. Maybe keep the original Region type -- for serializing -- but cache a Bitmap version where perf matters more. --- examples/wrapped_torus.rs | 14 +++++----- src/geom/mod.rs | 8 +++++- src/meas.rs | 57 ++++++++++++++++++++++++++++++--------- src/render.rs | 2 ++ 4 files changed, 62 insertions(+), 19 deletions(-) diff --git a/examples/wrapped_torus.rs b/examples/wrapped_torus.rs index ab56ec8..75c9d05 100644 --- a/examples/wrapped_torus.rs +++ b/examples/wrapped_torus.rs @@ -14,9 +14,9 @@ fn main() { let ferro_minor = 40e-6; let wire_minor = 20e-6; let wire_major = 80e-6; - let peak_current = 2e3; + let peak_current = 2e4; let current_duration = 1e-9; // half-wavelength of the sine wave - let conductivity = 1.0e5; + let conductivity = 1.0e9; let from_m = |m| (m/feat_size) as u32; let m_to_um = |px| (px * 1e6) as u32; @@ -29,10 +29,10 @@ fn main() { let size_px = Index((width_px, width_px, depth_px).into()); let mut driver = Driver::new(size_px, feat_size); driver.set_steps_per_frame(120); - let base = "wrapped_torus-2"; + let base = "wrapped_torus-3"; let ferro_region = Torus::new_xy(Meters((half_width, half_width, half_depth).into()), ferro_major, ferro_minor); - let drive_region = Torus::new_xy(Meters((half_width - ferro_major, half_width, half_depth).into()), wire_major, wire_minor); - let sense_region = Torus::new_xy(Meters((half_width + ferro_major, half_width, half_depth).into()), wire_major, wire_minor); + let drive_region = Torus::new_xz(Meters((half_width - ferro_major, half_width, half_depth).into()), wire_major, wire_minor); + let sense_region = Torus::new_xz(Meters((half_width + ferro_major, half_width, half_depth).into()), wire_major, wire_minor); driver.fill_region(&ferro_region, mat::db::ferroxcube_3r1()); driver.fill_region(&drive_region, mat::Static::conductor(conductivity).into()); @@ -45,10 +45,12 @@ fn main() { driver.add_stimulus(Stimulus::new( Sphere::new( - Meters((half_width - wire_major, half_width, half_depth).into()), + Meters((half_width - ferro_major + wire_major, half_width, half_depth).into()), wire_minor), Sinusoid::from_wavelength(Vec3::new(0.0, 0.0, peak_current), current_duration * 2.0) )); + driver.add_measurement(meas::Current::new("sense", sense_region.clone())); + driver.add_measurement(meas::Current::new("drive", drive_region.clone())); // TODO: simulate current in the drive element //driver.add_stimulus(Stimulus::new( // conductor_region.clone(), diff --git a/src/geom/mod.rs b/src/geom/mod.rs index edccbb1..6cd8f16 100644 --- a/src/geom/mod.rs +++ b/src/geom/mod.rs @@ -7,6 +7,7 @@ pub use vec::{Vec2, Vec3}; pub use vecu::Vec3u; use crate::flt::{Flt, Real}; +use dyn_clone::{self, DynClone}; use serde::{Serialize, Deserialize}; use std::fmt::{self, Display}; use std::ops::Add; @@ -182,9 +183,11 @@ impl Polygon2d { } } -pub trait Region { +#[typetag::serde(tag = "type")] +pub trait Region: Send + DynClone { fn contains(&self, p: Meters) -> bool; } +dyn_clone::clone_trait_object!(Region); #[derive(Copy, Clone, Serialize, Deserialize)] pub struct CylinderZ { @@ -202,6 +205,7 @@ impl CylinderZ { } } +#[typetag::serde] impl Region for CylinderZ { fn contains(&self, p: Meters) -> bool { p.xy().distance_sq(self.center) <= (self.radius * self.radius).into() @@ -242,6 +246,7 @@ impl Torus { } } +#[typetag::serde] impl Region for Torus { fn contains(&self, p: Meters) -> bool { // a torus is the set of all points < distance `r` from the circle of radius `R`, @@ -273,6 +278,7 @@ impl Sphere { } } +#[typetag::serde] impl Region for Sphere { fn contains(&self, p: Meters) -> bool { (*p - *self.center).mag() < self.rad.into_inner() diff --git a/src/meas.rs b/src/meas.rs index b09702d..bc40d63 100644 --- a/src/meas.rs +++ b/src/meas.rs @@ -1,12 +1,13 @@ use crate::flt::Flt; -use crate::geom::{Meters, Region}; +use crate::geom::{Meters, Region, Vec3}; use crate::mat::Material as _; use crate::sim::{Cell, GenericSim}; use dyn_clone::{self, DynClone}; use serde::{Serialize, Deserialize}; -use std::fmt::Display; use std::iter::Sum; +// TODO: remove this Clone and Send requirement? Have Measurements be shared by-reference across +// threads? i.e. Sync, and no Clone #[typetag::serde(tag = "type")] pub trait AbstractMeasurement: Send + DynClone { fn eval(&self, state: &dyn GenericSim) -> String; @@ -49,8 +50,7 @@ impl AbstractMeasurement for Label { } } -fn sum_over_region, R: Region, F: Fn(Meters, &Cell) -> T>(state: &dyn GenericSim, r: &R, f: F) -> T { - // TODO: z coordinate? +fn sum_over_region, F: Fn(Meters, &Cell) -> T>(state: &dyn GenericSim, r: &dyn Region, f: F) -> T { state.map_sum_enumerated(|coord, cell| { if r.contains(coord) { f(coord, cell) @@ -61,16 +61,49 @@ fn sum_over_region, R: Region, F: Fn(Meters, &Cell) -> T>(st } #[derive(Clone, Serialize, Deserialize)] -pub struct Current(pub R); +pub struct Current { + name: String, + region: Box, +} -#[typetag::serialize] -impl> AbstractMeasurement for Current { - fn eval(&self, state: &dyn GenericSim) -> String { - let current = sum_over_region(state, &self.0, |coord, _cell| state.current(coord)); - format!("I({}): ({:.2e}, {:.2e}, {:.2e})", self.0, current.x(), current.y(), current.z()) +impl Current { + pub fn new(name: &str, r: R) -> Self { + Self { + name: name.into(), + region: Box::new(r) + } } - fn typetag_deserialize(&self) { - todo!() +} + +#[derive(Default)] +struct CurrentSample(usize, Flt, Vec3); +impl std::iter::Sum for CurrentSample { + fn sum(iter: I) -> Self + where I: Iterator + { + let mut s = CurrentSample::default(); + for CurrentSample(a, b, c) in iter { + s = CurrentSample(s.0 + a, s.1 + b, s.2 + c) + } + s + } +} + +#[typetag::serde] +impl AbstractMeasurement for Current { + fn eval(&self, state: &dyn GenericSim) -> String { + let CurrentSample(volume, current_mag, current_new) = sum_over_region(state, &*self.region, |coord, _cell| { + let current = state.current(coord); + CurrentSample(1, current.mag(), current) + }); + let mean_current_mag = current_mag / (volume as Flt); + let mean_current_vec = current_new / (volume as Flt); + format!("I({}): {:.2e}, avg ({:.2e}, {:.2e}, {:.2e})", + self.name, + mean_current_mag, + mean_current_vec.x(), + mean_current_vec.y(), + mean_current_vec.z()) } } diff --git a/src/render.rs b/src/render.rs index d4eb97e..498b606 100644 --- a/src/render.rs +++ b/src/render.rs @@ -304,6 +304,8 @@ impl Renderer for ColorTermRenderer { stdout.queue(PrintStyledContent(style(format!("z: {}", z)))).unwrap(); for m in measurements { + // Measurements can be slow to compute + stdout.flush().unwrap(); let meas_string = m.eval(state); stdout.queue(cursor::MoveDown(1)).unwrap(); stdout.queue(cursor::MoveToColumn(1)).unwrap();