restructure this multi-crate project to use Cargo's "workspace" feature

this solves an issue in the Nix build, where managing multiple
Cargo.lock files is otherwise tricky. it causes (or fails to fix?) an adjacent issue where
the spirv builder doesn't seem to have everything it needs vendored.
This commit is contained in:
2022-07-05 17:34:21 -07:00
parent d3cd12aa47
commit 5b99d30cda
64 changed files with 108 additions and 206 deletions

View File

@@ -0,0 +1,393 @@
use crate::support::{Optional, Vec3Std};
pub trait Material: Sized {
fn conductivity(&self) -> Vec3Std {
Default::default()
}
/// returns the new M vector for this material
fn move_b_vec(&self, m: Vec3Std, _target_b: Vec3Std) -> Vec3Std {
// XXX could return either 0, or `m`. they should be the same, but one might be more
// optimizable than the other (untested).
m
}
}
#[derive(Copy, Clone, Default, PartialEq)]
pub struct Conductor<T>(pub T);
pub type AnisomorphicConductor = Conductor<Vec3Std>;
pub type IsomorphicConductor = Conductor<f32>;
impl Material for AnisomorphicConductor {
fn conductivity(&self) -> Vec3Std {
self.0
}
}
impl Material for IsomorphicConductor {
fn conductivity(&self) -> Vec3Std {
Vec3Std::uniform(self.0)
}
}
/// M(B) parallelogram
///
///```ignore
/// ____________
/// / /
/// / . /
/// / /
/// /___________/
/// ```
///
/// The `.` depicts (0, 0). X axis is B; y axis is M.
/// As B increases, M remains constant until it hits an edge.
/// Then M rises up to its max.
/// Same thing happens on the left edge, as B decreases and M falls to its min.
#[derive(Copy, Clone, Default, PartialEq)]
pub struct MBPgram {
/// X coordinate at which the upward slope starts
pub b_start: f32,
/// X coordinate at which the upward slope ends
pub b_end: f32,
/// Vertical range of the graph
pub max_m: f32,
}
/// f(x0) = -ymax
/// f(x1) = ymax
fn eval_bounded_line(x: f32, x0: f32, x1: f32, ymax: f32) -> f32 {
let x = x - x0;
let x1 = x1 - x0;
// Now, f(0) = -ymax, f(x1) = ymax
let x = x.max(0.0).min(x1);
ymax * (2.0*x/x1 - 1.0)
}
impl MBPgram {
pub fn new(b_start: f32, b_end: f32, max_m: f32) -> Self {
Self { b_start, b_end, max_m }
}
/// Return the new `M`
pub fn move_b(self, m: f32, target_b: f32) -> f32 {
// Evaluate the curve on the right:
let y_right = eval_bounded_line(target_b, self.b_start, self.b_end, self.max_m);
let y_left = eval_bounded_line(target_b, -self.b_end, -self.b_start, self.max_m);
m.max(y_right).min(y_left)
}
}
impl Material for MBPgram {
fn move_b_vec(&self, m: Vec3Std, target_b: Vec3Std) -> Vec3Std {
Vec3Std::new(
self.move_b(m.x(), target_b.x()),
self.move_b(m.y(), target_b.y()),
self.move_b(m.z(), target_b.z()),
)
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct MHPgram {
/// optimized form of mu_0^-1 * (1-mu_r^-1)
b_mult: f32,
/// optimized form of h_intercept * (1-mu_r^-1)
m_offset: f32,
/// Vertical range of the graph
pub max_m: f32,
}
impl MHPgram {
/// h_intercept: X coordinate at which M is always zero.
/// mu_r: relative mu value along the non-flat edges of the parallelogram.
pub const fn new(h_intercept: f32, mu_r: f32, max_m: f32) -> Self {
const MU0_INV: f32 = 795774.715025073;
let one_minus_mu_r_inv = 1.0 - 1.0/mu_r;
Self {
b_mult: MU0_INV * one_minus_mu_r_inv,
m_offset: h_intercept * one_minus_mu_r_inv,
max_m,
}
}
pub fn h_intercept(&self) -> f32 {
const MU0_INV: f32 = 795774.715025073;
MU0_INV * self.m_offset / self.b_mult
}
pub fn mu_r(&self) -> f32 {
const MU0: f32 = 1.2566370621219e-06;
let mu_r_inv = 1.0 - MU0*self.b_mult;
1.0 / mu_r_inv
}
/// Return the new `M`
pub fn move_b(self, m: f32, target_b: f32) -> f32 {
// The point may exist outside the parallelogram.
// The right slope is defined by:
// B(H)/mu0 = h_intercept + (h-h_intercept)*mu_r
// "unoptimized" solution:
// let target_mh = target_b*MU0_INV;
// let h_on_right = (target_mh-self.h_intercept)/self.mu_r + self.h_intercept;
// let m_on_right = target_mh - h_on_right;
// Left:
// B(H)/mu0 = -h_intercept + (h+h_intercept)*mu_r
// "unoptimized" solution:
// let h_on_left = (target_mh+self.h_intercept)/self.mu_r - self.h_intercept;
// let m_on_left = target_mh - h_on_left;
let m_on_right = target_b*self.b_mult - self.m_offset;
let m_on_left = target_b*self.b_mult + self.m_offset;
if m_on_right >= m {
// if m_on_right <= self.max_m {
// // rightward edge movement
// m_on_right
// } else {
// // right of saturation
// self.max_m
// }
m_on_right.min(self.max_m)
} else if m_on_left <= m {
// if m_on_left >= -self.max_m {
// // leftward edge movement
// m_on_left
// } else {
// // left of saturation
// -self.max_m
// }
m_on_left.max(-self.max_m)
} else {
// interior movement
m
}
// this boils down to:
// m.clamp(m_on_left, m_on_right).clamp(-max_m, max_m)
}
}
impl Material for MHPgram {
fn move_b_vec(&self, m: Vec3Std, target_b: Vec3Std) -> Vec3Std {
Vec3Std::new(
self.move_b(m.x(), target_b.x()),
self.move_b(m.y(), target_b.y()),
self.move_b(m.z(), target_b.z()),
)
}
}
#[derive(Copy, Clone, Default, PartialEq)]
pub struct FullyGenericMaterial {
pub conductivity: Vec3Std,
pub m_b_curve: Optional<MBPgram>,
pub m_h_curve: Optional<MHPgram>,
}
impl Material for FullyGenericMaterial {
fn conductivity(&self) -> Vec3Std {
self.conductivity
}
fn move_b_vec(&self, m: Vec3Std, target_b: Vec3Std) -> Vec3Std {
if self.m_b_curve.is_some() {
self.m_b_curve.unwrap().move_b_vec(m, target_b)
} else if self.m_h_curve.is_some() {
self.m_h_curve.unwrap().move_b_vec(m, target_b)
} else {
Default::default()
}
}
}
/// MHPgram that's vaguely similar to Ferroxcube's 3R1 material
#[derive(Copy, Clone, Default, PartialEq)]
pub struct Ferroxcube3R1MH;
impl Into<MHPgram> for Ferroxcube3R1MH {
fn into(self) -> MHPgram {
const CURVE: MHPgram = MHPgram::new(25.0, 881.33, 44000.0);
CURVE
}
}
impl Material for Ferroxcube3R1MH {
fn move_b_vec(&self, m: Vec3Std, target_b: Vec3Std) -> Vec3Std {
let curve: MHPgram = (*self).into();
curve.move_b_vec(m, target_b)
}
}
/// Optimized material aimed at saving space for the common case of a simulation that contains
/// isomorphic conductors plus one exception material.
#[derive(Copy, Clone, Default, PartialEq)]
pub struct IsoConductorOr<M> {
/// conductivity, if >= 0, else ignored & used to signal the other material
pub value: f32,
pub mat: M,
}
impl<M: Default> IsoConductorOr<M> {
pub fn new_conductor(conductivity: f32) -> Self {
Self {
value: conductivity,
mat: Default::default(),
}
}
pub fn new_other() -> Self {
Self {
value: -1.0,
mat: Default::default()
}
}
}
impl<M: Material + Copy> Material for IsoConductorOr<M> {
fn conductivity(&self) -> Vec3Std {
Vec3Std::uniform(self.value.max(0.0))
}
fn move_b_vec(&self, m: Vec3Std, target_b: Vec3Std) -> Vec3Std {
if self.value < 0.0 {
let mat = self.mat; //< XXX hack for ZST
mat.move_b_vec(m, target_b)
} else {
m
}
}
}
#[cfg(test)]
mod test {
use super::*;
fn assert_eq_approx(actual: f32, expected: f32, max_error: f32) {
assert!((actual - expected).abs() < max_error, "{} != {}", actual, expected);
}
fn assert_eq_approx_tight(actual: f32, expected: f32) {
assert_eq_approx(actual, expected, 1e-5);
}
#[test]
fn mb_curve_interior() {
let curve = MBPgram::new(4.0, 6.0, 20.0f32);
assert_eq_approx_tight(curve.move_b(0.0, 2.0), 0.0);
assert_eq_approx_tight(curve.move_b(0.0, 5.0), 0.0);
assert_eq_approx_tight(curve.move_b(1.0, 5.0), 1.0);
assert_eq_approx_tight(curve.move_b(20.0, 5.0), 20.0);
assert_eq_approx_tight(curve.move_b(-20.0, 4.0), -20.0);
assert_eq_approx_tight(curve.move_b(-20.0, -6.0), -20.0);
assert_eq_approx_tight(curve.move_b(20.0, -4.0), 20.0);
assert_eq_approx_tight(curve.move_b(10.0, -2.0), 10.0);
}
#[test]
fn mb_curve_exterior() {
let curve = MBPgram::new(4.0, 6.0, 20.0f32);
assert_eq_approx_tight(curve.move_b(0.0, 6.0), 20.0);
assert_eq_approx_tight(curve.move_b(0.0, 7.0), 20.0);
assert_eq_approx_tight(curve.move_b(0.0, -6.0), -20.0);
assert_eq_approx_tight(curve.move_b(0.0, -7.0), -20.0);
assert_eq_approx_tight(curve.move_b(2.0, -6.0), -20.0);
assert_eq_approx_tight(curve.move_b(20.0, -6.0), -20.0);
assert_eq_approx_tight(curve.move_b(20.0, -5.0), 0.0);
assert_eq_approx_tight(curve.move_b(20.0, -4.5), 10.0);
assert_eq_approx_tight(curve.move_b(-15.0, 4.5), -10.0);
assert_eq_approx_tight(curve.move_b(-15.0, 5.0), 0.0);
assert_eq_approx_tight(curve.move_b(-15.0, 5.5), 10.0);
assert_eq_approx_tight(curve.move_b(-15.0, 7.5), 20.0);
}
#[test]
fn mb_curve_3r1() {
// slope of 3r1 is about M=793210*B
// This is almost identical to iron (795615)!
let curve = MBPgram::new(-0.3899, 0.3900, 310000f32);
// magnetizing:
// v.s. 198893 in B(H) curve
assert_eq_approx(curve.move_b(0.0, 0.250), 198703.0, 1.0);
// v.s. 278321 in B(H) curve
assert_eq_approx(curve.move_b(198703.0, 0.350), 278201.0, 1.0);
assert_eq_approx(curve.move_b(278201.0, 0.390), 310000.0, 1.0);
// de-magnetizing:
// From saturation, decreasing B causes NO decrease in M: instead, it causes a decrease in
// H. This is probably BAD: in the B(H) curve, a large change in H always causes a large
// change in B. The movement of H here is likely to induce current, whereas it SHOULDN'T.
assert_eq_approx(curve.move_b(310000.0, 0.38995), 310000.0, 1.0);
// v.s. 258626 in B(H); H = 220
assert_eq_approx(curve.move_b(310000.0, 0.325), 258406.0, 1.0);
// here's where H crosses 0 (v.s. B=0.325 in the B(H) curve... quite a difference)
assert_eq_approx(curve.move_b(310000.0, 0.050), 39788.438, 1.0);
// v.s. 35.0 in B(H)
assert_eq_approx(curve.move_b(310000.0, 0.0), 39.75, 1.0);
// negative magnetization:
// erase the magnetization: H = -40
assert_eq_approx(curve.move_b(310000.0, -0.00005), 0.0, 1.0);
// the magnetization has been completely erased:
assert_eq_approx(curve.move_b(310000.0, -0.25), -198703.0, 1.0);
}
#[test]
fn mh_curve_parameters() {
let curve = MHPgram::new(50.0, 101.0, 500.0);
assert_eq_approx(curve.h_intercept(), 50.0, 1e-3);
assert_eq_approx(curve.mu_r(), 101.0, 1e-3);
}
#[test]
fn mh_curve_edge_travel() {
const MU0: f32 = 1.2566370621219e-06;
let curve = MHPgram::new(50.0, 101.0, 500.0);
assert_eq_approx(curve.move_b(0.0, 151.0*MU0), 100.0, 0.1); // rightward travel along edge
assert_eq_approx(curve.move_b(100.0, 252.0*MU0), 200.0, 0.1);
assert_eq_approx(curve.move_b(200.0, 555.0*MU0), 500.0, 0.1);
assert_eq_approx(curve.move_b(500.0, 500.0*MU0), 500.0, 0.1); // back (leftward) travel to H=0
assert_eq_approx(curve.move_b(500.0, 455.0*MU0), 500.0, 0.1); // back (leftward) travel to H=-45
assert_eq_approx(curve.move_b(500.0, 354.0*MU0), 400.0, 0.1); // back (leftward) travel to H=-46
assert_eq_approx(curve.move_b(400.0, 253.0*MU0), 300.0, 0.1); // back (leftward) travel to H=-47
assert_eq_approx(curve.move_b(300.0, -50.0*MU0), 0.0, 0.1); // back (leftward) travel to H=-50
assert_eq_approx(curve.move_b(0.0, -151.0*MU0), -100.0, 0.1); // back (leftward) travel to H=-51
assert_eq_approx(curve.move_b(-100.0, -555.0*MU0), -500.0, 0.1); // back (leftward) travel to H=-55
assert_eq_approx(curve.move_b(-500.0, -456.0*MU0), -500.0, 0.1); // interior (rightward) travel to H=44
assert_eq_approx(curve.move_b(-500.0, -354.0*MU0), -400.0, 0.1); // interior (rightward) travel to H=46
assert_eq_approx(curve.move_b(-400.0, -253.0*MU0), -300.0, 0.1); // interior (rightward) travel to H=47
assert_eq_approx(curve.move_b(-300.0, 50.0*MU0), 0.0, 0.1); // interior (rightward) travel to H=50
}
#[test]
fn mh_curve_interior() {
const MU0: f32 = 1.2566370621219e-06;
let curve = MHPgram::new(50.0, 101.0, 500.0);
// max M (right) happens at H=55; B = 555*MU0;
// min M (right) happens at H=45; B = -455*MU0;
// max M (left) happens at H=-45; B = 455*MU0;
// min M (left) happens at H=-55; B = -555*MU0;
assert_eq_approx(curve.move_b(0.0, 40.0*MU0), 0.0, 0.1);
assert_eq_approx(curve.move_b(0.0, 50.0*MU0), 0.0, 0.1);
assert_eq_approx(curve.move_b(0.0, -40.0*MU0), 0.0, 0.1);
assert_eq_approx(curve.move_b(0.0, -50.0*MU0), 0.0, 0.1);
assert_eq_approx(curve.move_b(-400.0, -400.0*MU0), -400.0, 0.1); // rightward travel from H=-54 to NOT H=-53.5
assert_eq_approx(curve.move_b(-400.0, -355.0*MU0), -400.0, 0.1); // rightward travel from H=-54 to H=45
assert_eq_approx(curve.move_b(-400.0, -354.0*MU0), -400.0, 0.1); // rightward travel from H=-54 to H=46
assert_eq_approx(curve.move_b(-400.0, 5000.0*MU0), 500.0, 0.1); // rightward travel from H=-54 to H>>55
}
#[test]
fn mh_curve_exterior() {
const MU0: f32 = 1.2566370621219e-06;
let curve = MHPgram::new(50.0, 101.0, 500.0);
assert_eq_approx(curve.move_b(500.0, 556.0*MU0), 500.0, 0.1); // exterior travel to H=56
assert_eq_approx(curve.move_b(500.0, 5000.0*MU0), 500.0, 0.1); // exterior travel to H>>55
assert_eq_approx(curve.move_b(500.0, -5000.0*MU0), -500.0, 0.1); // exterior travel from H>>55 to H << -55
assert_eq_approx(curve.move_b(-501.0, 454.0*MU0), 400.0, 0.1); // exterior travel from H<<-55 (rounding error) to H=54
}
}