From 204439704719620fccaa16909cd59ba43c2479c8 Mon Sep 17 00:00:00 2001 From: colin Date: Thu, 1 Sep 2022 18:40:20 -0700 Subject: [PATCH] app: stacked_cores: prototype the long term goal of this demo is to see if: (a) we can get stronger coupling between two cores than with multi-core-inverter. (b) we can get amplification by using a charge-pump like concept. (c) we can construct a *working* multi-core-inverter from this. --- Cargo.lock | 7 + Cargo.toml | 1 + crates/applications/stacked_cores/Cargo.toml | 8 + crates/applications/stacked_cores/src/main.rs | 363 ++++++++++++++++++ 4 files changed, 379 insertions(+) create mode 100644 crates/applications/stacked_cores/Cargo.toml create mode 100644 crates/applications/stacked_cores/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 1ecc6f9..0d47c9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2093,6 +2093,13 @@ dependencies = [ "coremem", ] +[[package]] +name = "stacked_cores" +version = "0.1.0" +dependencies = [ + "coremem", +] + [[package]] name = "strsim" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index d56fcf4..d38ee30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "crates/applications/buffer_proto5", "crates/applications/multi_core_inverter", "crates/applications/sr_latch", + "crates/applications/stacked_cores", "crates/applications/wavefront", ] diff --git a/crates/applications/stacked_cores/Cargo.toml b/crates/applications/stacked_cores/Cargo.toml new file mode 100644 index 0000000..bb80822 --- /dev/null +++ b/crates/applications/stacked_cores/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "stacked_cores" +version = "0.1.0" +authors = ["Colin "] +edition = "2021" + +[dependencies] +coremem = { path = "../../coremem" } diff --git a/crates/applications/stacked_cores/src/main.rs b/crates/applications/stacked_cores/src/main.rs new file mode 100644 index 0000000..eef4b3d --- /dev/null +++ b/crates/applications/stacked_cores/src/main.rs @@ -0,0 +1,363 @@ +//! this example takes the multi_core_inverter but tiles the cores along their primary axis. +//! this allows for more loops between them, and thereby hopefully better coupling. +//! +//! to run this, from toplevel directory: +//! ``` +//! $ cargo run --release --bin stacked_cores +//! $ pushd crates/coremem; cargo run --release --bin viewer ../../out/applications/stacked_cores/0/ ; popd +//! ``` + +use coremem::geom::{Coord as _, Meters}; +use coremem::geom::region::{ElongatedTorus, Torus}; +use coremem::mat::{Ferroxcube3R1MH, IsoConductorOr, IsomorphicConductor}; +use coremem::meas; +#[allow(unused)] +use coremem::real::{self, Real as _}; +use coremem::sim::spirv::{self, SpirvSim}; +use coremem::sim::units::Seconds; +use coremem::stim::{CurlVectorField, Exp, Gated, ModulatedVectorField, Scaled, Shifted, TimeVaryingExt as _}; +use coremem::Driver; + +// type R = real::R32; +type R = f32; +type Mat = IsoConductorOr; +// type Backend = spirv::CpuBackend; +type Backend = spirv::WgpuBackend; +type Sim = SpirvSim::; + + + +enum DriveDirection { + High, + Low, +} +impl DriveDirection { + fn as_signed_f32(&self) -> f32 { + match self { + DriveDirection::High => 1.0, + DriveDirection::Low => -1.0, + } + } +} + +/// different states each control signal can be in for some clock cycle. +#[derive(Copy, Clone)] +enum ClockState { + HoldHigh, + ReleaseHigh, + HoldLow, + ReleaseLow, + Float, +} +type ClockSegment = Shifted, R>>>; + +impl ClockState { + fn time_stimulus(&self, params: &Params, cycle: u32) + -> Option + { + use ClockState::*; + match self { + HoldHigh => Some(params.control_signal_hold(cycle, DriveDirection::High)), + ReleaseHigh => Some(params.control_signal_release(cycle, DriveDirection::High)), + HoldLow => Some(params.control_signal_hold(cycle, DriveDirection::Low)), + ReleaseLow => Some(params.control_signal_release(cycle, DriveDirection::Low)), + Float => None, + } + } +} + +#[derive(Copy, Clone)] +struct Params { + input_magnitude: f32, + clock_phase_duration: f32, + clock_decay: f32, // exp decay half-life + ctl_conductivity: f32, + coupling_conductivity: f32, + // 's' = core (ferromagnetic part) + s_major: f32, + s_minor: f32, + // 'io' = drive/control wire + io_major: f32, + io_minor: f32, + coupling_length: f32, + coupling_major: f32, + coupling_minor: f32, + // coords for core 'n' + sx: f32, + sy: f32, + sz1: f32, +} +fn um(n: u32) -> f32 { + n as f32 * 1e-6 +} +fn ps(n: u32) -> f32 { + n as f32 * 1e-12 +} +impl Params { + fn sz(&self, n: u32) -> f32 { + (n + 1) as f32 * self.sz1 + } + fn couplingz(&self, n: u32) -> f32 { + 0.5 * (self.sz(n) + self.sz(n+1)) + } + /// control loop for core n (alternately called "drive" loop) + fn ctl(&self, n: u32) -> Torus { + Torus::new_yz(Meters::new(self.sx - self.s_major, self.sy, self.sz(n)), self.io_major, self.io_minor) + } + /// the last core gets an external output in place of its coupling loop + fn sense(&self, n: u32) -> Torus { + Torus::new_xz(Meters::new(self.sx + self.s_major, self.sy, self.sz(n)), self.io_major, self.io_minor) + } + fn s(&self, n: u32) -> Torus { + Torus::new_xy(Meters::new(self.sx, self.sy, self.sz(n)), self.s_major, self.s_minor) + } + /// coupling(n) is the wire which couples core n into core n+1 + fn coupling(&self, n: u32) -> ElongatedTorus { + // TODO: plumb an `angle` argument into this + ElongatedTorus::new_xz( + Meters::new(self.sx, self.sy, self.couplingz(n)), + self.coupling_length, + self.coupling_major, + self.coupling_minor, + ) + } + + fn control_signal_hold(&self, cycle: u32, ty: DriveDirection) -> ClockSegment { + // simple square wave: + let amp = ty.as_signed_f32() * self.input_magnitude; + let start = self.clock_phase_duration * cycle as f32; + Exp::new(100f32.cast() /* very long decay */) + .scaled(amp.cast()) + .gated(R::zero(), self.clock_phase_duration.cast()) + .shifted(start.cast()) + } + + fn control_signal_release(&self, cycle: u32, ty: DriveDirection) -> ClockSegment { + // decaying exponential wave: + let amp = ty.as_signed_f32() * self.input_magnitude; + let start = self.clock_phase_duration * cycle as f32; + Exp::new(self.clock_decay.cast() /* half life */) + .scaled(amp.cast()) + .gated(R::zero(), self.clock_phase_duration.cast()) + .shifted(start.cast()) + } + + fn with_input_magnitude(mut self, p: f32) -> Self { + self.input_magnitude = p; + self + } + fn with_clock_phase_duration(mut self, p: f32) -> Self { + self.clock_phase_duration = p; + self + } + fn with_clock_decay(mut self, p: f32) -> Self { + self.clock_decay = p; + self + } + fn with_ctl_conductivity(mut self, p: f32) -> Self { + self.ctl_conductivity = p; + self + } + fn with_coupling_conductivity(mut self, p: f32) -> Self { + self.coupling_conductivity = p; + self + } +} + + +/// minimal 2-core inverter. +/// analyze how the inverter transfers a zero v.s. a one. +#[allow(unused)] +fn drive_map_isolated_inv() -> [[ClockState; 2]; 6] { + use ClockState::*; + [ + // charge each device to '1' + [HoldHigh, HoldHigh ], + // let the cores settle + [ReleaseHigh,ReleaseHigh], + // write S0 -> S1. S1 should be *cleared* to 0. + [HoldLow, Float ], + + // charge S0=0, reset S1 for next write + [HoldLow, HoldHigh ], + // let the cores settle + [ReleaseLow, ReleaseHigh], + // write S0 -> S1. S1 should *keep its state* of 1. + [HoldLow, Float ], + ] +} + +fn main() { + coremem::init_logging(); + // coremem::init_debug(); + + let params = Params { + input_magnitude: 0.0, + clock_phase_duration: 0.0, + clock_decay: 0.0, + ctl_conductivity: 0.0, + coupling_conductivity: 0.0, + // 's' = core (ferromagnetic part) + s_major: um(160), + s_minor: um(30), + // 'io' = drive/control wire + io_major: um(80), + io_minor: um(30), + coupling_length: um(320), + coupling_major: um(80), + coupling_minor: um(30), + // coords for core 'n' + sx: um(400), + sy: um(400), + sz1: um(320), + }; + // TODO: use a deque, with push_front and push_back + let deferred = || {}; // add to this to schedule sims at a lower priority + run_sim( + "71-2ns-100ps-5e9A-1e3pctl-1e4pcpl", + drive_map_isolated_inv(), + params + .with_clock_phase_duration(ps(2000)) + .with_clock_decay(ps(100)) + .with_input_magnitude(5e9) + .with_ctl_conductivity(1e3) + .with_coupling_conductivity(1e4) + ); + run_sim( + "76-2ns-100ps-1e10A-1e3pctl-1e4pcpl", + drive_map_isolated_inv(), + params + .with_clock_phase_duration(ps(2000)) + .with_clock_decay(ps(100)) + .with_input_magnitude(1e10) + .with_ctl_conductivity(1e3) + .with_coupling_conductivity(1e4) + ); + run_sim( + "82-2ns-100ps-5e10A-1e3pctl-1e4pcpl", + drive_map_isolated_inv(), + params + .with_clock_phase_duration(ps(2000)) + .with_clock_decay(ps(100)) + .with_input_magnitude(5e10) + .with_ctl_conductivity(1e3) + .with_coupling_conductivity(1e4) + ); + run_sim( + "85-2ns-100ps-1e11A-1e2pctl-1e4pcpl", + drive_map_isolated_inv(), + params + .with_clock_phase_duration(ps(2000)) + .with_clock_decay(ps(100)) + .with_input_magnitude(1e11) + .with_ctl_conductivity(1e2) + .with_coupling_conductivity(1e4) + ); + + deferred(); +} + + +fn run_sim( + name: &str, drive_map: [[ClockState; C]; R], params: Params +) { + // let ns = |n| n as f32 * 1e-9; + let feat_size = um(10); + + let sim_padding = Meters::new(um(80), um(80), um(80)); + let sim_bounds = |num_cores| { + Meters::new(params.sx * 2.0, params.sy * 2.0, params.sz(num_cores)) + + sim_padding + }; + + //////// define the control signals/transitions + // each row N denotes the drive currents at clock cycle N. + // each col M denotes the drive current at core M. + let num_cycles = drive_map.len() as u32; + let num_cores = drive_map[0].len() as u32; + let mut core_drivers = vec![Vec::default(); num_cores as usize]; + for (cycle, cores) in drive_map.into_iter().enumerate() { + for (core, clock) in cores.into_iter().enumerate() { + core_drivers[core as usize].extend(clock.time_stimulus(¶ms, cycle as u32)); + } + } + let stim: Vec<_> = core_drivers.into_iter() + .enumerate() + .map(|(core, time_varying)| { + let region = params.ctl(core as u32); + let area = region.cross_section(); + let amp = 1.0 / area; + let v_field = CurlVectorField::new(region.clone()); + ModulatedVectorField::new(v_field, time_varying.scaled(amp.cast::())) + }) + .collect(); + assert_eq!(stim.len(), num_cores as usize); + + + let ctl_mat = IsomorphicConductor::new(params.ctl_conductivity.cast::()); + let coupling_mat = IsomorphicConductor::new(params.coupling_conductivity.cast::()); + let ferro_mat = Ferroxcube3R1MH::new(); + + let last_core = num_cores - 1; + + let mut driver = Driver::new(Sim::new( + sim_bounds(num_cores).to_index(feat_size), feat_size, + )); + driver.add_classical_boundary(sim_padding); + + //////// create the wires and toroids + driver.fill_region(¶ms.sense(last_core), coupling_mat); + for core in 0..num_cores { + driver.fill_region(¶ms.s(core), ferro_mat); + driver.fill_region(¶ms.ctl(core), ctl_mat); + if core != last_core { + driver.fill_region(¶ms.coupling(core), coupling_mat); + } + } + + //////// monitor some measurements + for core in 0..num_cores { + driver.add_measurement(meas::CurrentLoop::new( + &format!("drive{}", core), + params.ctl(core), + )); + } + for core in 0..num_cores { + let name = format!("sense{}", core); + if core != last_core { + driver.add_measurement(meas::CurrentLoop::new( + &name, params.coupling(core), + )); + } else { + driver.add_measurement(meas::CurrentLoop::new( + &name, params.sense(core), + )); + } + } + for core in 0..num_cores { + driver.add_measurement(meas::MagneticLoop::new( + &format!("state{}", core), + params.s(core), + )); + } + + + let duration = Seconds(params.clock_phase_duration * num_cycles as f32); + + // let stim = DynStimuli::from_vec(stim.map(MapIntoBoxStimulus).into_vec()); + let mut driver = driver.with_modulated_stimulus(); + for s in stim { + driver.add_stimulus(s); + } + + let prefix = format!("out/applications/stacked_cores/{}/", name); + let _ = std::fs::create_dir_all(&prefix); + driver.add_state_file(&*format!("{}state.bc", prefix), 25600); + // driver.add_serializer_renderer(&*format!("{}frame-", prefix), 6400, None); + // driver.add_csv_renderer(&*format!("{}meas-detailed.csv", prefix), 100, None); + driver.add_csv_renderer(&*format!("{}meas.csv", prefix), 1600, None); + driver.add_csv_renderer(&*format!("{}meas-sparse.csv", prefix), 12800, None); + driver.set_steps_per_stimulus(200); + + driver.step_until(duration); +}