introduce Isomorphic/Anisomorphic conductors into the CPU side

this is needed for the future Spirv optimizations to be possible.
This commit is contained in:
2022-01-28 19:24:36 -08:00
parent 39c13afc10
commit aeec4258c7
10 changed files with 172 additions and 37 deletions

View File

@@ -337,9 +337,9 @@ fn run_sim(id: u32, p: Params, g: Geometries) -> Results {
let feat_vol = p.geom.feat_size * p.geom.feat_size * p.geom.feat_size;
// mu_r=881.33, starting at H=25 to H=75.
let ferro_mat = mat::MHPgram::new(25.0, 881.33, 44000.0);
let ferro_mat = mat::Ferroxcube3R1MH::new();
// let ferro_mat = mat::db::conductor(wire_conductivity);
let wire_mat = mat::db::conductor(p.wire_conductivity);
let wire_mat = mat::IsomorphicConductor::new(p.wire_conductivity);
let mut driver: Driver<_> = Driver::new_spirv(g.dim, p.geom.feat_size);
driver.set_steps_per_stim(1000);

View File

@@ -265,8 +265,8 @@ impl<S: MaterialSim> Driver<S> {
});
}
pub fn add_classical_boundary<C: Coord, R: Real>(&mut self, thickness: C)
where S::Material: From<mat::Conductor<R>>
pub fn add_classical_boundary<C: Coord>(&mut self, thickness: C)
where S::Material: From<mat::IsomorphicConductor<f32>>
{
let timestep = self.state.timestep();
self.state.fill_boundary_using(thickness, |boundary_ness| {
@@ -274,7 +274,7 @@ impl<S: MaterialSim> Driver<S> {
let cond = b * (0.5 / timestep);
let iso_cond = cond.x() + cond.y() + cond.z();
let iso_conductor = mat::db::conductor(iso_cond);
let iso_conductor = mat::IsomorphicConductor::new(iso_cond);
iso_conductor
});
}

View File

@@ -1,18 +1,18 @@
//! database of common materials
use crate::geom::Vec3;
use crate::mat::{Conductor, LinearMagnet, Ferroxcube3R1, MinimalSquare};
use crate::mat::{AnisomorphicConductor, IsomorphicConductor, LinearMagnet, Ferroxcube3R1, MinimalSquare};
use crate::real::Real;
pub fn conductor<R: Real, R2: Real>(conductivity: R2) -> Conductor<R> {
Conductor::new(conductivity)
pub fn conductor<R: Real, R2: Real>(conductivity: R2) -> IsomorphicConductor<R> {
IsomorphicConductor::new(conductivity.cast())
}
pub fn anisotropic_conductor<R: Real, R2: Real>(conductivity: Vec3<R2>) -> Conductor<R> {
Conductor::new_anisotropic(conductivity)
pub fn anisotropic_conductor<R>(conductivity: Vec3<R>) -> AnisomorphicConductor<R> {
AnisomorphicConductor::new(conductivity)
}
pub fn copper<R: Real>() -> Conductor<R> {
Conductor::new(50_000_000.0)
pub fn copper<R: Real>() -> IsomorphicConductor<R> {
conductor(50_000_000.0)
}
// See https://en.wikipedia.org/wiki/Permeability_(electromagnetism)#Values_for_some_common_materials

View File

@@ -8,29 +8,47 @@ use serde::{Serialize, Deserialize};
/// Material which has a conductivity parameter, but cannot be magnetized
#[derive(Copy, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct Conductor<R> {
conductivity: Vec3<R>,
pub struct Conductor<V> {
conductivity: V,
}
impl<R: Real> Conductor<R> {
pub fn new<R2: Real>(conductivity: R2) -> Self {
pub type IsomorphicConductor<R> = Conductor<R>;
pub type AnisomorphicConductor<R> = Conductor<Vec3<R>>;
impl<V> Conductor<V> {
pub fn new(conductivity: V) -> Self {
Self {
conductivity: Vec3::uniform(conductivity).cast()
}
}
pub fn new_anisotropic<R2: Real>(conductivity: Vec3<R2>) -> Self {
Self {
conductivity: conductivity.cast(),
conductivity
}
}
}
impl<R: Real> Material<R> for Conductor<R> {
impl<R: Real> Into<AnisomorphicConductor<R>> for IsomorphicConductor<R> {
fn into(self) -> AnisomorphicConductor<R> {
AnisomorphicConductor::new(Vec3::uniform(self.conductivity))
}
}
impl<R: Real> AnisomorphicConductor<R> {
pub fn new_anisotropic<R2: Real>(c: Vec3<R2>) -> Self {
Self {
conductivity: Vec3::new(c.x().cast(), c.y().cast(), c.z().cast())
}
}
}
impl<R: Real> Material<R> for AnisomorphicConductor<R> {
fn step_parameters_mut<'a>(&'a mut self) -> StepParametersMut<'a, R> {
StepParametersMut::default().with_conductivity(self.conductivity)
}
}
impl<R: Real> Material<R> for IsomorphicConductor<R> {
fn step_parameters_mut<'a>(&'a mut self) -> StepParametersMut<'a, R> {
StepParametersMut::default().with_conductivity(Vec3::uniform(self.conductivity))
}
}
/// Material which can be magnetized, but has no hysteresis and no coercivity.
#[derive(Copy, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct LinearMagnet<R> {

View File

@@ -1,7 +1,7 @@
use crate::real::Real;
use serde::{Serialize, Deserialize};
#[derive(Default, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MHPgram<R> {
/// X coordinate at which M is always zero.
pub h_intercept: R,
@@ -16,3 +16,18 @@ impl<R: Real> MHPgram<R> {
Self { h_intercept, mu_r, max_m }
}
}
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Ferroxcube3R1MH;
impl Ferroxcube3R1MH {
pub fn new() -> Self {
Self::default()
}
}
impl<R: Real> Into<MHPgram<R>> for Ferroxcube3R1MH {
fn into(self) -> MHPgram<R> {
MHPgram::new(R::from_f32(25.0), R::from_f32(881.33), R::from_f32(44000.0))
}
}

View File

@@ -110,7 +110,7 @@ impl<R: Real> Material<R> for Pml<R> {
// #[enum_dispatch(Material)]
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum GenericMaterial<R> {
Conductor(Conductor<R>),
Conductor(AnisomorphicConductor<R>),
LinearMagnet(LinearMagnet<R>),
Pml(Pml<R>),
MBFerromagnet(MBFerromagnet<R>),
@@ -120,16 +120,22 @@ pub enum GenericMaterial<R> {
impl<R: Real> Default for GenericMaterial<R> {
fn default() -> Self {
Conductor::default().into()
Self::Conductor(Default::default())
}
}
impl<R> From<Conductor<R>> for GenericMaterial<R> {
fn from(inner: Conductor<R>) -> Self {
impl<R> From<AnisomorphicConductor<R>> for GenericMaterial<R> {
fn from(inner: AnisomorphicConductor<R>) -> Self {
Self::Conductor(inner)
}
}
impl<R: Real> From<IsomorphicConductor<R>> for GenericMaterial<R> {
fn from(inner: IsomorphicConductor<R>) -> Self {
Self::Conductor(inner.into())
}
}
impl<R> From<LinearMagnet<R>> for GenericMaterial<R> {
fn from(inner: LinearMagnet<R>) -> Self {
Self::LinearMagnet(inner)

View File

@@ -1679,7 +1679,7 @@ mod test {
#[test]
fn conductor_dissipates_energy() {
let (energy_0, energy_1) = conductor_test(Conductor::new(1e3));
let (energy_0, energy_1) = conductor_test(Conductor::new(R64::from_f32(1e3)));
assert_float_eq!(energy_1/energy_0, 0.0, abs <= 1e-6);
}
@@ -1734,8 +1734,8 @@ mod test {
let timestep = state.timestep();
state.fill_boundary_using(size/4, |boundary_ness| {
let b = boundary_ness.elem_pow(3.0);
let conductivity = Vec3::uniform(f32::eps0() * b.mag() * 0.5 / timestep);
Conductor::new_anisotropic(conductivity)
let conductivity = f32::eps0() * b.mag() * 0.5 / timestep;
Conductor::new(conductivity.cast())
});
state
}

View File

@@ -2,14 +2,14 @@ use serde::de::Deserializer;
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use crate::mat::{Conductor, MaterialExt as _, MBFerromagnet, MBPgram, MHPgram, Static};
use crate::mat::{AnisomorphicConductor, IsomorphicConductor, Ferroxcube3R1MH, MaterialExt as _, MBFerromagnet, MBPgram, MHPgram, Static};
use crate::geom::{Index, Vec3, Vec3u};
/// hide the actual spirv backend structures inside a submodule to make their use/boundary clear.
mod ffi {
pub use spirv_backend_lib::sim::SerializedSimMeta;
pub use spirv_backend_lib::support::{Optional, Vec3Std, UVec3Std};
pub use spirv_backend_lib::mat::{FullyGenericMaterial, Material, MBPgram, MHPgram};
pub use spirv_backend_lib::mat::{Ferroxcube3R1MH, FullyGenericMaterial, Material, MBPgram, MHPgram};
}
// conversion traits for types defined cross-lib
@@ -100,6 +100,28 @@ impl IntoLib for ffi::MHPgram {
}
}
impl IntoFfi for Ferroxcube3R1MH {
type Ffi = ffi::Ferroxcube3R1MH;
fn into_ffi(self) -> Self::Ffi {
let ffi_self = ffi::Ferroxcube3R1MH;
let ffi_curve: ffi::MHPgram = ffi_self.into();
let lib_curve: MHPgram<f32> = self.into();
assert_eq!(lib_curve.into_ffi(), ffi_curve);
ffi_self
}
}
impl IntoLib for ffi::Ferroxcube3R1MH {
type Lib = Ferroxcube3R1MH;
fn into_lib(self) -> Self::Lib {
let lib_self = Ferroxcube3R1MH;
let lib_curve: MHPgram<f32> = lib_self.into();
let ffi_curve: ffi::MHPgram = self.into();
assert_eq!(ffi_curve.into_lib(), lib_curve);
lib_self
}
}
#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct FullyGenericMaterial {
pub conductivity: Vec3<f32>,
@@ -138,8 +160,17 @@ impl From<Static<f32>> for FullyGenericMaterial {
}
}
impl From<Conductor<f32>> for FullyGenericMaterial {
fn from(m: Conductor<f32>) -> Self {
impl From<AnisomorphicConductor<f32>> for FullyGenericMaterial {
fn from(m: AnisomorphicConductor<f32>) -> Self {
FullyGenericMaterial {
conductivity: m.conductivity(),
.. Default::default()
}
}
}
impl From<IsomorphicConductor<f32>> for FullyGenericMaterial {
fn from(m: IsomorphicConductor<f32>) -> Self {
FullyGenericMaterial {
conductivity: m.conductivity(),
.. Default::default()
@@ -165,6 +196,13 @@ impl From<MHPgram<f32>> for FullyGenericMaterial {
}
}
impl From<Ferroxcube3R1MH> for FullyGenericMaterial {
fn from(m: Ferroxcube3R1MH) -> Self {
let curve: MHPgram<f32> = m.into();
curve.into()
}
}
#[derive(Clone, Default, Serialize, Deserialize)]
pub struct SimMeta {
pub(crate) dim: Index,

View File

@@ -4,6 +4,7 @@
register_attr(spirv),
no_std
)]
#![feature(const_fn_floating_point_arithmetic)]
extern crate spirv_std;

View File

@@ -1,5 +1,7 @@
use crate::support::{Optional, Vec3Std};
use core::marker::PhantomData;
pub trait Material: Sized {
fn conductivity(&self) -> Vec3Std {
Default::default()
@@ -88,7 +90,7 @@ impl Material for MBPgram {
}
}
#[derive(Copy, Clone, Default, PartialEq)]
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct MHPgram {
/// optimized form of mu_0^-1 * (1-mu_r^-1)
b_mult: f32,
@@ -101,7 +103,7 @@ pub struct MHPgram {
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 fn new(h_intercept: f32, mu_r: f32, max_m: f32) -> Self {
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 {
@@ -198,6 +200,61 @@ impl Material for FullyGenericMaterial {
}
}
/// 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
value: f32,
_mat: PhantomData<M>,
}
impl<M> 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 + Default> 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 {
M::default().move_b_vec(m, target_b)
} else {
m
}
}
}
#[cfg(test)]
mod test {
use super::*;