diff --git a/src/driver.rs b/src/driver.rs index 1315fd6..fcc8b66 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -3,7 +3,7 @@ use crate::consts; use crate::coord::Coord; use crate::meas::{self, AbstractMeasurement}; use crate::render::{self, MultiRenderer, Renderer}; -use crate::sim::SimState; +use crate::sim::{GenericSim as _, SimState}; use crate::vec3::Vec3; use log::{info, trace}; diff --git a/src/meas.rs b/src/meas.rs index 473e016..df9ff34 100644 --- a/src/meas.rs +++ b/src/meas.rs @@ -1,18 +1,18 @@ use crate::coord::Coord; -use crate::geom::{Point, Region}; +use crate::geom::Region; use crate::mat::Material as _; -use crate::sim::{Cell, SimSnapshot}; +use crate::sim::GenericSim; use std::fmt::Display; -use std::iter::Sum; +//use std::iter::Sum; pub trait AbstractMeasurement { - fn eval(&self, state: &SimSnapshot) -> String; + fn eval(&self, state: &dyn GenericSim) -> String; } pub struct Time; impl AbstractMeasurement for Time { - fn eval(&self, state: &SimSnapshot) -> String { + fn eval(&self, state: &dyn GenericSim) -> String { format!("{:.3e}s (step {})", state.time(), state.step_no()) } } @@ -20,7 +20,7 @@ impl AbstractMeasurement for Time { pub struct Meta; impl AbstractMeasurement for Meta { - fn eval(&self, state: &SimSnapshot) -> String { + fn eval(&self, state: &dyn GenericSim) -> String { format!("{}x{} feat: {:.1e}m", state.width(), state.height(), state.feature_size()) } } @@ -34,25 +34,28 @@ impl Label { } impl AbstractMeasurement for Label { - fn eval(&self, _state: &SimSnapshot) -> String { + fn eval(&self, _state: &dyn GenericSim) -> String { self.0.clone() } } -fn sum_over_region, R: Region + Sync, F: Fn(Coord, &Cell) -> T + Sync>(state: &SimSnapshot, r: &R, f: F) -> T { - state.map_sum_enumerated(|coord, cell| if r.contains(Point::from(coord)*state.feature_size()) { - f(coord, cell) - } else { - Default::default() - }) -} +// fn sum_over_region, R: Region + Sync, F: Fn(Coord, &Cell) -> T + Sync>(state: &SimSnapshot, r: &R, f: F) -> T { +// state.map_sum_enumerated(|coord, cell| if r.contains(Point::from(coord)*state.feature_size()) { +// f(coord, cell) +// } else { +// Default::default() +// }) +// } pub struct Current(pub R); impl AbstractMeasurement for Current { - fn eval(&self, state: &SimSnapshot) -> 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()) + // fn eval(&self, state: &SimSnapshot) -> 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()) + // } + fn eval(&self, _state: &dyn GenericSim) -> String { + format!("I: TODO") } } @@ -60,7 +63,7 @@ impl AbstractMeasurement for Current { pub struct Magnetization(pub Coord); impl AbstractMeasurement for Magnetization { - fn eval(&self, state: &SimSnapshot) -> String { + fn eval(&self, state: &dyn GenericSim) -> String { let m = state.get(self.0).mat().m(); format!("M{}: ({:.2e}, {:.2e}, {:.2e})", self.0, m.x(), m.y(), m.z()) } @@ -70,7 +73,7 @@ impl AbstractMeasurement for Magnetization { pub struct MagneticFlux(pub Coord); impl AbstractMeasurement for MagneticFlux { - fn eval(&self, state: &SimSnapshot) -> String { + fn eval(&self, state: &dyn GenericSim) -> String { let b = state.get(self.0).b(); format!("B{}: ({:.2e}, {:.2e}, {:.2e})", self.0, b.x(), b.y(), b.z()) } @@ -80,7 +83,7 @@ impl AbstractMeasurement for MagneticFlux { pub struct MagneticStrength(pub Coord); impl AbstractMeasurement for MagneticStrength { - fn eval(&self, state: &SimSnapshot) -> String { + fn eval(&self, state: &dyn GenericSim) -> String { let h = state.get(self.0).h(); format!("H{}: ({:.2e}, {:.2e}, {:.2e})", self.0, h.x(), h.y(), h.z()) } @@ -89,7 +92,7 @@ impl AbstractMeasurement for MagneticStrength { pub struct ElectricField(pub Coord); impl AbstractMeasurement for ElectricField { - fn eval(&self, state: &SimSnapshot) -> String { + fn eval(&self, state: &dyn GenericSim) -> String { let e = state.get(self.0).e(); format!("E{}: ({:.2e}, {:.2e}, {:.2e})", self.0, e.x(), e.y(), e.z()) } @@ -98,21 +101,22 @@ impl AbstractMeasurement for ElectricField { pub struct Energy; impl AbstractMeasurement for Energy { - fn eval(&self, state: &SimSnapshot) -> String { + fn eval(&self, _state: &dyn GenericSim) -> String { // Potential energy stored in a E/M field: // https://en.wikipedia.org/wiki/Magnetic_energy // https://en.wikipedia.org/wiki/Electric_potential_energy#Energy_stored_in_an_electrostatic_field_distribution // U(B) = 1/2 \int H . B dV // U(E) = 1/2 \int E . D dV - let f = state.feature_size(); - #[allow(non_snake_case)] - let dV = f*f*f; - let e = state.map_sum(|cell| { - // E . D is perpetually 0 since we don't model D. - // All potential energy is in the magnetic field. - 0.5 * cell.h().dot(cell.b()) * dV - }); - format!("U: {:.2e}", e) + // let f = state.feature_size(); + // #[allow(non_snake_case)] + // let dV = f*f*f; + //let e = state.map_sum(|cell| { + // // E . D is perpetually 0 since we don't model D. + // // All potential energy is in the magnetic field. + // 0.5 * cell.h().dot(cell.b()) * dV + //}); + //format!("U: {:.2e}", e) + format!("U: TODO") } } diff --git a/src/render.rs b/src/render.rs index 46af665..c141af2 100644 --- a/src/render.rs +++ b/src/render.rs @@ -2,7 +2,8 @@ use ansi_term::Color::RGB; use crate::geom::Point; use crate::{flt::{Flt, Real}, Material as _, SimSnapshot, SimState}; use crate::mat; -use crate::sim::Cell; +use crate::sim::{Cell, GenericSim}; +use crate::vec3::Vec3; use crate::meas::AbstractMeasurement; use font8x8::{BASIC_FONTS, GREEK_FONTS, UnicodeFonts as _}; use log::trace; @@ -54,12 +55,12 @@ fn scale_vector(x: Point, typical_mag: Flt) -> Point { struct RenderSteps<'a> { im: RgbImage, - sim: &'a SimSnapshot, + sim: &'a dyn GenericSim, meas: &'a [Box], } impl<'a> RenderSteps<'a> { - fn render(state: &'a SimSnapshot, measurements: &'a [Box]) -> RgbImage { + fn render(state: &'a dyn GenericSim, measurements: &'a [Box]) -> RgbImage { let width = 768; let height = width * state.height() / state.width(); let mut me = Self::new(state, measurements, width, height); @@ -75,7 +76,7 @@ impl<'a> RenderSteps<'a> { me.render_measurements(); me.im } - fn new(sim: &'a SimSnapshot, meas: &'a [Box], width: u32, height: u32) -> Self { + fn new(sim: &'a dyn GenericSim, meas: &'a [Box], width: u32, height: u32) -> Self { RenderSteps { im: RgbImage::new(width, height), sim, @@ -83,10 +84,12 @@ impl<'a> RenderSteps<'a> { } } - fn get_at_px(&self, x_px: u32, y_px: u32) -> &Cell { - let x_sim = x_px * self.sim.width() / self.im.width(); - let y_sim = y_px * self.sim.height() / self.im.height(); - self.sim.get((x_sim, y_sim).into()) + fn get_at_px(&self, x_px: u32, y_px: u32) -> Cell { + let x_prop = x_px as Flt / self.im.width() as Flt; + let x_m = x_prop * (self.sim.width() as Flt * self.sim.feature_size()); + let y_prop = y_px as Flt / self.im.height() as Flt; + let y_m = y_prop * (self.sim.height() as Flt * self.sim.feature_size()); + self.sim.sample(Vec3::new(x_m, y_m, 0.0)) } ////////////// Ex/Ey/Bz configuration //////////// @@ -132,7 +135,7 @@ impl<'a> RenderSteps<'a> { for y in 0..h { for x in 0..w { let cell = self.get_at_px(x, y); - let value = measure(cell); + let value = measure(&cell); let scaled = if signed { scale_signed_to_u8(value, typical) } else { @@ -176,7 +179,7 @@ impl<'a> RenderSteps<'a> { let yend = (ystart + size).min(h); for y in ystart..yend { for x in xstart..xend { - field += measure(self.get_at_px(x, y)); + field += measure(&self.get_at_px(x, y)); } } let xw = xend - xstart; diff --git a/src/sim.rs b/src/sim.rs index b42939a..968098a 100644 --- a/src/sim.rs +++ b/src/sim.rs @@ -11,6 +11,20 @@ use std::iter::Sum; pub type SimSnapshot = SimState; +pub trait GenericSim { + fn sample(&self, pos_meters: Vec3) -> Cell; + fn get(&self, at: Coord) -> Cell { + // DEPRECATED + self.sample(Vec3::new(at.x().into(), at.y().into(), 0.0) * self.feature_size()) + } + fn width(&self) -> u32; + fn height(&self) -> u32; + fn depth(&self) -> u32; + fn feature_size(&self) -> Flt; + fn time(&self) -> Flt; + fn step_no(&self) -> u64; +} + #[derive(Default)] pub struct SimState { cells: Array3>, @@ -112,6 +126,43 @@ impl SimState { } } +impl GenericSim for SimState { + fn sample(&self, pos_meters: Vec3) -> Cell { + // TODO: smarter sampling than nearest neighbor? + let pos_sim = pos_meters / self.feature_size(); + let idx = [pos_sim.z() as usize, pos_sim.y() as _, pos_sim.x() as _]; + match self.cells.get(idx) { + Some(cell) => Cell { + state: cell.state, + mat: mat::Static { + conductivity: cell.mat.conductivity(), + m: cell.mat.m(), + } + }, + None => Default::default(), + } + } + + fn width(&self) -> u32 { + self.cells.shape()[2] as _ + } + fn height(&self) -> u32 { + self.cells.shape()[1] as _ + } + fn depth(&self) -> u32 { + self.cells.shape()[0] as _ + } + fn feature_size(&self) -> Flt { + self.feature_size.into() + } + fn time(&self) -> Flt { + self.timestep() * self.step_no as Flt + } + fn step_no(&self) -> u64 { + self.step_no + } +} + impl SimState { pub fn impulse_bz(&mut self, c: Coord, bz: Flt) { self.get_mut(c).impulse_bz(bz); @@ -122,17 +173,6 @@ impl SimState { } impl SimState { - pub fn time(&self) -> Flt { - self.timestep() * self.step_no as Flt - } - - pub fn step_no(&self) -> u64 { - self.step_no - } - - pub fn feature_size(&self) -> Flt { - self.feature_size.into() - } pub fn impulse_ex(&mut self, c: Coord, ex: Flt) { self.get_mut(c).state.e += Vec3::new(ex, 0.0, 0.0); @@ -144,15 +184,6 @@ impl SimState { self.get_mut(c).state.e += Vec3::new(0.0, 0.0, ez); } - pub fn width(&self) -> u32 { - self.cells.shape()[2] as _ - } - pub fn height(&self) -> u32 { - self.cells.shape()[1] as _ - } - pub fn depth(&self) -> u32 { - self.cells.shape()[0] as _ - } pub fn get(&self, c: Coord) -> &Cell { &self.cells[c.row_major_idx()] }