diff --git a/crates/coremem/src/sim/spirv/mod.rs b/crates/coremem/src/sim/spirv/mod.rs index 20b02df..ce3f754 100644 --- a/crates/coremem/src/sim/spirv/mod.rs +++ b/crates/coremem/src/sim/spirv/mod.rs @@ -273,7 +273,6 @@ mod test { use crate::mat::{self, AnisomorphicConductor}; use crate::meas::Energy; use crate::real::{R32, ToFloat as _}; - use crate::sim::legacy::{self, SimState}; use crate::stim::{ self, ModulatedVectorField, @@ -300,7 +299,9 @@ mod test { m.cast() } - fn assert_simstate_eq> + Send + Sync>(real_state: &SpirvSim, B>, ref_state: &SimState) { + fn assert_simstate_eq, S1: AbstractSim>( + real_state: &S0, ref_state: &S1 + ) { let rel_threshold = 1e-2f32.to_r32(); let mean_e = mean_magnitude_e(ref_state); let mean_h = mean_magnitude_h(ref_state); @@ -387,7 +388,7 @@ mod test { mod backend_agnostic { use super::*; - use crate::stim::{Fields, NoopStimulus, RngStimulus}; + use crate::stim::RngStimulus; pub fn do_smoke_small> + Send + Sync + Default>() { let mut state = SpirvSim::new_with_backend(Index::new(8, 8, 8), 1e-3, B::default()); state.step(); @@ -428,139 +429,6 @@ mod test { state.step(); } - fn test_same_explicit> + Send + Sync + Default>( - mut ref_state: SimState, - mut dut_state: SpirvSim, B>, - step_iters: u64, - steps_per_iter: u32 - ) { - for _ in 0..step_iters { - ref_state.step_multiple(steps_per_iter, &NoopStimulus); - dut_state.step_multiple(steps_per_iter, &NoopStimulus); - assert_simstate_eq(&dut_state, &ref_state); - } - } - - fn test_same> + Send + Sync + Default>(seed: u64, step_iters: u64, steps_per_iter: u32, size: Index) { - let mut cpu_state = SimState::new(size, 1e-3); - cpu_state.apply_stimulus(&RngStimulus::new(seed)); - - let mut spirv_state = SpirvSim::new_with_backend(size, 1e-3, B::default()); - spirv_state.apply_stimulus(&RngStimulus::new(seed)); - - test_same_explicit(cpu_state, spirv_state, step_iters, steps_per_iter); - } - - pub fn do_same_no_step_no_stim> + Send + Sync + Default>() { - let ref_state = SimState::new(Index::new(8, 8, 8), 1e-3); - let dut_state = SpirvSim::new_with_backend(Index::new(8, 8, 8), 1e-3, B::default()); - test_same_explicit(ref_state, dut_state, 1, 1); - } - - pub fn do_same_1_step> + Send + Sync + Default>() { - test_same::(0x1234, 1, 1, Index::new(4, 4, 4)); - } - - pub fn do_same_1000_step> + Send + Sync + Default>() { - test_same::(0x1234, 1000, 1, Index::new(4, 4, 4)); - } - - pub fn do_same_not_multiple_of_4> + Send + Sync + Default>() { - test_same::(0x1234, 100, 1, Index::new(3, 2, 5)); - } - - pub fn do_same_100_steps_larger> + Send + Sync + Default>() { - test_same::(0x1234, 100, 1, Index::new(24, 20, 44)); - } - - pub fn do_same_100_steps_of_10> + Send + Sync + Default>() { - test_same::(0x1234, 100, 10, Index::new(24, 20, 44)); - } - - fn test_same_conductor> + Send + Sync + Default>( - seed: u64, step_iters: u64, steps_per_iter: u32, size: Index - ) { - use rand::{Rng as _, SeedableRng as _}; - let mut rng = rand::rngs::StdRng::seed_from_u64(seed); - let mut ref_state = SimState::new(size, 1e-3); - let mut dut_state = SpirvSim::new_with_backend(size, 1e-3, B::default()); - - for z in 0..size.z() { - for y in 0..size.y() { - for x in 0..size.x() { - let cond = Vec3::new( - rng.gen_range(0.0..1e6), - 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())); - } - } - } - - ref_state.apply_stimulus(&RngStimulus::new(seed)); - dut_state.apply_stimulus(&RngStimulus::new(seed)); - - test_same_explicit(ref_state, dut_state, step_iters, steps_per_iter); - } - - pub fn do_conductor_1_step> + Send + Sync + Default>() { - test_same_conductor::(0x1234, 1, 1, Index::new(4, 4, 4)); - } - - pub fn do_conductor_many_steps_larger> + Send + Sync + Default>() { - test_same_conductor::(0x1234, 100, 10, Index::new(96, 16, 8)); - } - - fn test_same_mb_ferromagnet> + Send + Sync + Default>( - seed: u64, step_iters: u64, steps_per_iter: u32, size: Index - ) { - use rand::{Rng as _, SeedableRng as _}; - let mut rng = rand::rngs::StdRng::seed_from_u64(seed); - let mut ref_state = SimState::new(size, 1e-3); - let mut dut_state = SpirvSim::new_with_backend(size, 1e-3, B::default()); - - for z in 0..size.z() { - for y in 0..size.y() { - for x in 0..size.x() { - let b_start = rng.gen_range(-1e-4..1e-4); - // if b_start is negative, we need the endpoint to be > |b_start|: - let b_range = (-2.0f32*b_start).max(0.0) + rng.gen_range(1e-5..1e-2); - // It would be weird for an increase in B to trigger a DECREASE in H. - // We have mu0(M+H) = B, so - // mu0(dM/dB = dH/dB) = 1 - // -> dH/dB = mu0^-1 - dM/dB > 0 - let slope = rng.gen_range(0.0..0.9*f32::mu0_inv()); - let b_end = b_start + b_range; - let m_max = b_range * slope; - ref_state.put_material(Index::new(x, y, z), legacy::mat::MBPgram::new( - b_start.cast(), b_end.cast(), m_max.cast() - )); - dut_state.put_material(Index::new(x, y, z), coremem_cross::mat::MBPgram::new( - b_start.cast(), b_end.cast(), m_max.cast() - )); - } - } - } - - // XXX H stimulus on the ferromagnet behaves differently on the CPU than GPU - // the CPU implementation is the more suspect one here (it doesn't consider material - // parameters). - ref_state.apply_stimulus(&RngStimulus::new_e(seed)); - dut_state.apply_stimulus(&RngStimulus::new_e(seed)); - - test_same_explicit(ref_state, dut_state, step_iters, steps_per_iter); - } - - pub fn do_mb_ferromagnet_1_step> + Send + Sync + Default>() { - test_same_mb_ferromagnet::(0x1234, 1, 1, Index::new(4, 4, 4)); - } - - pub fn do_mb_ferromagnet_100_steps_larger> + Send + Sync + Default>() { - test_same_mb_ferromagnet::(0x1234, 10, 10, Index::new(96, 16, 8)); - } - // XXX these tests probably failed because they were allowing negative mu_r values // #[test] // fn mb_ferromagnet_diff_repro() { @@ -639,18 +507,6 @@ mod test { pub fn do_mh_ferromagnet_smoke_100_steps_larger> + Send + Sync + Default>() { test_smoke_mh_ferromagnet::(0x1234, 100, Index::new(328, 252, 160)); } - - pub fn do_step_multiple_with_stim> + Send + Sync + Default>() { - let size = Index::new(4, 12, 8); - let mut ref_state = SimState::new(size, 1e-3); - let mut dut_state = SpirvSim::new_with_backend(size, 1e-3, B::default()); - let stim = Fields::new_e(Vec3::new(1.0e15, 2.0e15, -3.0e15).cast::()); - for _ in 0..5 { - ref_state.step_multiple(100, &stim); - dut_state.step_multiple(100, &stim); - assert_simstate_eq(&dut_state, &ref_state); - } - } } macro_rules! test_backend { @@ -791,46 +647,6 @@ mod test { do_smoke_not_multiple_of_4::<$backend>(); } #[test] - fn same_no_step_no_stim() { - do_same_no_step_no_stim::<$backend>(); - } - #[test] - fn same_1_step() { - do_same_1_step::<$backend>(); - } - #[test] - fn same_1000_step() { - do_same_1000_step::<$backend>(); - } - #[test] - fn same_not_multiple_of_4() { - do_same_not_multiple_of_4::<$backend>(); - } - #[test] - fn same_100_steps_larger() { - do_same_100_steps_larger::<$backend>(); - } - #[test] - fn same_100_steps_of_10() { - do_same_100_steps_of_10::<$backend>(); - } - #[test] - fn conductor_1_step() { - do_conductor_1_step::<$backend>(); - } - #[test] - fn conductor_many_steps_larger() { - do_conductor_many_steps_larger::<$backend>(); - } - #[test] - fn mb_ferromagnet_1_step() { - do_mb_ferromagnet_1_step::<$backend>(); - } - #[test] - fn mb_ferromagnet_100_steps_larger() { - do_mb_ferromagnet_100_steps_larger::<$backend>(); - } - #[test] fn mh_ferromagnet_smoke_1_step() { do_mh_ferromagnet_smoke_1_step::<$backend>(); } @@ -838,14 +654,171 @@ mod test { fn mh_ferromagnet_smoke_100_steps_larger() { do_mh_ferromagnet_smoke_100_steps_larger::<$backend>(); } - #[test] - fn step_multiple_with_stim() { - do_step_multiple_with_stim::<$backend>(); - } } } } test_backend!(cpu, CpuBackend); test_backend!(wgpu, WgpuBackend); + + /// sanity checks that cpu and wgpu backends give *similar* results + mod same { + use super::*; + use crate::stim::{NoopStimulus, RngStimulus}; + fn test_same_explicit( + mut cpu_state: SpirvSim, CpuBackend>, + mut wgpu_state: SpirvSim, WgpuBackend>, + step_iters: u64, + steps_per_iter: u32 + ) { + for _ in 0..step_iters { + cpu_state.step_multiple(steps_per_iter, &NoopStimulus); + wgpu_state.step_multiple(steps_per_iter, &NoopStimulus); + assert_simstate_eq(&wgpu_state, &cpu_state); + } + } + fn test_same(seed: u64, step_iters: u64, steps_per_iter: u32, size: Index) { + let mut cpu_state = SpirvSim::new_with_backend(size, 1e-3, CpuBackend::default()); + cpu_state.apply_stimulus(&RngStimulus::new(seed)); + + let mut wgpu_state = SpirvSim::new_with_backend(size, 1e-3, WgpuBackend::default()); + wgpu_state.apply_stimulus(&RngStimulus::new(seed)); + + test_same_explicit(cpu_state, wgpu_state, step_iters, steps_per_iter); + } + + #[test] + fn same_no_step_no_stim() { + let cpu_state = SpirvSim::new_with_backend(Index::new(8, 8, 8), 1e-3, CpuBackend::default()); + let wgpu_state = SpirvSim::new_with_backend(Index::new(8, 8, 8), 1e-3, WgpuBackend::default()); + test_same_explicit(cpu_state, wgpu_state, 1, 1); + } + + #[test] + fn do_same_1_step() { + test_same(0x1234, 1, 1, Index::new(4, 4, 4)); + } + + #[test] + fn same_1000_step() { + test_same(0x1234, 1000, 1, Index::new(4, 4, 4)); + } + + #[test] + fn same_not_multiple_of_4() { + test_same(0x1234, 100, 1, Index::new(3, 2, 5)); + } + + #[test] + fn same_100_steps_larger() { + test_same(0x1234, 100, 1, Index::new(24, 20, 44)); + } + + #[test] + fn same_100_steps_of_10() { + test_same(0x1234, 100, 10, Index::new(24, 20, 44)); + } + + fn test_same_conductor( + seed: u64, step_iters: u64, steps_per_iter: u32, size: Index + ) { + use rand::{Rng as _, SeedableRng as _}; + let mut rng = rand::rngs::StdRng::seed_from_u64(seed); + let mut ref_state = SpirvSim::new_with_backend(size, 1e-3, CpuBackend::default()); + let mut dut_state = SpirvSim::new_with_backend(size, 1e-3, WgpuBackend::default()); + + for z in 0..size.z() { + for y in 0..size.y() { + for x in 0..size.x() { + let cond = Vec3::new( + rng.gen_range(0.0..1e6), + 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())); + } + } + } + + // TODO: we should apply this on every frame? + ref_state.apply_stimulus(&RngStimulus::new(seed)); + dut_state.apply_stimulus(&RngStimulus::new(seed)); + + test_same_explicit(ref_state, dut_state, step_iters, steps_per_iter); + } + + #[test] + fn conductor_1_step() { + test_same_conductor(0x1234, 1, 1, Index::new(4, 4, 4)); + } + + #[test] + fn conductor_many_steps_larger() { + test_same_conductor(0x1234, 100, 10, Index::new(96, 16, 8)); + } + + fn test_same_mb_ferromagnet( + seed: u64, step_iters: u64, steps_per_iter: u32, size: Index + ) { + use rand::{Rng as _, SeedableRng as _}; + let mut rng = rand::rngs::StdRng::seed_from_u64(seed); + let mut ref_state = SpirvSim::new_with_backend(size, 1e-3, CpuBackend::default()); + let mut dut_state = SpirvSim::new_with_backend(size, 1e-3, WgpuBackend::default()); + + for z in 0..size.z() { + for y in 0..size.y() { + for x in 0..size.x() { + let b_start = rng.gen_range(-1e-4..1e-4); + // if b_start is negative, we need the endpoint to be > |b_start|: + let b_range = (-2.0f32*b_start).max(0.0) + rng.gen_range(1e-5..1e-2); + // It would be weird for an increase in B to trigger a DECREASE in H. + // We have mu0(M+H) = B, so + // mu0(dM/dB = dH/dB) = 1 + // -> dH/dB = mu0^-1 - dM/dB > 0 + let slope = rng.gen_range(0.0..0.9*f32::mu0_inv()); + let b_end = b_start + b_range; + let m_max = b_range * slope; + ref_state.put_material(Index::new(x, y, z), coremem_cross::mat::MBPgram::new( + b_start.cast(), b_end.cast(), m_max.cast() + )); + dut_state.put_material(Index::new(x, y, z), coremem_cross::mat::MBPgram::new( + b_start.cast(), b_end.cast(), m_max.cast() + )); + } + } + } + + // TODO: use new (not new_e) + ref_state.apply_stimulus(&RngStimulus::new_e(seed)); + dut_state.apply_stimulus(&RngStimulus::new_e(seed)); + + test_same_explicit(ref_state, dut_state, step_iters, steps_per_iter); + } + + #[test] + fn mb_ferromagnet_1_step() { + test_same_mb_ferromagnet(0x1234, 1, 1, Index::new(4, 4, 4)); + } + + #[test] + fn mb_ferromagnet_100_steps_larger() { + test_same_mb_ferromagnet(0x1234, 10, 10, Index::new(96, 16, 8)); + } + + #[test] + fn step_multiple_with_stim() { + let size = Index::new(4, 12, 8); + let mut ref_state: SpirvSim, CpuBackend> = + SpirvSim::new(size, 1e-3); + let mut dut_state: SpirvSim, WgpuBackend> = + SpirvSim::new(size, 1e-3); + let stim = stim::Fields::new_e(Vec3::new(1.0e15, 2.0e15, -3.0e15).cast::()); + for _ in 0..5 { + ref_state.step_multiple(100, &stim); + dut_state.step_multiple(100, &stim); + assert_simstate_eq(&dut_state, &ref_state); + } + } + } }