Rendering now mostly uses the GenericSim
This commit is contained in:
@@ -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};
|
||||
|
66
src/meas.rs
66
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<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()) {
|
||||
f(coord, cell)
|
||||
} else {
|
||||
Default::default()
|
||||
})
|
||||
}
|
||||
// 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()) {
|
||||
// f(coord, cell)
|
||||
// } else {
|
||||
// Default::default()
|
||||
// })
|
||||
// }
|
||||
|
||||
pub struct Current<R>(pub R);
|
||||
|
||||
impl<R: Region + Display + Sync> AbstractMeasurement for Current<R> {
|
||||
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<R: Region + Display + Sync> AbstractMeasurement for Current<R> {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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<dyn AbstractMeasurement>],
|
||||
}
|
||||
|
||||
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 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<dyn AbstractMeasurement>], width: u32, height: u32) -> Self {
|
||||
fn new(sim: &'a dyn GenericSim, meas: &'a [Box<dyn AbstractMeasurement>], 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;
|
||||
|
71
src/sim.rs
71
src/sim.rs
@@ -11,6 +11,20 @@ use std::iter::Sum;
|
||||
|
||||
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)]
|
||||
pub struct SimState<M=GenericMaterial> {
|
||||
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> {
|
||||
pub fn impulse_bz(&mut self, c: Coord, bz: Flt) {
|
||||
self.get_mut(c).impulse_bz(bz);
|
||||
@@ -122,17 +173,6 @@ impl<M: Material> 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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> {
|
||||
&self.cells[c.row_major_idx()]
|
||||
}
|
||||
|
Reference in New Issue
Block a user