diff --git a/crates/applications/buffer_proto5/src/main.rs b/crates/applications/buffer_proto5/src/main.rs index 15da818..cd53344 100644 --- a/crates/applications/buffer_proto5/src/main.rs +++ b/crates/applications/buffer_proto5/src/main.rs @@ -4,7 +4,7 @@ use coremem::{Driver, mat, meas, SpirvDriver}; use coremem::geom::{region, Cube, Dilate, Memoize, Meters, Region, Spiral, SwapYZ, Torus, Translate, Wrap}; -use coremem::mat::Ferroxcube3R1MH; +use coremem::mat::{Ferroxcube3R1MH, IsoConductorOr}; use coremem::real::{R32, Real as _}; use coremem::render::CsvRenderer; use coremem::stim::{CurlStimulus, Exp1, Gated, Sinusoid1, TimeVarying as _}; @@ -14,7 +14,7 @@ use coremem::util::cache::DiskCache; use log::{error, info, warn}; use serde::{Deserialize, Serialize}; -type Mat = spirv::IsoConductorOr; +type Mat = IsoConductorOr; #[allow(unused)] use coremem::geom::{Coord as _, Region as _}; diff --git a/crates/applications/multi_core_inverter/src/main.rs b/crates/applications/multi_core_inverter/src/main.rs index f8a88ae..ec4da24 100644 --- a/crates/applications/multi_core_inverter/src/main.rs +++ b/crates/applications/multi_core_inverter/src/main.rs @@ -38,11 +38,11 @@ use coremem::geom::{Meters, Torus}; use coremem::sim::units::Seconds; -use coremem::mat::{Ferroxcube3R1MH, IsomorphicConductor}; +use coremem::mat::{Ferroxcube3R1MH, IsoConductorOr, IsomorphicConductor}; use coremem::spirv; use coremem::{Driver, SpirvDriver}; -type Mat = spirv::IsoConductorOr; +type Mat = IsoConductorOr; fn main() { coremem::init_logging(); diff --git a/crates/coremem/benches/driver.rs b/crates/coremem/benches/driver.rs index 79c64c5..49f81e0 100644 --- a/crates/coremem/benches/driver.rs +++ b/crates/coremem/benches/driver.rs @@ -1,6 +1,6 @@ use coremem::{Driver, SimState, SpirvDriver}; use coremem::geom::Index; -use coremem::mat::{Ferroxcube3R1MH, GenericMaterial, GenericMaterialNoPml, GenericMaterialOneField}; +use coremem::mat::{Ferroxcube3R1MH, IsoConductorOr, GenericMaterial, GenericMaterialNoPml, GenericMaterialOneField}; use coremem::real::R32; use coremem::sim::spirv::{self, SpirvSim}; use criterion::{BenchmarkId, criterion_group, criterion_main, Criterion}; @@ -28,7 +28,7 @@ pub fn bench_step_spirv(c: &mut Criterion) { } pub fn bench_step_spirv_iso_3r1(c: &mut Criterion) { - type Mat = spirv::IsoConductorOr; + type Mat = IsoConductorOr; for size in &[10, 20, 40, 80, 160] { let sim: SpirvSim = SpirvSim::new(Index::new(*size, *size, *size), 1e-5); c.bench_with_input(BenchmarkId::new("Driver::spirv_ISO3R1", size), &sim, |b, sim| { diff --git a/crates/coremem/src/mat/linear.rs b/crates/coremem/src/mat/linear.rs index 9550e2b..cd74be9 100644 --- a/crates/coremem/src/mat/linear.rs +++ b/crates/coremem/src/mat/linear.rs @@ -2,58 +2,9 @@ use crate::CellState; use crate::geom::Vec3; use crate::mat::Material; use crate::real::Real; -use crate::sim::StepParametersMut; use serde::{Serialize, Deserialize}; -/// Material which has a conductivity parameter, but cannot be magnetized -#[derive(Copy, Clone, Default, PartialEq, Serialize, Deserialize)] -pub struct Conductor { - pub conductivity: V, -} - -pub type IsomorphicConductor = Conductor<(R,)>; -pub type AnisomorphicConductor = Conductor>; - -impl IsomorphicConductor { - pub fn new(conductivity: V) -> Self { - Self { - conductivity: (conductivity,) - } - } -} -impl IsomorphicConductor { - pub fn iso_conductivity(&self) -> V { - self.conductivity.0.clone() - } -} - -impl AnisomorphicConductor { - pub fn new(conductivity: Vec3) -> Self { - Self { - conductivity - } - } -} - -impl Into> for IsomorphicConductor { - fn into(self) -> AnisomorphicConductor { - AnisomorphicConductor::new(Vec3::uniform(self.conductivity.0)) - } -} - -impl Material for AnisomorphicConductor { - fn step_parameters_mut<'a>(&'a mut self) -> StepParametersMut<'a, R> { - StepParametersMut::default().with_conductivity(self.conductivity) - } -} - -impl Material for IsomorphicConductor { - fn step_parameters_mut<'a>(&'a mut self) -> StepParametersMut<'a, R> { - StepParametersMut::default().with_conductivity(Vec3::uniform(self.conductivity.0)) - } -} - /// Material which can be magnetized, but has no hysteresis and no coercivity. #[derive(Copy, Clone, Default, PartialEq, Serialize, Deserialize)] pub struct LinearMagnet { diff --git a/crates/coremem/src/mat/mod.rs b/crates/coremem/src/mat/mod.rs index 7d3c900..c7f0baa 100644 --- a/crates/coremem/src/mat/mod.rs +++ b/crates/coremem/src/mat/mod.rs @@ -13,7 +13,7 @@ mod linear; pub use bh_ferromagnet::*; pub use mb_ferromagnet::*; -pub use coremem_types::mat::{MHPgram, Ferroxcube3R1MH}; +pub use coremem_types::mat::{AnisomorphicConductor, Ferroxcube3R1MH, IsoConductorOr, IsomorphicConductor, MHPgram}; pub use linear::*; #[enum_dispatch] @@ -311,3 +311,17 @@ impl Material for GenericMaterialOneField { } } } + +// coremem_types adapters +impl Material for AnisomorphicConductor { + fn step_parameters_mut<'a>(&'a mut self) -> StepParametersMut<'a, R> { + let c = coremem_types::mat::Material::conductivity(self); + StepParametersMut::default().with_conductivity(c) + } +} +impl Material for IsomorphicConductor { + fn step_parameters_mut<'a>(&'a mut self) -> StepParametersMut<'a, R> { + let c = coremem_types::mat::Material::conductivity(self); + StepParametersMut::default().with_conductivity(c) + } +} diff --git a/crates/coremem/src/sim/spirv/bindings.rs b/crates/coremem/src/sim/spirv/bindings.rs index 59b4a40..5fd671c 100644 --- a/crates/coremem/src/sim/spirv/bindings.rs +++ b/crates/coremem/src/sim/spirv/bindings.rs @@ -2,7 +2,7 @@ use serde::de::Deserializer; use serde::ser::Serializer; use serde::{Deserialize, Serialize}; -use crate::mat::{AnisomorphicConductor, IsomorphicConductor, Ferroxcube3R1MH, MaterialExt as _, MBFerromagnet, MBPgram, MHPgram, Static}; +use crate::mat::{AnisomorphicConductor, IsoConductorOr, 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. @@ -10,7 +10,7 @@ mod ffi { pub use spirv_backend::entry_points; pub use spirv_backend::sim::SerializedSimMeta; pub use spirv_backend::support::Optional; - pub use spirv_backend::mat::{FullyGenericMaterial, IsoConductorOr}; + pub use spirv_backend::mat::FullyGenericMaterial; pub use coremem_types::mat::MBPgram; } @@ -88,6 +88,7 @@ impl IntoLib for ffi::MBPgram { identity!( => MHPgram); identity!( => Ferroxcube3R1MH); +identity!(R, M, => IsoConductorOr); #[derive(Clone, Default, PartialEq, Serialize, Deserialize)] pub struct FullyGenericMaterial { @@ -170,52 +171,6 @@ impl From for FullyGenericMaterial { } } -#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)] -pub struct IsoConductorOr { - value: f32, - mat: M, -} - -impl IntoFfi for IsoConductorOr { - type Ffi = ffi::IsoConductorOr; - fn into_ffi(self) -> Self::Ffi { - Self::Ffi { - value: self.value, - mat: self.mat.into_ffi(), - } - } -} - -impl IntoLib for ffi::IsoConductorOr { - type Lib = IsoConductorOr; - fn into_lib(self) -> Self::Lib { - Self::Lib { - value: self.value, - mat: self.mat.into_lib(), - } - } -} - -impl From> for IsoConductorOr { - fn from(m: IsomorphicConductor) -> Self { - IsoConductorOr { - value: m.iso_conductivity(), - mat: Default::default(), - } - } -} - -// XXX: can't do this for generic M, because that creates duplicate `From` impls for the -// IsomorphicConductor itself -impl From for IsoConductorOr { - fn from(mat: Ferroxcube3R1MH) -> Self { - IsoConductorOr { - value: -1.0, - mat, - } - } -} - // this is bitwise- and type-compatible with the spirv SimMeta, except we need serde traits #[derive(Clone, Default, Serialize, Deserialize)] pub struct SimMeta { diff --git a/crates/coremem/src/sim/spirv/mod.rs b/crates/coremem/src/sim/spirv/mod.rs index 526ae10..233947c 100644 --- a/crates/coremem/src/sim/spirv/mod.rs +++ b/crates/coremem/src/sim/spirv/mod.rs @@ -15,7 +15,7 @@ use crate::stim::AbstractStimulus; use coremem_types::mat::Material; mod bindings; -pub use bindings::{entry_points, IntoFfi, IntoLib, IsoConductorOr, FullyGenericMaterial, Remote, SimMeta}; +pub use bindings::{entry_points, IntoFfi, IntoLib, FullyGenericMaterial, Remote, SimMeta}; /// Wrapper around an inner state object which offloads stepping onto a spirv backend (e.g. GPU). #[derive(Clone, Default, Serialize, Deserialize)] @@ -892,15 +892,16 @@ mod test { rng.gen_range(0.0..1e6), rng.gen_range(0.0..1e6), ); - ref_state.put_material(Index::new(x, y, z), AnisomorphicConductor::new(cond.cast())); - dut_state.put_material(Index::new(x, y, z), AnisomorphicConductor::new(cond.cast())); + let m = AnisomorphicConductor::new(cond.cast()); + ref_state.put_material(Index::new(x, y, z), m); + let m = AnisomorphicConductor::new(cond.cast()); + dut_state.put_material(Index::new(x, y, z), m); } } } ref_state.apply_stimulus(&RngStimulus::new(seed)); dut_state.apply_stimulus(&RngStimulus::new(seed)); - test_same_explicit(ref_state, dut_state, steps); } diff --git a/crates/spirv_backend/src/mat.rs b/crates/spirv_backend/src/mat.rs index e2c319b..028ca6f 100644 --- a/crates/spirv_backend/src/mat.rs +++ b/crates/spirv_backend/src/mat.rs @@ -25,41 +25,5 @@ impl Material for FullyGenericMaterial { } } -/// 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 { - /// conductivity, if >= 0, else ignored & used to signal the other material - pub value: f32, - pub mat: M, -} - -impl IsoConductorOr { - 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 + Copy> Material for IsoConductorOr { - fn conductivity(&self) -> Vec3 { - Vec3::uniform(self.value.max(0.0)) - } - fn move_b_vec(&self, m: Vec3, target_b: Vec3) -> Vec3 { - if self.value < 0.0 { - let mat = self.mat; //< XXX hack for ZST - mat.move_b_vec(m, target_b) - } else { - m - } - } -} +pub type IsoConductorOr = coremem_types::mat::IsoConductorOr; diff --git a/crates/types/src/mat/conductor.rs b/crates/types/src/mat/conductor.rs index 76eb91d..039b8d3 100644 --- a/crates/types/src/mat/conductor.rs +++ b/crates/types/src/mat/conductor.rs @@ -1,4 +1,4 @@ -use crate::mat::Material; +use crate::mat::{Discriminable, Material}; use crate::real::Real; use crate::vec::Vec3; @@ -10,7 +10,31 @@ use serde::{Serialize, Deserialize}; #[derive(Copy, Clone, Default, PartialEq)] pub struct Conductor(T); pub type AnisomorphicConductor = Conductor>; -pub type IsomorphicConductor = Conductor; +pub type IsomorphicConductor = Conductor<(R,)>; + +impl IsomorphicConductor { + pub fn new(c: R) -> Self { + Self((c,)) + } +} + +impl IsomorphicConductor { + pub fn iso_conductivity(&self) -> V { + self.0.0.clone() + } +} + +impl AnisomorphicConductor { + pub fn new(c: Vec3) -> Self { + Self(c) + } +} + +impl Into> for IsomorphicConductor { + fn into(self) -> AnisomorphicConductor { + AnisomorphicConductor::new(Vec3::uniform(self.iso_conductivity())) + } +} impl Material for AnisomorphicConductor { fn conductivity(&self) -> Vec3 { @@ -20,7 +44,20 @@ impl Material for AnisomorphicConductor { impl Material for IsomorphicConductor { fn conductivity(&self) -> Vec3 { - Vec3::uniform(self.0) + Vec3::uniform(self.iso_conductivity()) } } +impl Discriminable for IsomorphicConductor { + fn discr(&self) -> u32 { + let cond = self.iso_conductivity(); + if cond < R::zero() { + (-cond.to_f32()) as u32 + } else { + 0 + } + } + fn make_discr(d: u32) -> Self { + Self::new(R::from_primitive(-(d as i32))) + } +} diff --git a/crates/types/src/mat/mod.rs b/crates/types/src/mat/mod.rs index 0cd233e..1b7c0ea 100644 --- a/crates/types/src/mat/mod.rs +++ b/crates/types/src/mat/mod.rs @@ -8,6 +8,10 @@ pub use conductor::{AnisomorphicConductor, IsomorphicConductor}; pub use mb_pgram::MBPgram; pub use mh_pgram::{Ferroxcube3R1MH, MHPgram}; +#[cfg(feature = "serde")] +use serde::{Serialize, Deserialize}; + + pub trait Material: Sized { fn conductivity(&self) -> Vec3 { Default::default() @@ -19,3 +23,144 @@ pub trait Material: Sized { m } } + +/// any type which may hold a discriminant internally. +pub trait Discriminable { + fn discr(&self) -> u32; + fn make_discr(d: u32) -> Self; +} + +pub struct D0(pub T); +pub struct D1(pub T); + +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "fmt", derive(Debug))] +#[derive(Copy, Clone, Default, PartialEq)] +pub struct DiscrMat2 { + m0: M0, + m1: M1, +} + +impl DiscrMat2 { + fn new_from_fields(m0: M0, m1: M1) -> Self { + Self { m0, m1 } + } +} + +impl DiscrMat2 { + pub fn new0(m: M0) -> Self { + D0(m).into() + } + pub fn new1(m: M1) -> Self { + D1(m).into() + } +} + +impl From> for DiscrMat2 { + fn from(m0: D0) -> Self { + let m0 = m0.0; + assert!(m0.discr() == 0, "unexpected non-zero discriminant"); + Self::new_from_fields(m0, Default::default()) + } +} + +impl From> for DiscrMat2 { + fn from(m1: D1) -> Self { + let m1 = m1.0; + Self::new_from_fields(M0::make_discr(1), m1) + } +} + +impl, M1: Material + Copy> Material for DiscrMat2 { + fn conductivity(&self) -> Vec3 { + match self.m0.discr() { + 0 => self.m0.conductivity(), + 1 => { + let mat = self.m1; //< XXX hack for ZST + mat.conductivity() + } + _ => panic!() + // _ => unreachable!(), + } + } + fn move_b_vec(&self, m: Vec3, target_b: Vec3) -> Vec3 { + match self.m0.discr() { + 0 => self.m0.move_b_vec(m, target_b), + 1 => { + let mat = self.m1; //< XXX hack for ZST + mat.move_b_vec(m, target_b) + } + _ => panic!() + // _ => unreachable!(), + } + } +} + +pub type IsoConductorOr = DiscrMat2, M1>; + +impl IsoConductorOr { + pub fn new_conductor(conductivity: R) -> Self { + Self::new0(IsomorphicConductor::new(conductivity)) + } + pub fn new_other(other: M1) -> Self { + Self::new1(other) + } +} + +// XXX: can't do this for generic M, because that creates duplicate `From` impls for the +// IsomorphicConductor itself +impl From for IsoConductorOr { + fn from(mat: Ferroxcube3R1MH) -> Self { + IsoConductorOr::new1(mat) + } +} +impl From> for IsoConductorOr { + fn from(mat: IsomorphicConductor) -> Self { + IsoConductorOr::new0(mat) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::real::R32; + + #[test] + fn iso_conductor_discr() { + type T = IsomorphicConductor; + let c = T::make_discr(5); + assert_eq!(c.discr(), 5); + + let c = T::make_discr(0); + assert_eq!(c.discr(), 0); + + let c = T::new(5.0); + assert_eq!(c.discr(), 0); + + let c = T::new(0.0); + assert_eq!(c.discr(), 0); + } + + #[test] + fn iso_conductor_or_3r1() { + let c: IsoConductorOr = IsomorphicConductor::new(22.0f32).into(); + assert!(c.conductivity() == Vec3::uniform(22.0f32)); + + let c: IsoConductorOr = Ferroxcube3R1MH::new().into(); + assert!(c.conductivity() == Vec3::zero()); + } + + #[test] + fn iso_conductor_or_aniso() { + type I = IsoConductorOr>; + + let c = I::new0(IsomorphicConductor::new(22f32.cast())); + assert!(c.conductivity() == Vec3::uniform(22.0).cast()); + + let c = I::new1(AnisomorphicConductor::new( + Vec3::new(2.0, 3.0, 4.0).cast() + )); + + assert!(c.conductivity() == Vec3::new(2.0, 3.0, 4.0).cast()); + } +}