Model the H/B field in 3d, mostly

This commit is contained in:
2020-09-17 22:53:12 -07:00
parent 54b23a0494
commit a1f5b96ae1
5 changed files with 68 additions and 92 deletions

View File

@@ -1,5 +1,7 @@
use crate::{CellState, consts, flt::Flt}; use crate::{CellState, consts};
use crate::flt::Flt;
use crate::geom::{Line, Point, Polygon}; use crate::geom::{Line, Point, Polygon};
use crate::vec3::Vec3;
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use std::cmp::Ordering; use std::cmp::Ordering;
@@ -10,21 +12,21 @@ pub trait Material {
fn conductivity(&self) -> Flt { fn conductivity(&self) -> Flt {
0.0 0.0
} }
/// Return the magnetization in the z direction (M_z). /// Return the magnetization.
fn mz(&self) -> Flt { fn m(&self) -> Vec3 {
0.0 Vec3::zero()
} }
/// Return the new h_z, and optionally change any internal state (e.g. magnetization). /// Return the new h, and optionally change any internal state (e.g. magnetization).
fn step_h(&mut self, context: &CellState, delta_bz: Flt) -> Flt { fn step_h(&mut self, context: &CellState, delta_b: Vec3) -> Vec3 {
// B = mu0*(H+M), and in a default material M=0. // B = mu0*(H+M), and in a default material M=0.
context.hz() + delta_bz / consts::MU0 context.h() + delta_b / consts::real::MU0()
} }
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct Static { pub struct Static {
pub conductivity: Flt, pub conductivity: Flt,
pub mz: Flt, pub m: Vec3,
} }
impl Static { impl Static {
@@ -40,57 +42,8 @@ impl Material for Static {
fn conductivity(&self) -> Flt { fn conductivity(&self) -> Flt {
self.conductivity self.conductivity
} }
fn mz(&self) -> Flt { fn m(&self) -> Vec3 {
self.mz self.m
}
}
#[derive(Clone, Default)]
pub struct ComsolFerromagnet {
/// Instantaneous magnetization in the z direction.
pub mz: Flt,
// Ferromagnetic parameters:
/// Magnetic saturation (symmetric, so saturates to either +Ms or -Ms).
pub ms: Flt,
/// Rate at which M changes w.r.t. H.
pub xi: Flt,
}
impl Material for ComsolFerromagnet {
fn mz(&self) -> Flt {
self.mz
}
fn step_h(&mut self, context: &CellState, delta_bz: Flt) -> Flt {
// COMSOL ferromagnetic model: https://www.comsol.com/blogs/accessing-external-material-models-for-magnetic-simulations/
// delta(M) = xi * delta*(H) # delta(M) = M(t+1) - M(t)
// new_M = clamp(M+delta(M), -Ms, Ms)) # i.e. clamp to saturation
// Where delta*(H) is H(t+1) - H(t), but clamping H(t) to <= 0 if positively saturated
// and clamping H(t) to >= 0 if negatively saturated
let expected_delta_h = delta_bz / consts::MU0;
let expected_new_h = context.hz() + expected_delta_h;
// let unsaturated_new_m = self.mz + self.xi * unsaturated_delta_h;
let unsaturated_new_m = if self.mz == self.ms {
// positively saturated
let delta_h = expected_new_h.min(0.0) - context.hz().min(0.0);
// assert!(self.ms == 0.0, "pos sat");
self.mz + self.xi * delta_h
} else if self.mz == -self.ms {
// negatively saturated
let delta_h = expected_new_h.max(0.0) - context.hz().max(0.0);
//assert!(self.ms == 0.0, "neg sat");
self.mz + self.xi * delta_h
} else {
// not saturated
self.mz + self.xi * expected_delta_h
};
let new_m = unsaturated_new_m.min(self.ms).max(-self.ms);
let delta_m = new_m - self.mz;
self.mz = new_m;
// B = mu0*(H + M)
// \Delta B = mu0*(\Delta H + \Delta M)
let delta_h = delta_bz / consts::MU0 - delta_m;
context.hz() + delta_h
} }
} }
@@ -169,9 +122,11 @@ impl MHCurve {
#[derive(Clone)] #[derive(Clone)]
pub struct PiecewiseLinearFerromagnet { pub struct PiecewiseLinearFerromagnet {
/// Instantaneous magnetization in the z direction. /// Instantaneous magnetization in the z direction.
pub mz: Flt, pub m: Vec3,
pub conductivity: Flt, pub conductivity: Flt,
mh_curve: MHCurve, mh_curve_x: MHCurve,
mh_curve_y: MHCurve,
mh_curve_z: MHCurve,
} }
impl PiecewiseLinearFerromagnet { impl PiecewiseLinearFerromagnet {
@@ -184,28 +139,33 @@ impl PiecewiseLinearFerromagnet {
}).collect(); }).collect();
Self { Self {
mz: 0.0, m: Vec3::default(),
conductivity: 0.0, conductivity: 0.0,
mh_curve: MHCurve::new(&*mh_points), mh_curve_x: MHCurve::new(&*mh_points),
mh_curve_y: MHCurve::new(&*mh_points),
mh_curve_z: MHCurve::new(&*mh_points),
} }
} }
} }
impl Material for PiecewiseLinearFerromagnet { impl Material for PiecewiseLinearFerromagnet {
fn mz(&self) -> Flt { fn m(&self) -> Vec3 {
self.mz self.m
} }
fn conductivity(&self) -> Flt { fn conductivity(&self) -> Flt {
self.conductivity self.conductivity
} }
fn step_h(&mut self, context: &CellState, delta_bz: Flt) -> Flt { fn step_h(&mut self, context: &CellState, delta_b: Vec3) -> Vec3 {
let (h, m) = (context.hz(), self.mz); let (h, m) = (context.h(), self.m);
let target_hm = h + m + delta_bz/consts::MU0; let target_hm = h + m + delta_b/consts::real::MU0();
let (h, m) = self.mh_curve.move_to(h, m, target_hm); // TODO: this is probably not the best way to generalize a BH curve into 3d.
let (hx, mx) = self.mh_curve_x.move_to(h.x(), m.x(), target_hm.x());
let (hy, my) = self.mh_curve_x.move_to(h.y(), m.y(), target_hm.y());
let (hz, mz) = self.mh_curve_x.move_to(h.z(), m.z(), target_hm.z());
self.mz = m; self.m = Vec3::new(mx, my, mz);
h Vec3::new(hx, hy, hz)
} }
} }

