Rendering now mostly uses the GenericSim

This commit is contained in:
2020-09-25 20:15:39 -07:00
parent a931252e9c
commit cae84222c0
4 changed files with 100 additions and 62 deletions

View File

@@ -3,7 +3,7 @@ use crate::consts;
use crate::coord::Coord; use crate::coord::Coord;
use crate::meas::{self, AbstractMeasurement}; use crate::meas::{self, AbstractMeasurement};
use crate::render::{self, MultiRenderer, Renderer}; use crate::render::{self, MultiRenderer, Renderer};
use crate::sim::SimState; use crate::sim::{GenericSim as _, SimState};
use crate::vec3::Vec3; use crate::vec3::Vec3;
use log::{info, trace}; use log::{info, trace};

View File

@@ -1,18 +1,18 @@
use crate::coord::Coord; use crate::coord::Coord;
use crate::geom::{Point, Region}; use crate::geom::Region;
use crate::mat::Material as _; use crate::mat::Material as _;
use crate::sim::{Cell, SimSnapshot}; use crate::sim::GenericSim;
use std::fmt::Display; use std::fmt::Display;
use std::iter::Sum; //use std::iter::Sum;
pub trait AbstractMeasurement { pub trait AbstractMeasurement {
fn eval(&self, state: &SimSnapshot) -> String; fn eval(&self, state: &dyn GenericSim) -> String;
} }
pub struct Time; pub struct Time;
impl AbstractMeasurement for 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()) format!("{:.3e}s (step {})", state.time(), state.step_no())
} }
} }
@@ -20,7 +20,7 @@ impl AbstractMeasurement for Time {
pub struct Meta; pub struct Meta;
impl AbstractMeasurement for 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()) format!("{}x{} feat: {:.1e}m", state.width(), state.height(), state.feature_size())
} }
} }
@@ -34,25 +34,28 @@ impl Label {
} }
impl AbstractMeasurement for Label { impl AbstractMeasurement for Label {
fn eval(&self, _state: &SimSnapshot) -> String { fn eval(&self, _state: &dyn GenericSim) -> String {
self.0.clone() self.0.clone()
} }
} }
fn sum_over_region<T: Default + Send + Sum<T>, R: Region + Sync, F: Fn(Coord, &Cell) -> T + Sync>(state: &SimSnapshot, r: &R, f: F) -> T { // fn sum_over_region<T: Default + Send + Sum<T>, 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()) { // state.map_sum_enumerated(|coord, cell| if r.contains(Point::from(coord)*state.feature_size()) {
f(coord, cell) // f(coord, cell)
} else { // } else {
Default::default() // Default::default()
}) // })
} // }
pub struct Current<R>(pub R); pub struct Current<R>(pub R);
impl<R: Region + Display + Sync> AbstractMeasurement for Current<R> { impl<R: Region + Display + Sync> AbstractMeasurement for Current<R> {
fn eval(&self, state: &SimSnapshot) -> String { // fn eval(&self, state: &SimSnapshot) -> String {
let current = sum_over_region(state, &self.0, |coord, _cell| state.current(coord)); // 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()) // 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<R: Region + Display + Sync> AbstractMeasurement for Current<R> {
pub struct Magnetization(pub Coord); pub struct Magnetization(pub Coord);
impl AbstractMeasurement for Magnetization { 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(); let m = state.get(self.0).mat().m();
format!("M{}: ({:.2e}, {:.2e}, {:.2e})", self.0, m.x(), m.y(), m.z()) 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); pub struct MagneticFlux(pub Coord);
impl AbstractMeasurement for MagneticFlux { impl AbstractMeasurement for MagneticFlux {
fn eval(&self, state: &SimSnapshot) -> String { fn eval(&self, state: &dyn GenericSim) -> String {
let b = state.get(self.0).b(); let b = state.get(self.0).b();
format!("B{}: ({:.2e}, {:.2e}, {:.2e})", self.0, b.x(), b.y(), b.z()) 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); pub struct MagneticStrength(pub Coord);
impl AbstractMeasurement for MagneticStrength { impl AbstractMeasurement for MagneticStrength {
fn eval(&self, state: &SimSnapshot) -> String { fn eval(&self, state: &dyn GenericSim) -> String {
let h = state.get(self.0).h(); let h = state.get(self.0).h();
format!("H{}: ({:.2e}, {:.2e}, {:.2e})", self.0, h.x(), h.y(), h.z()) 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); pub struct ElectricField(pub Coord);
impl AbstractMeasurement for ElectricField { impl AbstractMeasurement for ElectricField {
fn eval(&self, state: &SimSnapshot) -> String { fn eval(&self, state: &dyn GenericSim) -> String {
let e = state.get(self.0).e(); let e = state.get(self.0).e();
format!("E{}: ({:.2e}, {:.2e}, {:.2e})", self.0, e.x(), e.y(), e.z()) format!("E{}: ({:.2e}, {:.2e}, {:.2e})", self.0, e.x(), e.y(), e.z())
} }
@@ -98,21 +101,22 @@ impl AbstractMeasurement for ElectricField {
pub struct Energy; pub struct Energy;
impl AbstractMeasurement for 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: // Potential energy stored in a E/M field:
// https://en.wikipedia.org/wiki/Magnetic_energy // https://en.wikipedia.org/wiki/Magnetic_energy
// https://en.wikipedia.org/wiki/Electric_potential_energy#Energy_stored_in_an_electrostatic_field_distribution // https://en.wikipedia.org/wiki/Electric_potential_energy#Energy_stored_in_an_electrostatic_field_distribution
// U(B) = 1/2 \int H . B dV // U(B) = 1/2 \int H . B dV
// U(E) = 1/2 \int E . D dV // U(E) = 1/2 \int E . D dV
let f = state.feature_size(); // let f = state.feature_size();
#[allow(non_snake_case)] // #[allow(non_snake_case)]
let dV = f*f*f; // let dV = f*f*f;
let e = state.map_sum(|cell| { //let e = state.map_sum(|cell| {
// E . D is perpetually 0 since we don't model D. // // E . D is perpetually 0 since we don't model D.
// All potential energy is in the magnetic field. // // All potential energy is in the magnetic field.
0.5 * cell.h().dot(cell.b()) * dV // 0.5 * cell.h().dot(cell.b()) * dV
}); //});
format!("U: {:.2e}", e) //format!("U: {:.2e}", e)
format!("U: TODO")
} }
} }

View File

@@ -2,7 +2,8 @@ use ansi_term::Color::RGB;
use crate::geom::Point; use crate::geom::Point;
use crate::{flt::{Flt, Real}, Material as _, SimSnapshot, SimState}; use crate::{flt::{Flt, Real}, Material as _, SimSnapshot, SimState};
use crate::mat; use crate::mat;
use crate::sim::Cell; use crate::sim::{Cell, GenericSim};
use crate::vec3::Vec3;
use crate::meas::AbstractMeasurement; use crate::meas::AbstractMeasurement;
use font8x8::{BASIC_FONTS, GREEK_FONTS, UnicodeFonts as _}; use font8x8::{BASIC_FONTS, GREEK_FONTS, UnicodeFonts as _};
use log::trace; use log::trace;
@@ -54,12 +55,12 @@ fn scale_vector(x: Point, typical_mag: Flt) -> Point {
struct RenderSteps<'a> { struct RenderSteps<'a> {
im: RgbImage, im: RgbImage,
sim: &'a SimSnapshot, sim: &'a dyn GenericSim,
meas: &'a [Box<dyn AbstractMeasurement>], meas: &'a [Box<dyn AbstractMeasurement>],
} }
impl<'a> RenderSteps<'a> { impl<'a> RenderSteps<'a> {
fn render(state: &'a SimSnapshot, measurements: &'a [Box<dyn AbstractMeasurement>]) -> RgbImage { fn render(state: &'a dyn GenericSim, measurements: &'a [Box<dyn AbstractMeasurement>]) -> RgbImage {
let width = 768; let width = 768;
let height = width * state.height() / state.width(); let height = width * state.height() / state.width();
let mut me = Self::new(state, measurements, width, height); let mut me = Self::new(state, measurements, width, height);
@@ -75,7 +76,7 @@ impl<'a> RenderSteps<'a> {
me.render_measurements(); me.render_measurements();
me.im me.im
} }
fn new(sim: &'a SimSnapshot, meas: &'a [Box<dyn AbstractMeasurement>], width: u32, height: u32) -> Self { fn new(sim: &'a dyn GenericSim, meas: &'a [Box<dyn AbstractMeasurement>], width: u32, height: u32) -> Self {
RenderSteps { RenderSteps {
im: RgbImage::new(width, height), im: RgbImage::new(width, height),
sim, sim,
@@ -83,10 +84,12 @@ impl<'a> RenderSteps<'a> {
} }
} }
fn get_at_px(&self, x_px: u32, y_px: u32) -> &Cell { fn get_at_px(&self, x_px: u32, y_px: u32) -> Cell {
let x_sim = x_px * self.sim.width() / self.im.width(); let x_prop = x_px as Flt / self.im.width() as Flt;
let y_sim = y_px * self.sim.height() / self.im.height(); let x_m = x_prop * (self.sim.width() as Flt * self.sim.feature_size());
self.sim.get((x_sim, y_sim).into()) 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 //////////// ////////////// Ex/Ey/Bz configuration ////////////
@@ -132,7 +135,7 @@ impl<'a> RenderSteps<'a> {
for y in 0..h { for y in 0..h {
for x in 0..w { for x in 0..w {
let cell = self.get_at_px(x, y); let cell = self.get_at_px(x, y);
let value = measure(cell); let value = measure(&cell);
let scaled = if signed { let scaled = if signed {
scale_signed_to_u8(value, typical) scale_signed_to_u8(value, typical)
} else { } else {
@@ -176,7 +179,7 @@ impl<'a> RenderSteps<'a> {
let yend = (ystart + size).min(h); let yend = (ystart + size).min(h);
for y in ystart..yend { for y in ystart..yend {
for x in xstart..xend { 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; let xw = xend - xstart;

View File

@@ -11,6 +11,20 @@ use std::iter::Sum;
pub type SimSnapshot = SimState<mat::Static>; pub type SimSnapshot = SimState<mat::Static>;
pub trait GenericSim {
fn sample(&self, pos_meters: Vec3) -> Cell<mat::Static>;
fn get(&self, at: Coord) -> Cell<mat::Static> {
// 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)] #[derive(Default)]
pub struct SimState<M=GenericMaterial> { pub struct SimState<M=GenericMaterial> {
cells: Array3<Cell<M>>, cells: Array3<Cell<M>>,
@@ -112,6 +126,43 @@ impl<M: Material + Default + Send + Sync> SimState<M> {
} }
} }
impl<M: Material> GenericSim for SimState<M> {
fn sample(&self, pos_meters: Vec3) -> Cell<mat::Static> {
// 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<M: Material> SimState<M> { impl<M: Material> SimState<M> {
pub fn impulse_bz(&mut self, c: Coord, bz: Flt) { pub fn impulse_bz(&mut self, c: Coord, bz: Flt) {
self.get_mut(c).impulse_bz(bz); self.get_mut(c).impulse_bz(bz);
@@ -122,17 +173,6 @@ impl<M: Material> SimState<M> {
} }
impl<M> SimState<M> { impl<M> SimState<M> {
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) { pub fn impulse_ex(&mut self, c: Coord, ex: Flt) {
self.get_mut(c).state.e += Vec3::new(ex, 0.0, 0.0); self.get_mut(c).state.e += Vec3::new(ex, 0.0, 0.0);
@@ -144,15 +184,6 @@ impl<M> SimState<M> {
self.get_mut(c).state.e += Vec3::new(0.0, 0.0, ez); 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<M> { pub fn get(&self, c: Coord) -> &Cell<M> {
&self.cells[c.row_major_idx()] &self.cells[c.row_major_idx()]
} }