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 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(),

View File

@@ -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()

View File

@@ -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<T: Default + Sum<T>, R: Region, F: Fn(Meters, &Cell) -> T>(state: &dyn GenericSim, r: &R, f: F) -> T {
// TODO: z coordinate?
fn sum_over_region<T: Default + Sum<T>, 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<T: Default + Sum<T>, R: Region, F: Fn(Meters, &Cell) -> T>(st
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Current<R>(pub R);
pub struct Current {
name: String,
region: Box<dyn Region>,
}
#[typetag::serialize]
impl<R: Region + Clone + Display + Send + Sync + Serialize + Deserialize<'static>> AbstractMeasurement for Current<R> {
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<R: Region + 'static>(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<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();
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();