View File

@@ -49,8 +49,8 @@ pub struct Magnetization(pub u32, pub u32);
impl AbstractMeasurement for Magnetization { impl AbstractMeasurement for Magnetization {
fn eval(&self, state: &SimSnapshot) -> String { fn eval(&self, state: &SimSnapshot) -> String {
let mz = state.get(self.0, self.1).mat().mz(); let m = state.get(self.0, self.1).mat().m();
format!("Mz({}, {}): {:.2e}", self.0, self.1, mz) format!("M({}, {}): ({:.2e}, {:.2e}, {:.2e})", self.0, self.1, m.x(), m.y(), m.z())
} }
} }
@@ -59,8 +59,8 @@ pub struct MagneticFlux(pub u32, pub u32);
impl AbstractMeasurement for MagneticFlux { impl AbstractMeasurement for MagneticFlux {
fn eval(&self, state: &SimSnapshot) -> String { fn eval(&self, state: &SimSnapshot) -> String {
let bz = state.get(self.0, self.1).bz(); let b = state.get(self.0, self.1).b();
format!("Bz({}, {}): {:.2e}", self.0, self.1, bz) format!("Bz({}, {}): ({:.2e}, {:.2e}, {:.2e})", self.0, self.1, b.x(), b.y(), b.z())
} }
} }
@@ -69,8 +69,8 @@ pub struct MagneticStrength(pub u32, pub u32);
impl AbstractMeasurement for MagneticStrength { impl AbstractMeasurement for MagneticStrength {
fn eval(&self, state: &SimSnapshot) -> String { fn eval(&self, state: &SimSnapshot) -> String {
let bz = state.get(self.0, self.1).hz(); let h = state.get(self.0, self.1).h();
format!("Hz({}, {}): {:.2e}", self.0, self.1, bz) format!("Hz({}, {}): ({:.2e}, {:.2e}, {:.2e})", self.0, self.1, h.x(), h.y(), h.z())
} }
} }
@@ -82,9 +82,10 @@ impl AbstractMeasurement for Energy {
#[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| {
0.5 * cell.hz() * cell.bz() * dV 0.5 * cell.h().z() * cell.b().z() * dV
}); });
format!("U: {:.2e}", e) // TODO: update to 3 dimensions!
format!("U(TODO): {:.2e}", e)
} }
} }

View File

@@ -81,9 +81,9 @@ 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.sim.get(x, y); let cell = self.sim.get(x, y);
let r = scale_signed_to_u8(cell.mat().mz(), 100.0); let r = scale_signed_to_u8(cell.mat().m().z(), 100.0);
let b = scale_unsigned_to_u8(cell.mat().conductivity(), 10.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.b().z(), 1.0e-4);
self.im.put_pixel(x, y, Rgb([r, g, b])); self.im.put_pixel(x, y, Rgb([r, g, b]));
} }
} }

View File

