diff --git a/crates/applications/multi_core_inverter/src/main.rs b/crates/applications/multi_core_inverter/src/main.rs index 9d5246d..e3f66ef 100644 --- a/crates/applications/multi_core_inverter/src/main.rs +++ b/crates/applications/multi_core_inverter/src/main.rs @@ -37,23 +37,30 @@ //! ``` use coremem::geom::{Coord as _, Meters, Torus}; +use coremem::mat::{Ferroxcube3R1MH, IsoConductorOr, IsomorphicConductor}; +use coremem::meas; use coremem::real::{self, Real as _}; use coremem::sim::spirv::{self, SpirvSim}; use coremem::sim::units::Seconds; -use coremem::mat::{Ferroxcube3R1MH, IsoConductorOr, IsomorphicConductor}; +use coremem::stim::{CurlStimulus, Gated, Sinusoid1, TimeVarying as _}; use coremem::Driver; type R = real::R32; type Mat = IsoConductorOr; +// type Backend = spirv::CpuBackend; type Backend = spirv::WgpuBackend; fn main() { coremem::init_logging(); coremem::init_debug(); let um = |n| n as f32 * 1e-6; - let ns = |n| n as f32 * 1e-9; + // let ns = |n| n as f32 * 1e-9; + let ps = |n| n as f32 * 1e-12; let feat_size = um(10); + let input_magnitude = 1.0e9; + let clock_phase_duration = ps(1000); + let s_major = um(160); let s_minor = um(30); let io_major = um(80); @@ -64,42 +71,106 @@ fn main() { let sx = |n| um((n+1) * 400); let sy = um(400); let sz = um(280); - let couplingx = |n| sx(n) - um(200); + let couplingx = |n| sx(n) + um(200); let sim_bounds = Meters::new(sx(4), sy * 2.0, sz * 2.0); let sim_padding = Meters::new(um(80), um(80), um(80)); - let duration = Seconds(ns(1)); let drive0 = Torus::new_xz(Meters::new(sx(0) - s_major, sy, sz), io_major, io_minor); let sense3 = Torus::new_xz(Meters::new(sx(3) + s_major, sy, sz), io_major, io_minor); let ctl = |n| Torus::new_yz(Meters::new(sx(n), sy + s_major, sz), io_major, io_minor); let s = |n| Torus::new_xy(Meters::new(sx(n), sy, sz), s_major, s_minor); + // coupling(n) is the wire which couples core n into core n+1 let coupling = |n| Torus::new_xz(Meters::new(couplingx(n), sy, sz), coupling_major, coupling_minor); + let input = |region: &Torus, cycle: u32, direction: i32| { + let area = region.cross_section(); + let amp = direction as f32 * input_magnitude / area; + let start = clock_phase_duration * cycle as f32; + let wave = Gated::new(amp, start, start + clock_phase_duration); + // let wave = Sinusoid1::from_wavelength(direction as f32 * input_magnitude / area, clock_phase_duration * 2.0) + // .half_cycle() + // .shifted(clock_phase_duration * cycle as f32); + CurlStimulus::new( + region.clone(), + wave.clone(), + region.center(), + region.axis(), + ) + }; + let wire_mat = IsomorphicConductor::new(1e6f32.cast::()); - let ferro_mat = wire_mat; - // let ferro_mat = Ferroxcube3R1MH::new(); // uncomment when ready to simulate for real + // let ferro_mat = wire_mat; + let ferro_mat = Ferroxcube3R1MH::new(); let mut driver = Driver::new(SpirvSim::::new( sim_bounds.to_index(feat_size), feat_size, )); driver.add_classical_boundary_explicit::(sim_padding); + + //////// create the wires and toroids driver.fill_region(&drive0, wire_mat); driver.fill_region(&sense3, wire_mat); for core in 0..4 { driver.fill_region(&s(core), ferro_mat); driver.fill_region(&ctl(core), wire_mat); - if core != 0 { + if core != 3 { driver.fill_region(&coupling(core), wire_mat); } } - let prefix = "out/applications/multi_core_inverter/0/"; + //////// monitor some measurements + // driver.add_measurement(meas::CurrentLoop::new("drv0", drive0.clone())); + for core in 0..4 { + driver.add_measurement(meas::CurrentLoop::new( + &format!("drive{}", core), + ctl(core), + )); + } + for core in 0..3 { + driver.add_measurement(meas::CurrentLoop::new( + &format!("sense{}", core), + coupling(core), + )); + } + for core in 0..4 { + driver.add_measurement(meas::MagneticLoop::new( + &format!("state{}", core), + s(core), + )); + } + driver.add_measurement(meas::CurrentLoop::new("sense3", sense3.clone())); + + //////// create the stimuli + // CTL{n} effectively leads CTL{n-1} + // or: at time t, CTL{n} is at cycle[t+n] + // where cycle[t] is defined by CTL[0](t): + // 0, +Vdd, +Vdd, +Vdd + // TODO: this is wrong (as is the diagram in the blog)! CTL0, being an inverter, + // needs -Vdd to recharge to +polarization + let cycles = 1; + let duration = Seconds(clock_phase_duration * (cycles + 2) as f32); + for cycle in 0..cycles { + for core in 0..1 { // TODO: core 0 + let dir = 1; + //let dir = if (cycle+core) % 4 == 0 { + // 0 + //} else { + // 1 + //}; + if dir != 0 { + // micro opt/safety: don't place zero-magnitude stimuli + driver.add_stimulus(input(&ctl(core), cycle, dir)); + } + } + } + + let prefix = "out/applications/multi_core_inverter/2/"; let _ = std::fs::create_dir_all(&prefix); // driver.add_state_file(&*format!("{}state.bc", prefix), 9600); - driver.add_serializer_renderer(&*format!("{}frame-", prefix), 36000, None); - driver.add_csv_renderer(&*format!("{}meas.csv", prefix), 400, None); - driver.set_steps_per_stim(200); + driver.add_serializer_renderer(&*format!("{}frame-", prefix), 400, None); + driver.add_csv_renderer(&*format!("{}meas.csv", prefix), 100, None); + driver.set_steps_per_stim(100); driver.step_until(duration); }