coremem_types: IsomorphicConductor, AnisomorphicConductor are now used by both spirv and cpu impls

This commit is contained in:
2022-07-19 02:08:22 -07:00
parent 78f7e2be45
commit f8fccd957a
10 changed files with 215 additions and 148 deletions

View File

@@ -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<Ferroxcube3R1MH>;
type Mat = IsoConductorOr<f32, Ferroxcube3R1MH>;
#[allow(unused)]
use coremem::geom::{Coord as _, Region as _};

View File

@@ -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<Ferroxcube3R1MH>;
type Mat = IsoConductorOr<f32, Ferroxcube3R1MH>;
fn main() {
coremem::init_logging();

View File

@@ -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<Ferroxcube3R1MH>;
type Mat = IsoConductorOr<f32, Ferroxcube3R1MH>;
for size in &[10, 20, 40, 80, 160] {
let sim: SpirvSim<Mat> = SpirvSim::new(Index::new(*size, *size, *size), 1e-5);
c.bench_with_input(BenchmarkId::new("Driver::spirv_ISO3R1", size), &sim, |b, sim| {

View File

@@ -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<V> {
pub conductivity: V,
}
pub type IsomorphicConductor<R> = Conductor<(R,)>;
pub type AnisomorphicConductor<R> = Conductor<Vec3<R>>;
impl<V> IsomorphicConductor<V> {
pub fn new(conductivity: V) -> Self {
Self {
conductivity: (conductivity,)
}
}
}
impl<V: Clone> IsomorphicConductor<V> {
pub fn iso_conductivity(&self) -> V {
self.conductivity.0.clone()
}
}
impl<R> AnisomorphicConductor<R> {
pub fn new(conductivity: Vec3<R>) -> Self {
Self {
conductivity
}
}
}
impl<R: Real> Into<AnisomorphicConductor<R>> for IsomorphicConductor<R> {
fn into(self) -> AnisomorphicConductor<R> {
AnisomorphicConductor::new(Vec3::uniform(self.conductivity.0))
}
}
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.0))
}
}
/// 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

@@ -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<R: Real> Material<R> for GenericMaterialOneField<R> {
}
}
}
// coremem_types adapters
impl<R: Real> Material<R> for AnisomorphicConductor<R> {
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<R: Real> Material<R> for IsomorphicConductor<R> {
fn step_parameters_mut<'a>(&'a mut self) -> StepParametersMut<'a, R> {
let c = coremem_types::mat::Material::conductivity(self);
StepParametersMut::default().with_conductivity(c)
}
}

View File