@@ -75,14 +75,14 @@ impl<M: Material + Default + Send + Sync> SimState<M> {
let mut cell: Cell<mat::Static> = Default::default(); let mut cell: Cell<mat::Static> = Default::default();
for sample in cells { for sample in cells {
cell.state.e += sample.state.e(); cell.state.e += sample.state.e();
cell.state.hz += sample.hz(); cell.state.h += sample.h();
cell.mat.conductivity += sample.mat.conductivity(); cell.mat.conductivity += sample.mat.conductivity();
cell.mat.mz += sample.mat.mz(); cell.mat.m += sample.mat.m();
} }
cell.state.e /= Real::from_inner(sample_area); cell.state.e /= Real::from_inner(sample_area);
cell.state.hz /= sample_area; cell.state.h /= Real::from_inner(sample_area);
cell.mat.conductivity /= sample_area; cell.mat.conductivity /= sample_area;
cell.mat.mz /= sample_area; cell.mat.m /= Real::from_inner(sample_area);
cell cell
}); });
@@ -181,6 +181,9 @@ impl<M> Cell<M> {
pub fn hz(&self) -> Flt { pub fn hz(&self) -> Flt {
self.state.hz() self.state.hz()
} }
pub fn h(&self) -> Vec3 {
self.state.h()
}
pub fn mat(&self) -> &M { pub fn mat(&self) -> &M {
&self.mat &self.mat
} }
@@ -190,8 +193,11 @@ impl<M> Cell<M> {
} }
impl<M: Material> Cell<M> { impl<M: Material> Cell<M> {
pub fn b(&self) -> Vec3 {
(self.h() + self.mat.m()) * consts::real::MU0()
}
pub fn bz(&self) -> Flt { pub fn bz(&self) -> Flt {
consts::MU0 * (self.hz() + self.mat.mz()) self.b().z()
} }
pub fn current(&self) -> Point { pub fn current(&self) -> Point {
@@ -200,7 +206,7 @@ impl<M: Material> Cell<M> {
} }
fn impulse_bz(&mut self, delta_bz: Flt) { fn impulse_bz(&mut self, delta_bz: Flt) {
self.state.hz = self.mat.step_h(&self.state, delta_bz).into(); self.state.h = self.mat.step_h(&self.state, Vec3::new(0.0, 0.0, delta_bz));
} }
fn step_h(mut self, right: &Self, down: &Self, delta_t: Real, feature_size: Real) -> Self { fn step_h(mut self, right: &Self, down: &Self, delta_t: Real, feature_size: Real) -> Self {
@@ -227,10 +233,10 @@ impl<M: Material> Cell<M> {
let nabla_e = Vec3::from((delta_ez_y - delta_ey_z, delta_ex_z - delta_ez_x, delta_ey_x - delta_ex_y)); let nabla_e = Vec3::from((delta_ez_y - delta_ey_z, delta_ex_z - delta_ez_x, delta_ey_x - delta_ex_y));
let delta_b = -nabla_e * delta_t; let delta_b = -nabla_e * delta_t;
let hz = self.mat.step_h(&self.state, delta_b.z()); let h = self.mat.step_h(&self.state, delta_b);
Cell { Cell {
state: CellState { state: CellState {
hz: hz.into(), h: h.into(),
..self.state ..self.state
}, },
mat: self.mat, mat: self.mat,
@@ -284,7 +290,7 @@ impl<M: Material> Cell<M> {
Cell { Cell {
state: CellState { state: CellState {
e: e_next, e: e_next,
hz: self.state.hz, h: self.state.h,
}, },
mat: self.mat, mat: self.mat,
} }
@@ -294,7 +300,7 @@ impl<M: Material> Cell<M> {
#[derive(Copy, Clone, Default)] #[derive(Copy, Clone, Default)]
pub struct CellState { pub struct CellState {
e: Vec3, e: Vec3,
hz: Real, h: Vec3,
} }
impl CellState { impl CellState {
@@ -305,7 +311,10 @@ impl CellState {
self.e.y() self.e.y()
} }
pub fn hz(&self) -> Flt { pub fn hz(&self) -> Flt {
self.hz.into() self.h.z().into()
}
pub fn h(&self) -> Vec3 {
self.h
} }
pub fn e(&self) -> Vec3 { pub fn e(&self) -> Vec3 {
self.e self.e

View File

@@ -17,6 +17,12 @@ impl Vec3 {
z: z.into() z: z.into()
} }
} }
pub fn unit() -> Self {
Self::new(1.0, 1.0, 1.0)
}
pub fn zero() -> Self {
Self::new(0.0, 0.0, 0.0)
}
pub fn x(&self) -> Flt { pub fn x(&self) -> Flt {
self.x.into() self.x.into()
} }