Model the H/B field in 3d, mostly
This commit is contained in:
102
src/mat.rs
102
src/mat.rs
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
src/meas.rs
17
src/meas.rs
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
31
src/sim.rs
31
src/sim.rs
@@ -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
|
||||||
|
@@ -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()
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user