@@ -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<f32> {
identity!( => MHPgram<f32>);
identity!( => Ferroxcube3R1MH);
identity!(R, M, => IsoConductorOr<R, M>);
#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct FullyGenericMaterial {
@@ -170,52 +171,6 @@ impl From<Ferroxcube3R1MH> for FullyGenericMaterial {
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct IsoConductorOr<M> {
value: f32,
mat: M,
}
impl<L: IntoFfi> IntoFfi for IsoConductorOr<L> {
type Ffi = ffi::IsoConductorOr<L::Ffi>;
fn into_ffi(self) -> Self::Ffi {
Self::Ffi {
value: self.value,
mat: self.mat.into_ffi(),
}
}
}
impl<F: IntoLib> IntoLib for ffi::IsoConductorOr<F> {
type Lib = IsoConductorOr<F::Lib>;
fn into_lib(self) -> Self::Lib {
Self::Lib {
value: self.value,
mat: self.mat.into_lib(),
}
}
}
impl<M: Default> From<IsomorphicConductor<f32>> for IsoConductorOr<M> {
fn from(m: IsomorphicConductor<f32>) -> 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<Ferroxcube3R1MH> for IsoConductorOr<Ferroxcube3R1MH> {
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 {

View File

@@ -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);
}

View File

@@ -25,41 +25,5 @@ impl Material<f32> 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<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<f32> + Copy> Material<f32> for IsoConductorOr<M> {
fn conductivity(&self) -> Vec3<f32> {
Vec3::uniform(self.value.max(0.0))
}
fn move_b_vec(&self, m: Vec3<f32>, target_b: Vec3<f32>) -> Vec3<f32> {
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<M> = coremem_types::mat::IsoConductorOr<f32, M>;

View File

@@ -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>(T);
pub type AnisomorphicConductor<R> = Conductor<Vec3<R>>;
pub type IsomorphicConductor<R> = Conductor<R>;
pub type IsomorphicConductor<R> = Conductor<(R,)>;
impl<R> IsomorphicConductor<R> {
pub fn new(c: R) -> Self {
Self((c,))
}
}
impl<V: Clone> IsomorphicConductor<V> {
pub fn iso_conductivity(&self) -> V {
self.0.0.clone()
}
}
impl<R> AnisomorphicConductor<R> {
pub fn new(c: Vec3<R>) -> Self {
Self(c)
}
}
impl<R: Real> Into<AnisomorphicConductor<R>> for IsomorphicConductor<R> {
fn into(self) -> AnisomorphicConductor<R> {
AnisomorphicConductor::new(Vec3::uniform(self.iso_conductivity()))
}
}
impl<R: Real> Material<R> for AnisomorphicConductor<R> {
fn conductivity(&self) -> Vec3<R> {
@@ -20,7 +44,20 @@ impl<R: Real> Material<R> for AnisomorphicConductor<R> {
impl<R: Real> Material<R> for IsomorphicConductor<R> {
fn conductivity(&self) -> Vec3<R> {
Vec3::uniform(self.0)
Vec3::uniform(self.iso_conductivity())
}
}
impl<R: Real> Discriminable for IsomorphicConductor<R> {
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)))
}
}

View File

@@ -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<R: Real>: Sized {
fn conductivity(&self) -> Vec3<R> {
Default::default()
@@ -19,3 +23,144 @@ pub trait Material<R: Real>: 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<T>(pub T);
pub struct D1<T>(pub T);
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "fmt", derive(Debug))]
#[derive(Copy, Clone, Default, PartialEq)]
pub struct DiscrMat2<M0, M1> {
m0: M0,
m1: M1,
}
impl<M0, M1> DiscrMat2<M0, M1> {
fn new_from_fields(m0: M0, m1: M1) -> Self {
Self { m0, m1 }
}
}
impl<M0: Discriminable, M1: Default> DiscrMat2<M0, M1> {
pub fn new0(m: M0) -> Self {
D0(m).into()
}
pub fn new1(m: M1) -> Self {
D1(m).into()
}
}
impl<M0: Discriminable, M1: Default> From<D0<M0>> for DiscrMat2<M0, M1> {
fn from(m0: D0<M0>) -> Self {
let m0 = m0.0;
assert!(m0.discr() == 0, "unexpected non-zero discriminant");
Self::new_from_fields(m0, Default::default())
}
}
impl<M0: Discriminable, M1> From<D1<M1>> for DiscrMat2<M0, M1> {
fn from(m1: D1<M1>) -> Self {
let m1 = m1.0;
Self::new_from_fields(M0::make_discr(1), m1)
}
}
impl<R: Real, M0: Discriminable + Material<R>, M1: Material<R> + Copy> Material<R> for DiscrMat2<M0, M1> {
fn conductivity(&self) -> Vec3<R> {
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<R>, target_b: Vec3<R>) -> Vec3<R> {
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<R, M1> = DiscrMat2<IsomorphicConductor<R>, M1>;
impl<R: Real, M1: Default> IsoConductorOr<R, M1> {
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<R: Real> From<Ferroxcube3R1MH> for IsoConductorOr<R, Ferroxcube3R1MH> {
fn from(mat: Ferroxcube3R1MH) -> Self {
IsoConductorOr::new1(mat)
}
}
impl<R: Real> From<IsomorphicConductor<R>> for IsoConductorOr<R, Ferroxcube3R1MH> {
fn from(mat: IsomorphicConductor<R>) -> Self {
IsoConductorOr::new0(mat)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::real::R32;
#[test]
fn iso_conductor_discr() {
type T = IsomorphicConductor<f32>;
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<f32, Ferroxcube3R1MH> = IsomorphicConductor::new(22.0f32).into();
assert!(c.conductivity() == Vec3::uniform(22.0f32));
let c: IsoConductorOr<f32, Ferroxcube3R1MH> = Ferroxcube3R1MH::new().into();
assert!(c.conductivity() == Vec3::zero());
}
#[test]
fn iso_conductor_or_aniso() {
type I = IsoConductorOr<R32, AnisomorphicConductor<R32>>;
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());
}
}