Snapshot and decimate before rendering; add an explicit boundary layer

This commit is contained in:
2020-09-07 17:42:05 -07:00
parent 5d9c086137
commit 14fa68bfb2
4 changed files with 107 additions and 27 deletions

View File

@@ -1,14 +1,16 @@
use coremem::{Driver, mat}; use coremem::{Driver, mat};
fn main() { fn main() {
let width = 201; let width = 1300;
let height = 101; let height = 1000;
let mut driver = Driver::new(width, height, 1e-3 /* feature size */) let mut driver = Driver::new(width, height, 1e-5 /* feature size */)
.with_y4m_renderer("ferromagnet.y4m") .with_y4m_renderer("ferromagnet.y4m")
.with_term_renderer()
.with_steps_per_frame(40)
; ;
for y in 0..height { for y in 0..height {
for x in 50..60 { for x in 500..600 {
*driver.state.get_mut(x, y).mat_mut() = mat::Static::conductor(1.0e1).into(); *driver.state.get_mut(x, y).mat_mut() = mat::Static::conductor(1.0e1).into();
} }
// for x in 30..40 { // for x in 30..40 {
@@ -19,12 +21,12 @@ fn main() {
// *state.get_mut(x, y).mat_mut() = mat::Conductor { conductivity: 1.0e8 }.into(); // *state.get_mut(x, y).mat_mut() = mat::Conductor { conductivity: 1.0e8 }.into();
// } // }
// } // }
for x in 72..80 { for x in 720..800 {
*driver.state.get_mut(x, y).mat_mut() = mat::Static::conductor(1.0e1).into(); *driver.state.get_mut(x, y).mat_mut() = mat::Static::conductor(1.0e1).into();
} }
} }
for y in 40..60 { for y in 400..600 {
for x in 62..70 { for x in 620..700 {
*driver.state.get_mut(x, y).mat_mut() = mat::PiecewiseLinearFerromagnet::from_bh(&[ *driver.state.get_mut(x, y).mat_mut() = mat::PiecewiseLinearFerromagnet::from_bh(&[
( 35.0, 0.0), ( 35.0, 0.0),
( 50.0, 0.250), ( 50.0, 0.250),
@@ -44,8 +46,8 @@ fn main() {
//}.into(); //}.into();
} }
} }
driver.add_boundary(100);
//let mut renderer = render::NullRenderer;
loop { loop {
//let imp = match state.step_no() { //let imp = match state.step_no() {
// 20..=60 => 1e6, // 20..=60 => 1e6,
@@ -61,8 +63,8 @@ fn main() {
// state.impulse_ey(50, 50, imp); // state.impulse_ey(50, 50, imp);
// state.impulse_bz(20, 20, (imp / 3.0e8) as _); // state.impulse_bz(20, 20, (imp / 3.0e8) as _);
// state.impulse_bz(80, 20, (imp / 3.0e8) as _); // state.impulse_bz(80, 20, (imp / 3.0e8) as _);
for y in 10..height-10 { for y in 100..height-100 {
for x in 52..58 { for x in 520..580 {
driver.state.impulse_ey(x, y, imp as _); driver.state.impulse_ey(x, y, imp as _);
} }
} }

View File

@@ -1,5 +1,6 @@
use crate::sim::SimState; use crate::mat;
use crate::render::{self, MultiRenderer, Renderer}; use crate::render::{self, MultiRenderer, Renderer};
use crate::sim::SimState;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
@@ -39,6 +40,26 @@ impl Driver {
self.with_renderer(render::ColorTermRenderer) self.with_renderer(render::ColorTermRenderer)
} }
pub fn add_boundary(&mut self, thickness: u32) {
for inset in 0..thickness {
let depth = thickness - inset;
// TODO: tune a scalar multiplier on this value
let conductivity = (depth*depth) as f64;
for x in inset as usize..self.state.width() - inset as usize {
// left
*self.state.get_mut(x, inset as _).mat_mut() = mat::Static::conductor(conductivity).into();
// right
*self.state.get_mut(x, self.state.height() - 1 - inset as usize).mat_mut() = mat::Static::conductor(conductivity).into();
}
for y in inset as usize..self.state.height() - inset as usize {
// top
*self.state.get_mut(inset as _, y).mat_mut() = mat::Static::conductor(conductivity).into();
// bottom
*self.state.get_mut(self.state.width() - 1 - inset as usize, y).mat_mut() = mat::Static::conductor(conductivity).into();
}
}
}
pub fn step(&mut self) { pub fn step(&mut self) {
let start_time = SystemTime::now(); let start_time = SystemTime::now();
if self.state.step_no() % self.steps_per_frame == 0 { if self.state.step_no() % self.steps_per_frame == 0 {

View File

@@ -1,6 +1,6 @@
use ansi_term::Color::RGB; use ansi_term::Color::RGB;
use crate::geom::Point; use crate::geom::Point;
use crate::{Material as _, SimState}; use crate::{Material as _, SimSnapshot, SimState};
use decorum::R64; use decorum::R64;
use image::{RgbImage, Rgb}; use image::{RgbImage, Rgb};
use imageproc::{pixelops, drawing}; use imageproc::{pixelops, drawing};
@@ -49,12 +49,12 @@ fn scale_vector(x: Point, typical_mag: f64) -> Point {
x.with_mag(new_mag) x.with_mag(new_mag)
} }
trait SimStateRenderExt { trait SimSnapshotRenderExt {
fn to_image(&self) -> RgbImage; fn to_image(&self) -> RgbImage;
fn e_vector(&self, xidx: u32, yidx: u32, size: u32) -> Point; fn e_vector(&self, xidx: u32, yidx: u32, size: u32) -> Point;
} }
impl SimStateRenderExt for SimState { impl SimSnapshotRenderExt for SimSnapshot {
fn to_image(&self) -> RgbImage { fn to_image(&self) -> RgbImage {
let w = self.width().try_into().unwrap(); let w = self.width().try_into().unwrap();
let h = self.height().try_into().unwrap(); let h = self.height().try_into().unwrap();
@@ -64,7 +64,7 @@ impl SimStateRenderExt for SimState {
for x in 0..w { for x in 0..w {
let cell = self.get(x as usize, y as usize); let cell = self.get(x as usize, y as usize);
let r = scale_signed_to_u8(cell.mat().mz(), 100.0); let r = scale_signed_to_u8(cell.mat().mz(), 100.0);
let b = scale_unsigned_to_u8(cell.mat().conductivity(), 3.0); let b = scale_unsigned_to_u8(cell.mat().conductivity(), 10.0);
let g = scale_signed_to_u8(cell.bz(), 1.0e-4); let g = scale_signed_to_u8(cell.bz(), 1.0e-4);
image.put_pixel(x, y, Rgb([r, g, b])); image.put_pixel(x, y, Rgb([r, g, b]));
} }
@@ -128,10 +128,10 @@ impl ImageRenderExt for RgbImage {
} }
pub trait Renderer { pub trait Renderer {
fn render(&mut self, state: &SimState) { fn render(&mut self, state: &SimSnapshot) {
self.render_with_image(state, &state.to_image()); self.render_with_image(state, &state.to_image());
} }
fn render_with_image(&mut self, state: &SimState, _im: &RgbImage) { fn render_with_image(&mut self, state: &SimSnapshot, _im: &RgbImage) {
self.render(state); self.render(state);
} }
} }
@@ -139,7 +139,7 @@ pub trait Renderer {
pub struct NumericTermRenderer; pub struct NumericTermRenderer;
impl Renderer for NumericTermRenderer { impl Renderer for NumericTermRenderer {
fn render(&mut self, state: &SimState) { fn render(&mut self, state: &SimSnapshot) {
for y in 0..state.height() { for y in 0..state.height() {
for x in 0..state.width() { for x in 0..state.width() {
let cell = state.get(x, y); let cell = state.get(x, y);
@@ -159,7 +159,7 @@ impl Renderer for NumericTermRenderer {
pub struct ColorTermRenderer; pub struct ColorTermRenderer;
impl Renderer for ColorTermRenderer { impl Renderer for ColorTermRenderer {
fn render_with_image(&mut self, state: &SimState, im: &RgbImage) { fn render_with_image(&mut self, state: &SimSnapshot, im: &RgbImage) {
let square = ""; let square = "";
let buf: String = im let buf: String = im
.enumerate_rows() .enumerate_rows()
@@ -189,7 +189,7 @@ impl Y4MRenderer {
} }
impl Renderer for Y4MRenderer { impl Renderer for Y4MRenderer {
fn render_with_image(&mut self, _state: &SimState, im: &RgbImage) { fn render_with_image(&mut self, _state: &SimSnapshot, im: &RgbImage) {
if self.encoder.is_none() { if self.encoder.is_none() {
let writer = File::create(&self.out_path).unwrap(); let writer = File::create(&self.out_path).unwrap();
self.encoder = Some(y4m::encode(im.width() as usize, im.height() as usize, y4m::Ratio::new(30, 1)) self.encoder = Some(y4m::encode(im.width() as usize, im.height() as usize, y4m::Ratio::new(30, 1))
@@ -236,16 +236,24 @@ impl MultiRenderer {
self.push(r); self.push(r);
self self
} }
pub fn render(&mut self, state: &SimState) {
//let max_width = 1980; //< TODO: make configurable
let max_width = 200;
let dec = (state.width() as u32 + max_width - 1) / max_width;
let snap = state.snapshot(dec);
Renderer::render(self, &snap);
}
} }
impl Renderer for MultiRenderer { impl Renderer for MultiRenderer {
fn render(&mut self, state: &SimState) { fn render(&mut self, state: &SimSnapshot) {
if self.renderers.len() != 0 { if self.renderers.len() != 0 {
self.render_with_image(state, &state.to_image()); self.render_with_image(state, &state.to_image());
} }
} }
fn render_with_image(&mut self, state: &SimState, im: &RgbImage) { fn render_with_image(&mut self, state: &SimSnapshot, im: &RgbImage) {
for r in &mut self.renderers { for r in &mut self.renderers {
r.render_with_image(state, im); r.render_with_image(state, im);
} }

View File

@@ -1,11 +1,13 @@
use crate::{consts}; use crate::{consts};
use crate::geom::Point; use crate::geom::Point;
use crate::mat::{GenericMaterial, Material}; use crate::mat::{self, GenericMaterial, Material};
use decorum::R64; use decorum::R64;
use ndarray::Array2; use ndarray::Array2;
use rayon::prelude::*; use rayon::prelude::*;
pub type SimSnapshot = SimState<mat::Static>;
#[derive(Default)] #[derive(Default)]
pub struct SimState<M=GenericMaterial> { pub struct SimState<M=GenericMaterial> {
cells: Array2<Cell<M>>, cells: Array2<Cell<M>>,
@@ -70,6 +72,49 @@ impl<M: Material + Clone + Default + Send + Sync> SimState<M> {
self.step_no += 1; self.step_no += 1;
} }
/// dec = decimation, e.g. set to 3 to sample 3 x 3 grids
pub fn snapshot(&self, dec: u32) -> SimSnapshot {
let rows = self.height() as u32 / dec;
let cols = self.width() as u32 / dec;
let sample_area = (dec * dec) as f64;
let immute_self = &self;
let new_cells_vec: Vec<Vec<_>> = (0..rows).into_par_iter()
.map(|new_y| {
(0..cols).into_iter().map(move |new_x| {
// sample dec x dec cells
let mut cell: Cell<mat::Static> = Default::default();
for y in new_y*dec..new_y*dec + dec {
for x in new_x*dec..new_x*dec + dec {
let sample = immute_self.get(x as _, y as _);
cell.state.ex += sample.ex();
cell.state.ey += sample.ey();
cell.state.hz += sample.hz();
cell.mat.conductivity += sample.mat.conductivity();
cell.mat.mz += sample.mat.mz();
}
}
cell.state.ex /= sample_area;
cell.state.ey /= sample_area;
cell.state.hz /= sample_area;
cell.mat.conductivity /= sample_area;
cell.mat.mz /= sample_area;
cell
}).collect()
}).collect();
let mut new_cells: Array2<Cell<mat::Static>> = Array2::default((rows as _, cols as _));
for (row, mut new_row) in new_cells_vec.into_iter().enumerate() {
new_cells.row_mut(row).as_slice_mut().unwrap().swap_with_slice(&mut new_row[..]);
}
SimState {
cells: new_cells,
feature_size: self.feature_size,
step_no: self.step_no,
}
}
} }
impl<M: Material> SimState<M> { impl<M: Material> SimState<M> {
@@ -87,6 +132,10 @@ impl<M> SimState<M> {
self.step_no self.step_no
} }
pub fn feature_size(&self) -> f64 {
self.feature_size.into()
}
pub fn impulse_ex(&mut self, x: usize, y: usize, ex: f64) { pub fn impulse_ex(&mut self, x: usize, y: usize, ex: f64) {
self.cells[[y, x]].state.ex += ex; self.cells[[y, x]].state.ex += ex;
} }
@@ -227,13 +276,13 @@ impl<M: Material> Cell<M> {
let sigma: R64 = self.mat.conductivity().into(); let sigma: R64 = self.mat.conductivity().into();
// XXX not obvious that bz_to_hz is sensible // XXX not obvious that bz_to_hz is sensible
let delta_hz_y = self.hz() - self.bz_to_hz(up.bz()); // let delta_hz_y = self.hz() - self.bz_to_hz(up.bz());
// let delta_hz_y = self.hz() - up.hz(); let delta_hz_y = self.hz() - up.hz();
let ex_rhs = self.state.ex*(ONE() - sigma/EPS0()*delta_t) + TWO()*delta_t/EPS0()*delta_hz_y/feature_size; let ex_rhs = self.state.ex*(ONE() - sigma/EPS0()*delta_t) + TWO()*delta_t/EPS0()*delta_hz_y/feature_size;
let ex_next = ex_rhs / (ONE() + sigma/EPS0()*delta_t); let ex_next = ex_rhs / (ONE() + sigma/EPS0()*delta_t);
let delta_hz_x = self.hz() - self.bz_to_hz(left.bz()); // let delta_hz_x = self.hz() - self.bz_to_hz(left.bz());
// let delta_hz_x = self.hz() - left.hz(); let delta_hz_x = self.hz() - left.hz();
let ey_rhs = self.state.ey*(ONE() - sigma/EPS0()*delta_t) - TWO()*delta_t/EPS0()*delta_hz_x/feature_size; let ey_rhs = self.state.ey*(ONE() - sigma/EPS0()*delta_t) - TWO()*delta_t/EPS0()*delta_hz_x/feature_size;
let ey_next = ey_rhs / (ONE() + sigma/EPS0()*delta_t); let ey_next = ey_rhs / (ONE() + sigma/EPS0()*delta_t);