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.
This commit is contained in:
2020-12-06 19:54:02 -08:00
parent fa23989cd9
commit d619383fb9
4 changed files with 62 additions and 19 deletions

View File

@@ -14,9 +14,9 @@ fn main() {
let ferro_minor = 40e-6; let ferro_minor = 40e-6;
let wire_minor = 20e-6; let wire_minor = 20e-6;
let wire_major = 80e-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 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 from_m = |m| (m/feat_size) as u32;
let m_to_um = |px| (px * 1e6) 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 size_px = Index((width_px, width_px, depth_px).into());
let mut driver = Driver::new(size_px, feat_size); let mut driver = Driver::new(size_px, feat_size);
driver.set_steps_per_frame(120); 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 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 drive_region = Torus::new_xz(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 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(&ferro_region, mat::db::ferroxcube_3r1());
driver.fill_region(&drive_region, mat::Static::conductor(conductivity).into()); driver.fill_region(&drive_region, mat::Static::conductor(conductivity).into());
@@ -45,10 +45,12 @@ fn main() {
driver.add_stimulus(Stimulus::new( driver.add_stimulus(Stimulus::new(
Sphere::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), wire_minor),
Sinusoid::from_wavelength(Vec3::new(0.0, 0.0, peak_current), current_duration * 2.0) 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 // TODO: simulate current in the drive element
//driver.add_stimulus(Stimulus::new( //driver.add_stimulus(Stimulus::new(
// conductor_region.clone(), // conductor_region.clone(),

View File

@@ -7,6 +7,7 @@ pub use vec::{Vec2, Vec3};
pub use vecu::Vec3u; pub use vecu::Vec3u;
use crate::flt::{Flt, Real}; use crate::flt::{Flt, Real};
use dyn_clone::{self, DynClone};
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::ops::Add; 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; fn contains(&self, p: Meters) -> bool;
} }
dyn_clone::clone_trait_object!(Region);
#[derive(Copy, Clone, Serialize, Deserialize)] #[derive(Copy, Clone, Serialize, Deserialize)]
pub struct CylinderZ { pub struct CylinderZ {
@@ -202,6 +205,7 @@ impl CylinderZ {
} }
} }
#[typetag::serde]
impl Region for CylinderZ { impl Region for CylinderZ {
fn contains(&self, p: Meters) -> bool { fn contains(&self, p: Meters) -> bool {
p.xy().distance_sq(self.center) <= (self.radius * self.radius).into() p.xy().distance_sq(self.center) <= (self.radius * self.radius).into()
@@ -242,6 +246,7 @@ impl Torus {
} }
} }
#[typetag::serde]
impl Region for Torus { impl Region for Torus {
fn contains(&self, p: Meters) -> bool { fn contains(&self, p: Meters) -> bool {
// a torus is the set of all points < distance `r` from the circle of radius `R`, // 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 { impl Region for Sphere {
fn contains(&self, p: Meters) -> bool { fn contains(&self, p: Meters) -> bool {
(*p - *self.center).mag() < self.rad.into_inner() (*p - *self.center).mag() < self.rad.into_inner()

View File

@@ -1,12 +1,13 @@
use crate::flt::Flt; use crate::flt::Flt;
use crate::geom::{Meters, Region}; use crate::geom::{Meters, Region, Vec3};
use crate::mat::Material as _; use crate::mat::Material as _;
use crate::sim::{Cell, GenericSim}; use crate::sim::{Cell, GenericSim};
use dyn_clone::{self, DynClone}; use dyn_clone::{self, DynClone};
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use std::fmt::Display;
use std::iter::Sum; 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")] #[typetag::serde(tag = "type")]
pub trait AbstractMeasurement: Send + DynClone { pub trait AbstractMeasurement: Send + DynClone {
fn eval(&self, state: &dyn GenericSim) -> String; fn eval(&self, state: &dyn GenericSim) -> String;
@@ -49,8 +50,7 @@ impl AbstractMeasurement for Label {
} }
} }
fn sum_over_region<T: Default + Sum<T>, R: Region, F: Fn(Meters, &Cell) -> T>(state: &dyn GenericSim, r: &R, f: F) -> T { fn sum_over_region<T: Default + Sum<T>, F: Fn(Meters, &Cell) -> T>(state: &dyn GenericSim, r: &dyn Region, f: F) -> T {
// TODO: z coordinate?
state.map_sum_enumerated(|coord, cell| { state.map_sum_enumerated(|coord, cell| {
if r.contains(coord) { if r.contains(coord) {
f(coord, cell) f(coord, cell)
@@ -61,16 +61,49 @@ fn sum_over_region<T: Default + Sum<T>, R: Region, F: Fn(Meters, &Cell) -> T>(st
} }
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
pub struct Current<R>(pub R); pub struct Current {
name: String,
region: Box<dyn Region>,
}
#[typetag::serialize] impl Current {
impl<R: Region + Clone + Display + Send + Sync + Serialize + Deserialize<'static>> AbstractMeasurement for Current<R> { pub fn new<R: Region + 'static>(name: &str, r: R) -> Self {
fn eval(&self, state: &dyn GenericSim) -> String { Self {
let current = sum_over_region(state, &self.0, |coord, _cell| state.current(coord)); name: name.into(),
format!("I({}): ({:.2e}, {:.2e}, {:.2e})", self.0, current.x(), current.y(), current.z()) region: Box::new(r)
}
} }
fn typetag_deserialize(&self) { }
todo!()
#[derive(Default)]
struct CurrentSample(usize, Flt, Vec3);
impl std::iter::Sum for CurrentSample {
fn sum<I>(iter: I) -> Self
where I: Iterator<Item = Self>
{
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())
} }
} }

View File

@@ -304,6 +304,8 @@ impl Renderer for ColorTermRenderer {
stdout.queue(PrintStyledContent(style(format!("z: {}", z)))).unwrap(); stdout.queue(PrintStyledContent(style(format!("z: {}", z)))).unwrap();
for m in measurements { for m in measurements {
// Measurements can be slow to compute
stdout.flush().unwrap();
let meas_string = m.eval(state); let meas_string = m.eval(state);
stdout.queue(cursor::MoveDown(1)).unwrap(); stdout.queue(cursor::MoveDown(1)).unwrap();
stdout.queue(cursor::MoveToColumn(1)).unwrap(); stdout.queue(cursor::MoveToColumn(1)).unwrap();