restructure this multi-crate project to use Cargo's "workspace" feature

this solves an issue in the Nix build, where managing multiple
Cargo.lock files is otherwise tricky. it causes (or fails to fix?) an adjacent issue where
the spirv builder doesn't seem to have everything it needs vendored.
This commit is contained in:
2022-07-05 17:34:21 -07:00
parent d3cd12aa47
commit 5b99d30cda
64 changed files with 108 additions and 206 deletions

View File

@@ -0,0 +1,4 @@
the examples in this directory haven't been made for clarity.
lacking more suitable examples for certain features, i'm keeping these
ones around, but i recommend you only consult them if you can't find
what you're looking for in the toplevel `examples` directory.

View File

@@ -0,0 +1,152 @@
use coremem::{Driver, mat, meas, SpirvDriver};
use coremem::geom::{Meters, Torus};
use coremem::sim::units::Seconds;
use coremem::stim::{CurlStimulus, Sinusoid1, TimeVarying as _};
fn main() {
coremem::init_logging();
let feat_size = 40e-6f32;
let depth = 1600e-6;
let buffer_x = 240e-6;
let buffer_y = 240e-6;
// let buffer_z = 240e-6;
let ferro_major = 320e-6;
let ferro_minor = 60e-6;
let ferro_buffer = 60e-6;
let wire_minor = 40e-6;
let wire_major = 160e-6;
let wire_coupling_major = 280e-6; // (ferro_minor*4 + ferro_buffer)/2 + wire_minor = 190
let peak_current = 1.5e6;
let current_duration = 24e-9; // half-wavelength of the sine wave
// let current_break = 0.2e-9; // time between 'set' pulse and 'clear' pulse
let drive_conductivity = 5e6f32;
let sense_conductivity = 5e3f32;
// let coupling_offsets = [0.0];
let coupling_offsets = [-1.5*wire_minor, 1.5*wire_minor];
let m_to_um = |m: f32| (m * 1e6).round() as u32;
let half_depth = depth * 0.5;
let base = "buffer-22";
let boundary_xy = 500e-6;
let boundary_z = 300e-6;
let ferro_top_mid = boundary_xy + buffer_y + wire_minor + wire_major;
let ferro_center_y = ferro_top_mid + ferro_major;
let ferro_bot_mid = ferro_center_y + ferro_major;
let height = ferro_bot_mid + wire_major + wire_minor + buffer_y + boundary_xy;
let ferro1_left_edge = boundary_xy + buffer_x;
let ferro1_center = ferro1_left_edge + ferro_minor + ferro_major;
let ferro1_right_edge = ferro1_center + ferro_major + ferro_minor;
let ferro2_left_edge = ferro1_right_edge + ferro_buffer;
let ferro2_center = ferro2_left_edge + ferro_minor + ferro_major;
let ferro2_right_edge = ferro2_center + ferro_major + ferro_minor;
let width = ferro2_right_edge + wire_major + wire_minor + buffer_x + boundary_xy;
let ferro1_region = Torus::new_xy(Meters::new(ferro1_center, ferro_center_y, half_depth), ferro_major, ferro_minor);
let ferro2_region = Torus::new_xy(Meters::new(ferro2_center, ferro_center_y, half_depth), ferro_major, ferro_minor);
// SET connected to top of ferro1
let set_region = Torus::new_yz(Meters::new(ferro1_center, ferro_center_y - ferro_major, half_depth), wire_major, wire_minor);
// RESET connected to bottom of ferro1
let reset_region = Torus::new_yz(Meters::new(ferro1_center, ferro_center_y + ferro_major, half_depth), wire_major, wire_minor);
let coupling_regions: Vec<_> = coupling_offsets.iter().map(|off| {
Torus::new_xz(Meters::new(0.5*(ferro1_center + ferro2_center), ferro_center_y + off, half_depth), wire_coupling_major, wire_minor)
}).collect();
// SENSE connected to right of ferro2
let sense_region = Torus::new_xz(Meters::new(ferro2_center + ferro_major, ferro_center_y, half_depth), wire_major, wire_minor);
// TODO: make sure none of the regions overlap
let mut driver: SpirvDriver = Driver::new_spirv(Meters::new(width, height, depth), feat_size);
driver.set_steps_per_stim(1000);
//driver.fill_region(&ferro1_region, mat::db::linear_iron());
// Original, 3R1-LIKE ferromagnet (only a vague likeness), sr-latch-8:
// driver.fill_region(&ferro1_region, mat::MBFerromagnet::new(-0.3899, 0.3900, 310_000.0));
// driver.fill_region(&ferro2_region, mat::MBFerromagnet::new(-0.3899, 0.3900, 310_000.0));
// sr-latch-9; dead spot from B=[-0.03, 0.03]. This will help us see if the math is H-triggered
// or B-triggered
// driver.fill_region(&ferro1_region, mat::MBFerromagnet::new(-0.3300, 0.3900, 310_000.0));
// driver.fill_region(&ferro2_region, mat::MBFerromagnet::new(-0.3300, 0.3900, 310_000.0));
// mu_r=881.33, starting at H=25 to H=75.
driver.fill_region(&ferro1_region, mat::MHPgram::new(25.0, 881.33, 44000.0));
driver.fill_region(&ferro2_region, mat::MHPgram::new(25.0, 881.33, 44000.0));
driver.fill_region(&set_region, mat::IsomorphicConductor::new(drive_conductivity));
driver.fill_region(&reset_region, mat::IsomorphicConductor::new(drive_conductivity));
for r in &coupling_regions {
driver.fill_region(r, mat::IsomorphicConductor::new(drive_conductivity));
}
driver.fill_region(&sense_region, mat::IsomorphicConductor::new(sense_conductivity));
println!("boundary: {}um; {}um", m_to_um(boundary_xy), m_to_um(boundary_z));
driver.add_classical_boundary(Meters::new(boundary_xy, boundary_xy, boundary_z));
assert!(driver.test_region_filled(&ferro1_region, mat::MHPgram::new(25.0, 881.33, 44000.0)));
assert!(driver.test_region_filled(&ferro2_region, mat::MHPgram::new(25.0, 881.33, 44000.0)));
assert!(driver.test_region_filled(&set_region, mat::IsomorphicConductor::new(drive_conductivity)));
assert!(driver.test_region_filled(&reset_region, mat::IsomorphicConductor::new(drive_conductivity)));
for r in &coupling_regions {
assert!(driver.test_region_filled(r, mat::IsomorphicConductor::new(drive_conductivity)));
}
assert!(driver.test_region_filled(&sense_region, mat::IsomorphicConductor::new(sense_conductivity)));
let mut add_drive_pulse = |region: &Torus, start, duration, amp| {
let wave = Sinusoid1::from_wavelength(amp, duration * 2.0)
.half_cycle()
.shifted(start);
driver.add_stimulus(CurlStimulus::new(
region.clone(),
wave.clone(),
region.center(),
region.axis()
));
};
// J=\sigma E
// dJ/dt = \sigma dE/dT
// dE/dt = dJ/dt / \sigma
// dE/dt = dI/dt / (A*\sigma)
// if I = k*sin(w t) then dE/dt = k*w cos(w t) / (A*\sigma)
// i.e. dE/dt is proportional to I/(A*\sigma), multiplied by w (or, divided by wavelength)
let peak_stim1 = peak_current/current_duration / (set_region.cross_section() * drive_conductivity);
// SET
add_drive_pulse(&set_region, 0.01*current_duration, current_duration, peak_stim1);
let duration = 20.0*current_duration;
for (i, r) in coupling_regions.iter().enumerate() {
driver.add_measurement(meas::CurrentLoop::new(&*format!("coupling{}", i), r.clone()));
driver.add_measurement(meas::Current::new(&*format!("coupling{}", i), r.clone()));
}
driver.add_measurement(meas::CurrentLoop::new("sense", sense_region.clone()));
driver.add_measurement(meas::Current::new("sense", sense_region.clone()));
driver.add_measurement(meas::Volume::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::Magnetization::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::MagneticFlux::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::Volume::new("mem2", ferro2_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem2", ferro2_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("set", set_region.clone()));
driver.add_measurement(meas::Current::new("set", set_region.clone()));
driver.add_measurement(meas::Power::new("set", set_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("reset", reset_region.clone()));
let prefix = format!("out/{}/{}-{}mA-{}ps-{}um-{}xcoupling",
base,
base,
(peak_current * 1e3).round() as i64,
(current_duration * 1e12).round() as i64,
(feat_size * 1e6).round() as i64,
coupling_regions.len(),
);
let _ = std::fs::create_dir_all(&prefix);
driver.add_state_file(&*format!("{}/state.bc", prefix), 9600);
// driver.add_serializer_renderer(&*format!("{}/frame-", prefix), 36000);
driver.add_csv_renderer(&*format!("{}/meas.csv", prefix), 200, None);
driver.add_csv_renderer(&*format!("{}/meas-sparse.csv", prefix), 8000, None);
driver.step_until(Seconds(duration));
}

View File

@@ -0,0 +1,149 @@
use coremem::{Driver, mat, meas, SpirvDriver};
use coremem::geom::{Meters, Torus};
use coremem::sim::units::Seconds;
use coremem::stim::{CurlStimulus, Sinusoid1, TimeVarying as _};
fn main() {
coremem::init_logging();
let feat_size = 40e-6f32;
let depth = 1600e-6;
let buffer_x = 240e-6;
let buffer_y = 240e-6;
// let buffer_z = 240e-6;
let ferro_major = 320e-6;
let ferro_minor = 60e-6;
let ferro_buffer = 60e-6;
let wire_minor = 40e-6;
let wire_major = 160e-6;
let wire_coupling_major = 280e-6; // (ferro_minor*4 + ferro_buffer)/2 + wire_minor = 190
let drive_conductivity = 5e6f32;
let peak_set_current = 15.0;
let peak_clock_current = 100.0;
let set_duration = 10e-9; // half-wavelength of the sine wave
let steady_time = 500e-9; // how long to wait for sets to stabilize
let clock_duration = 1e-9;
// let coupling_offsets = [0.0];
// let coupling_offsets = [-1.5*wire_minor, 1.5*wire_minor];
// let coupling_offsets = [-3.0*wire_minor, 0.0, 3.0*wire_minor];
let coupling_offsets = [-4.5*wire_minor, -1.5*wire_minor, 1.5*wire_minor, 4.5*wire_minor];
// let coupling_offsets = [-6.0*wire_minor, -3.0*wire_minor, 0.0, 3.0*wire_minor, 6.0*wire_minor];
let m_to_um = |m: f32| (m * 1e6).round() as u32;
let half_depth = depth * 0.5;
let feat_vol = feat_size * feat_size * feat_size;
let base = "buffer2-11";
let boundary_xy = 500e-6;
let boundary_z = 300e-6;
let ferro_top_mid = boundary_xy + buffer_y + wire_minor + wire_major;
let ferro_center_y = ferro_top_mid + ferro_major;
let ferro_bot_mid = ferro_center_y + ferro_major;
let height = ferro_bot_mid + buffer_y + boundary_xy;
let ferro1_left_edge = boundary_xy + buffer_x;
let ferro1_center = ferro1_left_edge + ferro_minor + ferro_major;
let ferro1_right_edge = ferro1_center + ferro_major + ferro_minor;
let ferro2_left_edge = ferro1_right_edge + ferro_buffer;
let ferro2_center = ferro2_left_edge + ferro_minor + ferro_major;
let ferro2_right_edge = ferro2_center + ferro_major + ferro_minor;
let width = ferro2_right_edge + 2.0*wire_major + 2.0*wire_minor + buffer_x + boundary_xy;
let ferro1_region = Torus::new_xy(Meters::new(ferro1_center, ferro_center_y, half_depth), ferro_major, ferro_minor);
let ferro2_region = Torus::new_xy(Meters::new(ferro2_center, ferro_center_y, half_depth), ferro_major, ferro_minor);
let set1_region = Torus::new_xz(Meters::new(ferro1_center - ferro_major, ferro_center_y, half_depth), wire_major, wire_minor);
let set2_region = Torus::new_xz(Meters::new(ferro2_center + ferro_major, ferro_center_y, half_depth), wire_major, wire_minor);
let coupling_regions: Vec<_> = coupling_offsets.iter().map(|off| {
Torus::new_xz(Meters::new(0.5*(ferro1_center + ferro2_center), ferro_center_y + off, half_depth), wire_coupling_major, wire_minor)
}).collect();
let mut driver: SpirvDriver = Driver::new_spirv(Meters::new(width, height, depth), feat_size);
driver.set_steps_per_stim(1000);
// mu_r=881.33, starting at H=25 to H=75.
driver.fill_region(&ferro1_region, mat::MHPgram::new(25.0, 881.33, 44000.0));
driver.fill_region(&ferro2_region, mat::MHPgram::new(25.0, 881.33, 44000.0));
driver.fill_region(&set1_region, mat::IsomorphicConductor::new(drive_conductivity));
driver.fill_region(&set2_region, mat::IsomorphicConductor::new(drive_conductivity));
for r in &coupling_regions {
driver.fill_region(r, mat::IsomorphicConductor::new(drive_conductivity));
}
println!("boundary: {}um; {}um", m_to_um(boundary_xy), m_to_um(boundary_z));
driver.add_classical_boundary(Meters::new(boundary_xy, boundary_xy, boundary_z));
assert!(driver.test_region_filled(&ferro1_region, mat::MHPgram::new(25.0, 881.33, 44000.0)));
assert!(driver.test_region_filled(&ferro2_region, mat::MHPgram::new(25.0, 881.33, 44000.0)));
assert!(driver.test_region_filled(&set1_region, mat::IsomorphicConductor::new(drive_conductivity)));
assert!(driver.test_region_filled(&set2_region, mat::IsomorphicConductor::new(drive_conductivity)));
for r in &coupling_regions {
assert!(driver.test_region_filled(r, mat::IsomorphicConductor::new(drive_conductivity)));
}
let mut add_drive_pulse = |region: &Torus, start, duration, amp| {
let wave = Sinusoid1::from_wavelength(amp, duration * 2.0)
.half_cycle()
.shifted(start);
driver.add_stimulus(CurlStimulus::new(
region.clone(),
wave.clone(),
region.center(),
region.axis()
));
};
// J=\sigma E
// dJ/dt = \sigma dE/dT
// dE/dt = dJ/dt / \sigma
// dE/dt = dI/dt / (A*\sigma)
// if I = k*sin(w t) then dE/dt = k*w cos(w t) / (A*\sigma)
// i.e. dE/dt is proportional to I/(A*\sigma), multiplied by w (or, divided by wavelength)
let peak_set = peak_set_current / feat_vol / (set1_region.cross_section() * drive_conductivity);
let peak_clock = peak_clock_current / feat_vol / (set1_region.cross_section() * drive_conductivity);
// SET cores
add_drive_pulse(&set1_region, 0.01*set_duration, set_duration, -peak_set);
add_drive_pulse(&set2_region, 0.01*set_duration, set_duration, peak_set);
// CLEAR core1
add_drive_pulse(&set1_region, set_duration + steady_time, clock_duration, peak_clock);
let duration = 2.5*steady_time;
for (i, r) in coupling_regions.iter().enumerate() {
driver.add_measurement(meas::CurrentLoop::new(&*format!("coupling{}", i), r.clone()));
driver.add_measurement(meas::Current::new(&*format!("coupling{}", i), r.clone()));
}
driver.add_measurement(meas::Volume::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::Magnetization::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::MagneticFlux::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::Volume::new("mem2", ferro2_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem2", ferro2_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("set1", set1_region.clone()));
driver.add_measurement(meas::Current::new("set1", set1_region.clone()));
driver.add_measurement(meas::Power::new("set1", set1_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("set2", set2_region.clone()));
let prefix = format!("out/{}/{}-{}setmA-{}setps-{}clkmA-{}clkps-{}um-{}xcoupling",
base,
base,
(peak_set_current * 1e3).round() as i64,
(set_duration * 1e12).round() as i64,
(peak_clock_current * 1e3).round() as i64,
(clock_duration * 1e12).round() as i64,
(feat_size * 1e6).round() as i64,
coupling_regions.len(),
);
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), 200, None);
driver.add_csv_renderer(&*format!("{}/meas-sparse.csv", prefix), 8000, None);
driver.step_until(Seconds(duration));
}

View File

@@ -0,0 +1,171 @@
//! this example stacks buffers vertically so that we can couple them with MANY wire loops.
//! the conclusion is that denser loops gets the coupling closer to 1:1 (counteracts losses),
//! but it likely won't ever achieve amplification.
use coremem::{Driver, mat, meas, SpirvDriver};
use coremem::geom::{Meters, Torus};
use coremem::sim::units::Seconds;
use coremem::stim::{CurlStimulus, Sinusoid1, TimeVarying as _};
fn main() {
coremem::init_logging();
let feat_size = 40e-6f32;
let buffer_xy = 160e-6;
let buffer_z = 160e-6;
let boundary_xy = 320e-6;
let boundary_z = 320e-6;
let ferro_major = 640e-6;
let ferro_minor = 60e-6;
let ferro_buffer = 160e-6; // vertical space between ferros
let wire_minor = 40e-6;
let wire_set_major = 140e-6; // this just needs to exceed ferro_minor + wire_minor; less than ferro_buffer - wire_minor
let wire_coupling_major = 280e-6; // (ferro_minor*4 + ferro_buffer)/2 + wire_minor = 220
let drive_conductivity = 5e6f32;
let peak_set_current = 15.0;
let peak_clock_current = 100.0;
let set_duration = 10e-9; // half-wavelength of the sine wave
let steady_time = 500e-9; // how long to wait for sets to stabilize
let clock_duration = 1e-9;
let m_to_um = |m: f32| (m * 1e6).round() as u32;
let feat_vol = feat_size * feat_size * feat_size;
let base = "buffer3-13";
let width = 2.0*(ferro_major + wire_coupling_major + wire_minor + buffer_xy + boundary_xy);
let height = width;
let depth = 2.0*(wire_coupling_major + wire_minor + buffer_z + boundary_z);
let ferro1_center = Meters::new(0.5 * width, 0.5 * height, 0.5 * depth - ferro_minor - 0.5 * ferro_buffer);
let ferro2_center = Meters::new(0.5 * width, 0.5 * height, 0.5 * depth + ferro_minor + 0.5 * ferro_buffer);
// reserve the left/right locations for the SET wires.
let set1_center = (ferro1_center + ferro2_center) * 0.5 + Meters::new(-ferro_major, 0.0, -wire_set_major);
let set2_center = (ferro1_center + ferro2_center) * 0.5 + Meters::new(ferro_major, 0.0, wire_set_major);
let ferro1_region = Torus::new_xy(ferro1_center, ferro_major, ferro_minor);
let ferro2_region = Torus::new_xy(ferro2_center, ferro_major, ferro_minor);
let set1_region = Torus::new_xz(set1_center, wire_set_major, wire_minor);
let set2_region = Torus::new_xz(set2_center, wire_set_major, wire_minor);
let rev = 6.283185307179586;
// let coupling_angles = [0.25*rev, 0.75*rev];
// let coupling_angles = [0.125*rev, 0.25*rev, 0.375*rev, 0.625*rev, 0.75*rev, 0.875*rev];
let coupling_angles = [
0.0625*rev,
0.1250*rev, 0.1875*rev,
0.2500*rev, 0.3125*rev,
0.3750*rev, 0.4375*rev,
0.5625*rev,
0.6250*rev, 0.6875*rev,
0.7500*rev, 0.8125*rev,
0.8750*rev, 0.9375*rev,
];
let define_coupling_region = |angle: f32| -> Torus {
let ferro_center = (ferro1_center + ferro2_center) * 0.5;
let tor_center = ferro_center + Meters::new(angle.cos(), angle.sin(), 0.0) * ferro_major;
let tor_normal = Meters::new(-angle.sin(), angle.cos(), 0.0);
Torus::new(tor_center, tor_normal, wire_coupling_major, wire_minor)
};
let coupling_regions: Vec<_> = coupling_angles.iter().cloned().map(define_coupling_region).collect();
// mu_r=881.33, starting at H=25 to H=75.
let ferro_mat = mat::MHPgram::new(25.0, 881.33, 44000.0);
// let ferro_mat = mat::db::conductor(drive_conductivity);
let wire_mat = mat::IsomorphicConductor::new(drive_conductivity);
let mut driver: SpirvDriver = Driver::new_spirv(Meters::new(width, height, depth), feat_size);
driver.set_steps_per_stim(1000);
driver.fill_region(&ferro1_region, ferro_mat);
driver.fill_region(&ferro2_region, ferro_mat);
driver.fill_region(&set1_region, wire_mat);
driver.fill_region(&set2_region, wire_mat);
for r in &coupling_regions {
driver.fill_region(r, wire_mat);
}
println!("boundary: {}um; {}um", m_to_um(boundary_xy), m_to_um(boundary_z));
println!("size: {}, {}, {}", width, height, depth);
println!("ferro1: {:?}", ferro1_center);
println!("ferro2: {:?}", ferro2_center);
driver.add_classical_boundary(Meters::new(boundary_xy, boundary_xy, boundary_z));
assert!(driver.test_region_filled(&ferro1_region, ferro_mat));
assert!(driver.test_region_filled(&ferro2_region, ferro_mat));
assert!(driver.test_region_filled(&set1_region, wire_mat));
assert!(driver.test_region_filled(&set2_region, wire_mat));
for r in &coupling_regions {
assert!(driver.test_region_filled(r, wire_mat));
}
let mut add_drive_pulse = |region: &Torus, start, duration, amp| {
let wave = Sinusoid1::from_wavelength(amp, duration * 2.0)
.half_cycle()
.shifted(start);
driver.add_stimulus(CurlStimulus::new(
region.clone(),
wave.clone(),
region.center(),
region.axis()
));
};
// J=\sigma E
// dJ/dt = \sigma dE/dT
// dE/dt = dJ/dt / \sigma
// dE/dt = dI/dt / (A*\sigma)
// if I = k*sin(w t) then dE/dt = k*w cos(w t) / (A*\sigma)
// i.e. dE/dt is proportional to I/(A*\sigma), multiplied by w (or, divided by wavelength)
let peak_set = peak_set_current / feat_vol / (set1_region.cross_section() * drive_conductivity);
let peak_clock = peak_clock_current / feat_vol / (set1_region.cross_section() * drive_conductivity);
// SET cores
add_drive_pulse(&set1_region, 0.01*set_duration, set_duration, -peak_set);
add_drive_pulse(&set2_region, 0.01*set_duration, set_duration, -peak_set);
// CLEAR core1
add_drive_pulse(&set1_region, set_duration + steady_time, clock_duration, peak_clock);
let duration = 2.5*steady_time;
for (i, r) in coupling_regions.iter().enumerate() {
driver.add_measurement(meas::CurrentLoop::new(&*format!("coupling{}", i), r.clone()));
}
driver.add_measurement(meas::Volume::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::Volume::new("mem2", ferro2_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem2", ferro2_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("set1", set1_region.clone()));
driver.add_measurement(meas::Power::new("set1", set1_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("set2", set2_region.clone()));
let prefix = format!("out/{}/{}-{}-{}setmA-{}setps-{}clkmA-{}clkps-{}um-{}xcoupling",
base,
base,
*driver.size(),
(peak_set_current * 1e3).round() as i64,
(set_duration * 1e12).round() as i64,
(peak_clock_current * 1e3).round() as i64,
(clock_duration * 1e12).round() as i64,
(feat_size * 1e6).round() as i64,
coupling_regions.len(),
);
let _ = std::fs::create_dir_all(&prefix);
driver.add_state_file(&*format!("{}/state.bc", prefix), 16000);
// driver.add_serializer_renderer(&*format!("{}/frame-", prefix), 32000, None);
driver.add_csv_renderer(&*format!("{}/meas.csv", prefix), 200, None);
driver.add_csv_renderer(&*format!("{}/meas-sparse.csv", prefix), 8000, None);
driver.step_until(Seconds(duration));
}

View File

@@ -0,0 +1,197 @@
//! this example positions buffers adjacently and uses an ASYMMETRIC coil winding
//! it demonstrates a logic-high transmission rate > 1, but also a rapid degradation of logic-low.
//! i believe logic-low values are degraded MOSTLY because of direct coupling between the clock
//! wire and the coupling wire (instead of coupling achieved exclusively via the core).
//!
//! this models an inverter function that's something like mem2 = 700 - 0.15*mem1.
//! that 0.15 factor is too small for a cascadable inverter, which needs to be at least 1.0.
//! this factor can be increased by increasing the energy dumped into the clock -- but in order for
//! that to be workable i need to first decrease the direct coupling between the clock and mem2.
//! that will be the goal of buffer_proto5.
use coremem::{Driver, mat, meas, SpirvDriver};
use coremem::geom::{region, Cube, Meters, Spiral, SwapYZ, Torus, Translate, Wrap};
use coremem::sim::units::Seconds;
use coremem::stim::{CurlStimulus, Sinusoid1, TimeVarying as _};
fn main() {
coremem::init_logging();
let feat_size = 40e-6f32;
let buffer_xy = 160e-6;
let buffer_z = 160e-6;
let boundary_xy = 320e-6;
let boundary_z = 320e-6;
let ferro_major = 640e-6;
let ferro_minor = 60e-6;
let ferro_buffer = 640e-6; // horizontal space between ferros
let wire_minor = 40e-6;
let wire_wrap_minor = 240e-6; // 2x wire_wrap_minor + feat_size must be < ferro_buffer
let wire_set_major = 600e-6; // this just needs to exceed ferro_minor + wire_wrap_minor + feat_size + wire_minor
let drive_conductivity = 5e6f32;
let peak_set_current = 60.0;
let peak_clock_current = 5.0;
let set_duration = 20e-9; // half-wavelength of the sine wave
let steady_time = 160e-9; // how long to wait for sets to stabilize
let clock_duration = 40e-9;
let m_to_um = |m: f32| (m * 1e6).round() as u32;
let feat_vol = feat_size * feat_size * feat_size;
let base = "buffer4-NN";
let width = 4.0*ferro_major + 2.0*(buffer_xy + boundary_xy + wire_set_major + wire_minor) + ferro_buffer;
let height = 2.0*(ferro_major + ferro_minor + 2.0*wire_wrap_minor + feat_size + buffer_xy + boundary_xy);
let depth = 2.0*(wire_set_major + wire_minor + buffer_z + boundary_z);
let ferro1_center = Meters::new(
buffer_xy + boundary_xy + wire_set_major + wire_minor + ferro_major,
buffer_xy + boundary_xy + ferro_major + ferro_minor + 2.0*wire_wrap_minor + feat_size,
buffer_z + boundary_z + wire_set_major + wire_minor
);
let ferro2_center = ferro1_center + Meters::new(2.0*ferro_major + ferro_buffer, 0.0, 0.0);
let ferro_center = (ferro1_center + ferro2_center)*0.5;
// reserve the left/right locations for the SET wires.
let set1_center = ferro1_center - Meters::new(ferro_major, 0.0, 0.0);
let set2_center = ferro2_center + Meters::new(ferro_major, 0.0, 0.0);
let ferro1_region = Torus::new_xy(ferro1_center, ferro_major, ferro_minor);
let ferro2_region = Torus::new_xy(ferro2_center, ferro_major, ferro_minor);
let set1_region = Torus::new_xz(set1_center, wire_set_major, wire_minor);
let set2_region = Torus::new_xz(set2_center, wire_set_major, wire_minor);
let coupling_region1 = Wrap::new_about(
Translate::new(
SwapYZ::new(region::and_not(
Spiral::new(ferro_minor + wire_wrap_minor + feat_size, wire_wrap_minor, 0.125),
Cube::new(Meters::new(-1.0, -1.0, -0.125), Meters::new(1.0, 1.0, 0.125))
)),
ferro1_center + Meters::new(1.0*ferro_major, 0.0, 0.0),
),
1.0, // one half-rev => y=1.0
ferro1_center,
);
let coupling_region2 = Wrap::new_about(
Translate::new(
SwapYZ::new(region::and_not(
Spiral::new(ferro_minor + wire_wrap_minor + feat_size, wire_wrap_minor, 0.125),
Cube::new(Meters::new(-1.0, -1.0, -0.875), Meters::new(1.0, 1.0, 0.875))
)),
ferro2_center + Meters::new(1.0*ferro_major, 0.0, 0.0),
),
1.0, // one half-rev => y=1.0
ferro2_center,
);
let coupling_wire_top = Cube::new_centered(
ferro_center - Meters::new(0.0, 0.45*ferro_major, 0.0),
Meters::new(ferro_buffer - 3.0*feat_size, 1.0*feat_size, 2.0*feat_size)
);
let coupling_wire_bot = Cube::new_centered(
ferro_center + Meters::new(0.0, 0.45*ferro_major, 0.0),
Meters::new(ferro_buffer - 3.0*feat_size, 1.0*feat_size, 2.0*feat_size)
);
let coupling_region = region::Union::new()
.with(coupling_region1.clone())
.with(coupling_region2.clone())
// we don't actually need these coupling wires: the wraps touch naturally.
// .with(coupling_wire_top.clone())
// .with(coupling_wire_bot.clone())
;
// mu_r=881.33, starting at H=25 to H=75.
let ferro_mat = mat::MHPgram::new(25.0, 881.33, 44000.0);
let wire_mat = mat::IsomorphicConductor::new(drive_conductivity);
let mut driver: SpirvDriver = Driver::new_spirv(Meters::new(width, height, depth), feat_size);
driver.set_steps_per_stim(1000);
driver.fill_region(&ferro1_region, ferro_mat);
driver.fill_region(&ferro2_region, ferro_mat);
driver.fill_region(&set1_region, wire_mat);
driver.fill_region(&set2_region, wire_mat);
driver.fill_region(&coupling_region, wire_mat);
println!("boundary: {}um; {}um", m_to_um(boundary_xy), m_to_um(boundary_z));
println!("size: {}, {}, {}", width, height, depth);
println!("ferro1: {:?}", ferro1_center);
println!("ferro2: {:?}", ferro2_center);
driver.add_classical_boundary(Meters::new(boundary_xy, boundary_xy, boundary_z));
assert!(driver.test_region_filled(&ferro1_region, ferro_mat));
assert!(driver.test_region_filled(&ferro2_region, ferro_mat));
assert!(driver.test_region_filled(&set1_region, wire_mat));
assert!(driver.test_region_filled(&set2_region, wire_mat));
assert!(driver.test_region_filled(&coupling_region, wire_mat));
let mut add_drive_pulse = |region: &Torus, start, duration, amp| {
let wave = Sinusoid1::from_wavelength(amp, duration * 2.0)
.half_cycle()
.shifted(start);
driver.add_stimulus(CurlStimulus::new(
region.clone(),
wave.clone(),
region.center(),
region.axis()
));
};
// J=\sigma E
// dJ/dt = \sigma dE/dT
// dE/dt = dJ/dt / \sigma
// dE/dt = dI/dt / (A*\sigma)
// if I = k*sin(w t) then dE/dt = k*w cos(w t) / (A*\sigma)
// i.e. dE/dt is proportional to I/(A*\sigma), multiplied by w (or, divided by wavelength)
let peak_set = peak_set_current / feat_vol / (set1_region.cross_section() * drive_conductivity);
let peak_clock = peak_clock_current / feat_vol / (set1_region.cross_section() * drive_conductivity);
// SET cores
add_drive_pulse(&set1_region, 0.01*set_duration, set_duration, peak_set);
add_drive_pulse(&set2_region, 0.01*set_duration, set_duration, peak_set);
// CLEAR core1
add_drive_pulse(&set1_region, set_duration + steady_time, clock_duration, peak_clock);
let duration = 2.5*steady_time + set_duration + clock_duration;
driver.add_measurement(meas::Volume::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::Volume::new("mem2", ferro2_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem2", ferro2_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("set1", set1_region.clone()));
driver.add_measurement(meas::Power::new("set1", set1_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("set2", set2_region.clone()));
// driver.add_measurement(meas::CurrentLoop::new("coupling1", coupling_region1.clone()));
// driver.add_measurement(meas::CurrentLoop::new("coupling2", coupling_region2.clone()));
// driver.add_measurement(meas::CurrentLoop::new("couplingtop", coupling_wire_top.clone()));
// driver.add_measurement(meas::CurrentLoop::new("couplingbot", coupling_wire_bot.clone()));
let prefix = format!("out/{}/{}-{}-{}setmA-{}setps-{}clkmA-{}clkps-{}um",
base,
base,
*driver.size(),
(peak_set_current * 1e3).round() as i64,
(set_duration * 1e12).round() as i64,
(peak_clock_current * 1e3).round() as i64,
(clock_duration * 1e12).round() as i64,
(feat_size * 1e6).round() as i64,
);
let _ = std::fs::create_dir_all(&prefix);
driver.add_state_file(&*format!("{}/state.bc", prefix), 16000);
// driver.add_serializer_renderer(&*format!("{}/frame-", prefix), 32000);
driver.add_csv_renderer(&*format!("{}/meas.csv", prefix), 200, None);
driver.add_csv_renderer(&*format!("{}/meas-sparse.csv", prefix), 8000, None);
driver.step_until(Seconds(duration));
}

View File

@@ -0,0 +1,124 @@
use coremem::{Driver, mat, meas, SimState};
use coremem::geom::{Cube, Index, InvertedRegion, Meters, Torus, Union};
use coremem::real::R64 as Real;
use coremem::stim::{CurlStimulus, Sinusoid1, TimeVarying as _};
use coremem::units::Seconds;
fn main() {
coremem::init_logging();
let feat_size = 10e-6f32; // feature size
let duration = 6e-9;
let width = 2200e-6;
let depth = 1800e-6;
let buffer = 200e-6;
let ferro_major = 320e-6;
let ferro_minor = 60e-6;
let wire_minor = 40e-6;
let wire_major = 160e-6;
let peak_current = 5e11;
let current_duration = 1.0e-11; // half-wavelength of the sine wave
let current_break = 0.5e-11; // time between 'set' pulse and 'clear' pulse
let conductivity = 1.0e9f32;
let steps_per_frame = 400;
let from_m = |m: f32| (m/feat_size).round() as u32;
let m_to_um = |m: f32| (m * 1e6).round() as u32;
let half_width = width * 0.5;
let half_depth = depth * 0.5;
let width_px = from_m(width);
let depth_px = from_m(depth);
let size_px = Index((width_px, width_px, depth_px).into());
let mut driver: Driver<SimState<Real>> = Driver::new(size_px, feat_size);
driver.set_steps_per_stim(1);
let base = "minimal_torus-6";
let ferro_region = Torus::new_xy(Meters::new(half_width, half_width, half_depth), ferro_major, ferro_minor);
let drive_region = Torus::new_xz(Meters::new(half_width - ferro_major, half_width, half_depth), wire_major, wire_minor);
let sense_region = Torus::new_xz(Meters::new(half_width + ferro_major, half_width, half_depth), wire_major, wire_minor);
let boundary_xy = half_width - ferro_major - ferro_minor - buffer;
let boundary_z = half_depth - wire_major - wire_minor - buffer;
let boundary_lower = Meters::new(boundary_xy, boundary_xy, boundary_z);
let boundary_upper = Meters::new(width - boundary_xy, width - boundary_xy, depth - boundary_z);
println!("boundary: {}um; {}um", m_to_um(boundary_xy), m_to_um(boundary_z));
let boundary_region = InvertedRegion::new(Cube::new(boundary_lower, boundary_upper));
let vacuum_region = InvertedRegion::new(Union::new()
.with(ferro_region.clone())
.with(drive_region.clone())
.with(sense_region.clone())
.with(boundary_region.clone())
);
driver.fill_region(&ferro_region, mat::db::minimal_square_ferrite());
driver.fill_region(&drive_region, mat::IsomorphicConductor::new(conductivity));
driver.fill_region(&sense_region, mat::IsomorphicConductor::new(conductivity));
// driver.add_pml_boundary(Meters((boundary_xy, boundary_xy, boundary_z).into()));
driver.add_classical_boundary_explicit::<Real, _>(Meters::new(boundary_xy, boundary_xy, boundary_z));
// J=\sigma E
// dJ/dt = \sigma dE/dT
// dE/dt = dJ/dt / \sigma
// dE/dt = dI/dt / (A*\sigma)
// if I = k*sin(w t) then dE/dt = k*w sin(w t) / (A*\sigma)
// i.e. dE/dt is proportional to I/(A*\sigma), multiplied by w (or, divided by wavelength)
let peak_stim = peak_current/current_duration / (drive_region.cross_section() * conductivity);
let pos_wave = Sinusoid1::from_wavelength(peak_stim as _, current_duration * 2.0)
.half_cycle();
let neg_wave = Sinusoid1::from_wavelength(-peak_stim as _, current_duration * 2.0)
.half_cycle()
.shifted(current_duration + current_break);
driver.add_stimulus(CurlStimulus::new(
drive_region.clone(),
pos_wave,
drive_region.center(),
drive_region.axis()
));
driver.add_stimulus(CurlStimulus::new(
drive_region.clone(),
neg_wave,
drive_region.center(),
drive_region.axis()
));
driver.add_measurement(meas::CurrentLoop::new("sense", sense_region.clone()));
driver.add_measurement(meas::Current::new("sense", sense_region.clone()));
driver.add_measurement(meas::Energy::new("sense", sense_region.clone()));
driver.add_measurement(meas::Energy::new("sense", sense_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("drive", drive_region.clone()));
driver.add_measurement(meas::Current::new("drive", drive_region.clone()));
driver.add_measurement(meas::Energy::new("drive", drive_region.clone()));
driver.add_measurement(meas::Power::new("drive", drive_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem", ferro_region.clone()));
driver.add_measurement(meas::Magnetization::new("mem", ferro_region.clone()));
driver.add_measurement(meas::MagneticFlux::new("mem", ferro_region.clone()));
driver.add_measurement(meas::Energy::new("mem", ferro_region.clone()));
driver.add_measurement(meas::Power::new("mem", ferro_region.clone()));
driver.add_measurement(meas::Energy::new("boundary", boundary_region.clone()));
driver.add_measurement(meas::Power::new("boundary", boundary_region.clone()));
driver.add_measurement(meas::Energy::new("vacuum", vacuum_region.clone()));
driver.add_measurement(meas::Power::new("vacuum", vacuum_region.clone()));
let prefix = format!("out/{}/{}-flt{}-{}-feat{}um-{}mA-{}ps--radii{}um-{}um-{}um-{}um",
base,
base,
std::mem::size_of::<Real>() * 8,
*size_px,
m_to_um(feat_size),
(peak_current * 1e3).round() as i64,
(current_duration * 1e12).round() as i64,
m_to_um(ferro_major),
m_to_um(ferro_minor),
m_to_um(wire_major),
m_to_um(wire_minor),
);
let _ = std::fs::create_dir_all(&prefix);
driver.add_serializer_renderer(&*format!("{}/frame-", prefix), steps_per_frame, None);
driver.step_until(Seconds(duration));
}

View File

@@ -0,0 +1,75 @@
use coremem::{Driver, MaterialSim as _, SimState};
use coremem::geom::{Cube, Index, Meters, Vec3};
use coremem::mat::Static;
use coremem::real::R64;
use coremem::stim::{Stimulus, Sinusoid3};
use coremem::units::Seconds;
fn main() {
coremem::init_logging();
let feat_size = 1e-3;
let s = 200;
let steps_per_frame = 10;
let size = Index::new(s, s, 1);
let duration = 10e-8;
let current_duration = 1e-10;
let peak_stim = 1.0e18;
let mut driver: Driver<SimState<R64, Static<R64>>> = Driver::new(size, feat_size);
let timestep = driver.state.timestep();
driver.state.fill_boundary_using(size/4, |boundary_ness| {
let b = boundary_ness.elem_pow(3.0);
let coord_stretch = b * (0.5 / timestep);
Static {
conductivity: coord_stretch.cast(), ..Default::default() // TODO PML coord_stretch
}
});
// for y in 0..size.y() {
// for x in 0..size.x() {
// // if y >= x {
// let mut m = driver.state.mat_mut(Index::new(x, y, 0));
// // Force the stretch to be only along one axis
// m.conductivity = match m.conductivity.to_tuple() { // TODO PML coord_stretch
// (cs_x, cs_y, cs_z) if x < s/2 || y < s/2 => (cs_x, cs_y, cs_z),
// // (x, y, _) if x == y => (0.0, 0.0, 0.0),
// // (cs_x, cs_y, cs_z) if cs_x >= cs_y && cs_x >= cs_z && (y > 5 || x > 5) => (cs_x, 0.0, 0.0),
// // (cs_x, cs_y, cs_z) if cs_y >= cs_x && cs_y >= cs_z && (y > 5 || x > 5) => (0.0, cs_y, 0.0),
// // (cs_x, cs_y, cs_z) if cs_z >= cs_x && cs_z >= cs_y => (0.0, 0.0, 0.0),
// // (cs_x, cs_y, cs_z) if cs_y >= cs_x && cs_y >= cs_z && y > size.y()/2 => (0.0, cs_y - cs_x, 0.0),
// // (cs_x, cs_y, cs_z) if (x == 0 && y == 0) => (cs_x, cs_y, cs_z),
// // (cs_x, cs_y, cs_z) if (x == 200 && y == 0) => (cs_x, cs_y, cs_z),
// // (cs_x, cs_y, cs_z) if (x == 200 && y == 200) => (cs_x, cs_y, cs_z),
// // (cs_x, cs_y, cs_z) if (x == 0 && y == 200) => (cs_x, cs_y, cs_z),
// // (cs_x, cs_y, cs_z) if x == y && y >= 100 => (cs_x, cs_y, cs_z),
// // (cs_x, cs_y, cs_z) if x == 200 - y && y <= 100 => (cs_x, cs_y, cs_z),
// // (cs_x, cs_y, cs_z) if x == y && (x <= 19 || x >= 181) => (cs_x, cs_y, cs_z),
// // (cs_x, cs_y, cs_z) if x == 200 - y && (x <= 19 || x >= 181) => (cs_x, cs_y, cs_z),
// (_) => (0.0, 0.0, 0.0),
// // _ => unreachable!(),
// }.into();
// // if x <= 7 || x >= 193 || y <= 7 || y >= 193 {
// // m.coord_stretch = (0.0, 0.0, 0.0).into();
// // //m.conductivity = (1e3, 1e3, 1e3).into();
// // }
// // }
// }
// }
let drive_region = Cube::new(
Meters(Vec3::new((size.x() - 1) as f32 * 0.5 * feat_size, (size.y() - 1) as f32 * 0.5 * feat_size, 0.0)),
Meters(Vec3::new((size.x() + 1) as f32 * 0.5 * feat_size, (size.y() + 1) as f32 * 0.5 * feat_size, 1.0 * feat_size)),
);
let pos_wave = Sinusoid3::from_wavelength(Vec3::new(0.0, 0.0, peak_stim), current_duration * 2.0)
.half_cycle();
driver.add_stimulus(Stimulus::new(drive_region, pos_wave));
let prefix = "out/pml-mono-200-200-border-25-only-lower-right".to_string();
let _ = std::fs::create_dir_all(&prefix);
driver.add_serializer_renderer(&*format!("{}/frame-", prefix), steps_per_frame, None);
driver.step_until(Seconds(duration));
}

View File

@@ -0,0 +1,124 @@
use coremem::{Driver, mat, meas, MaterialSim as _, SimState};
use coremem::geom::{CylinderZ, Index, Meters, Vec2, Vec3};
use coremem::real::R64 as Real;
use coremem::stim::{Stimulus, Sinusoid3};
use log::trace;
fn main() {
coremem::init_logging();
for p in 0..20 {
let ferro_depth = 10e-6 * (20-p) as f32;
let feat_size = 10e-6f32; // feature size
let from_m = |m| (m/feat_size) as u32;
let m_to_um = |px| (px * 1e6) as u32;
let to_m = |px| px as f32 * feat_size;
let width = 2500e-6;
let depth = 200e-6;
let conductor_inner_rad = 0e-6;
let conductor_outer_rad = 19e-6;
let ferro_inner_rad = 100e-6;
let ferro_outer_rad = 200e-6;
//let ferro_depth = 10e-6;
let buffer = 250e-6;
let peak_current = 2e3;
let current_duration = 1e-9; // half-wavelength of the sine wave
let conductivity = 1.0e5;
let half_width = width * 0.5;
let half_depth = depth * 0.5;
let steps_per_frame = 160;
let duration = 1e-9;
let width_px = from_m(width);
let depth_px = from_m(depth);
let size_px = Index((width_px, width_px, depth_px).into());
let mut driver: Driver<SimState<Real>> = Driver::new(size_px, feat_size);
let base = "toroid25d-9";
let _ = std::fs::create_dir(base);
let prefix = format!("{}/{}-flt{}-{}-feat{}um-{}mA-{}ps--radii{}um-{}um-{}um-{}um",
base,
base,
std::mem::size_of::<Real>() * 8,
*size_px,
m_to_um(feat_size),
(peak_current * 1e3) as i64,
(current_duration * 1e12) as i64,
m_to_um(conductor_outer_rad),
m_to_um(ferro_inner_rad),
m_to_um(ferro_outer_rad),
m_to_um(ferro_depth),
);
let _ = std::fs::create_dir(&prefix);
//driver.add_y4m_renderer(&*format!("{}.y4m", prefix), steps_per_frame);
//driver.add_plotly_renderer(&*format!("{}/frame-", prefix), steps_per_frame);
driver.add_serializer_renderer(&*format!("{}/frame-", prefix), steps_per_frame, None);
let conductor_region = CylinderZ::new(
Vec2::new(half_width, half_width),
conductor_outer_rad);
// driver.add_term_renderer(steps_per_frame);
driver.add_measurement(meas::Label(format!("Conductivity: {}, Imax: {:.2e}", conductivity, peak_current)));
//driver.add_measurement(meas::Current(conductor_region.clone()));
driver.add_measurement(meas::MagnetizationAt(
Meters::new(half_width + ferro_inner_rad + 2.0*feat_size, half_width, half_depth)
));
driver.add_measurement(meas::MagneticFluxAt(
Meters::new(half_width + ferro_inner_rad + 2.0*feat_size, half_width, half_depth)
));
driver.add_measurement(meas::MagneticStrengthAt(
Meters::new(half_width + ferro_inner_rad + 2.0*feat_size, half_width, half_depth)
));
driver.add_measurement(meas::MagnetizationAt(
Meters::new(half_width + ferro_inner_rad + 1.0*feat_size, half_width, half_depth)
));
driver.add_measurement(meas::MagneticFluxAt(
Meters::new(half_width + ferro_inner_rad + 1.0*feat_size, half_width, half_depth)
));
driver.add_measurement(meas::MagneticStrengthAt(
Meters::new(half_width + ferro_inner_rad + 1.0*feat_size, half_width, half_depth)
));
driver.add_measurement(meas::MagnetizationAt(
Meters::new(half_width + 0.5 * (ferro_inner_rad + ferro_outer_rad), half_width, half_depth)
));
driver.add_measurement(meas::MagneticFluxAt(
Meters::new(half_width + 0.5 * (ferro_inner_rad + ferro_outer_rad), half_width, half_depth)
));
driver.add_measurement(meas::MagneticStrengthAt(
Meters::new(half_width + 0.5 * (ferro_inner_rad + ferro_outer_rad), half_width, half_depth)
));
let center = Vec2::new(half_width, half_width);
for z_px in 0..depth_px {
for y_px in 0..width_px {
for x_px in 0..width_px {
let loc = Index((x_px, y_px, z_px).into());
let d = Vec2::new(to_m(x_px), to_m(y_px)) - center;
let r = d.mag();
if (conductor_inner_rad..conductor_outer_rad).contains(&r) {
driver.state.put_material(loc, mat::IsomorphicConductor::new(conductivity));
} else if (ferro_inner_rad..ferro_outer_rad).contains(&r) {
let half_depth_px = from_m(half_depth);
let ferro_depth_px = from_m(ferro_depth);
if (half_depth_px-ferro_depth_px/2 .. half_depth_px+(ferro_depth_px+1)/2).contains(&z_px) {
trace!("placing ferro at {:?}", loc);
driver.state.put_material(loc, mat::db::ferroxcube_3r1());
}
}
}
}
}
let boundary_xy = half_width - ferro_outer_rad - buffer;
println!("boundary: {}um", m_to_um(boundary_xy));
let boundary = Index((from_m(boundary_xy), from_m(boundary_xy), 0).into());
driver.add_pml_boundary(boundary);
driver.add_stimulus(Stimulus::new(
conductor_region.clone(),
Sinusoid3::from_wavelength(Vec3::new(0.0, 0.0, peak_current), current_duration * 2.0
)));
while driver.dyn_state().time() < duration {
driver.step();
}
}
}

View File

@@ -0,0 +1,135 @@
use coremem::{Driver, mat, meas, SimState, SpirvDriver};
use coremem::geom::{Index, Meters, Torus};
use coremem::stim::{CurlStimulus, Sinusoid1, TimeVarying as _};
use coremem::units::Seconds;
fn main() {
coremem::init_logging();
let feat_size = 10e-6f32; // feature size
let duration = 60.0e-9;
let width = 3400e-6;
let height = 1800e-6;
let depth = 1400e-6;
let buffer = 200e-6;
let ferro_spacing = 600e-6;
let ferro_major = 320e-6;
let ferro_minor = 60e-6;
let wire_minor = 40e-6;
let wire_major = 160e-6;
let peak_current1 = 7.5e6;
//let peak_current2 = 5e5;
let peak_current2 = 7.5e6;
let current_duration = 1.0e-9; // half-wavelength of the sine wave
// let current_break = 0.2e-9; // time between 'set' pulse and 'clear' pulse
let drive_conductivity = 5e6f32;
let sense_conductivity = 5e3f32;
let from_m = |m: f32| (m/feat_size).round() as u32;
let m_to_um = |m: f32| (m * 1e6).round() as u32;
let half_width = width * 0.5;
let half_height = height * 0.5;
let half_depth = depth * 0.5;
let width_px = from_m(width);
let height_px = from_m(height);
let depth_px = from_m(depth);
let size_px = Index((width_px, height_px, depth_px).into());
let mut driver: SpirvDriver = Driver::new_spirv(size_px, feat_size);
// let mut driver: Driver<SimState<f32, mat::GenericMaterial<f32>>> = Driver::new(size_px, feat_size);
driver.set_steps_per_stim(1000);
let base = "wrapped_torus-44-classical-boundary-spirv-100steps-stepfn-on-device-eh_reorder";
let ferro1_center = half_width - ferro_major - 0.5*ferro_spacing;
let ferro1_region = Torus::new_xy(Meters::new(ferro1_center, half_height, half_depth), ferro_major, ferro_minor);
let drive1_region = Torus::new_xz(Meters::new(ferro1_center - ferro_major, half_height, half_depth), wire_major, wire_minor);
let sense1_region = Torus::new_xz(Meters::new(ferro1_center + ferro_major, half_height, half_depth), wire_major, wire_minor);
//driver.fill_region(&ferro1_region, mat::db::linear_iron());
driver.fill_region(&ferro1_region, mat::MBFerromagnet::new(-0.3899, 0.3900, 310_000.0));
driver.fill_region(&drive1_region, mat::IsomorphicConductor::new(drive_conductivity));
driver.fill_region(&sense1_region, mat::IsomorphicConductor::new(sense_conductivity));
let ferro2_center = half_width + ferro_major + 0.5*ferro_spacing;
let ferro2_region = Torus::new_xy(Meters::new(ferro2_center, half_height, half_depth), ferro_major, ferro_minor);
let drive2_region = Torus::new_xz(Meters::new(ferro2_center - ferro_major, half_height, half_depth), wire_major, wire_minor);
let sense2_region = Torus::new_xz(Meters::new(ferro2_center + ferro_major, half_height, half_depth), wire_major, wire_minor);
driver.fill_region(&ferro2_region, mat::MBFerromagnet::new(-0.3899, 0.3900, 310_000.0));
driver.fill_region(&drive2_region, mat::IsomorphicConductor::new(drive_conductivity));
driver.fill_region(&sense2_region, mat::IsomorphicConductor::new(sense_conductivity));
let boundary_xy = ferro1_center - ferro_major - ferro_minor - buffer;
let boundary_z = half_depth - wire_major - wire_minor - buffer;
println!("boundary: {}um; {}um", m_to_um(boundary_xy), m_to_um(boundary_z));
driver.add_classical_boundary(Meters::new(boundary_xy, boundary_xy, boundary_z));
let mut add_drive_pulse = |region: &Torus, start, duration, amp| {
let wave = Sinusoid1::from_wavelength(amp, duration * 2.0)
.half_cycle()
.shifted(start);
driver.add_stimulus(CurlStimulus::new(
region.clone(),
wave.clone(),
region.center(),
region.axis()
));
};
// J=\sigma E
// dJ/dt = \sigma dE/dT
// dE/dt = dJ/dt / \sigma
// dE/dt = dI/dt / (A*\sigma)
// if I = k*sin(w t) then dE/dt = k*w sin(w t) / (A*\sigma)
// i.e. dE/dt is proportional to I/(A*\sigma), multiplied by w (or, divided by wavelength)
let peak_stim1 = peak_current1/current_duration / (drive1_region.cross_section() * drive_conductivity);
add_drive_pulse(&drive1_region, 0.0, current_duration, peak_stim1);
// add_drive_pulse(&drive1_region, current_duration + current_break, current_duration, -4.0*peak_stim1);
// add_drive_pulse(&drive1_region, 2.0*(current_duration + current_break), current_duration, -4.0*peak_stim1);
// add_drive_pulse(&drive1_region, 3.0*(current_duration + current_break), current_duration, peak_stim1);
let peak_stim2 = peak_current2/current_duration / (drive2_region.cross_section() * drive_conductivity);
add_drive_pulse(&drive2_region, 0.0, current_duration, peak_stim2);
// add_drive_pulse(&drive2_region, current_duration + current_break, current_duration, -4.0*peak_stim2);
// add_drive_pulse(&drive2_region, 2.0*(current_duration + current_break), current_duration, -4.0*peak_stim2);
// add_drive_pulse(&drive2_region, 3.0*(current_duration + current_break), current_duration, peak_stim2);
driver.add_measurement(meas::CurrentLoop::new("sense1", sense1_region.clone()));
driver.add_measurement(meas::Current::new("sense1", sense1_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::Magnetization::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::MagneticFlux::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("drive1", drive1_region.clone()));
driver.add_measurement(meas::Current::new("drive1", drive1_region.clone()));
driver.add_measurement(meas::Power::new("drive1", drive1_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("sense2", sense2_region.clone()));
driver.add_measurement(meas::Current::new("sense2", sense2_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem2", ferro2_region.clone()));
driver.add_measurement(meas::Magnetization::new("mem2", ferro2_region.clone()));
driver.add_measurement(meas::MagneticFlux::new("mem2", ferro2_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("drive2", drive2_region.clone()));
driver.add_measurement(meas::Current::new("drive2", drive2_region.clone()));
driver.add_measurement(meas::Power::new("drive2", drive2_region.clone()));
let prefix = format!("out/{}/{}-flt{}-{}-feat{}um-{}mA-{}ps--radii{}um-{}um-{}um-{}um",
base,
base,
32,
*size_px,
m_to_um(feat_size),
(peak_current1 * 1e3).round() as i64,
(current_duration * 1e12).round() as i64,
m_to_um(ferro_major),
m_to_um(ferro_minor),
m_to_um(wire_major),
m_to_um(wire_minor),
);
let _ = std::fs::create_dir_all(&prefix);
// driver.add_state_file(&*format!("{}/state.bc", prefix), 1000);
// driver.add_serializer_renderer(&*format!("{}/frame-", prefix), 1000);
driver.add_csv_renderer(&*format!("{}/meas.csv", prefix), 100, None);
driver.step_until(Seconds(duration));
}

View File

@@ -0,0 +1,739 @@
//! this "example" positions ferromagnetic buffers adjacently and uses an ASYMMETRIC coil winding
//! to couple them. i parameterize the entire setup over a bunch of different factors in order to
//! search for the conditions which maximize energy transfer from the one core to the other.
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::real::{R32, Real as _};
use coremem::render::CsvRenderer;
use coremem::stim::{CurlStimulus, Exp1, Gated, Sinusoid1, TimeVarying as _};
use coremem::sim::units::{Seconds, Frame, Time as _};
use coremem::sim::spirv;
use coremem::util::cache::DiskCache;
use log::{error, info, warn};
use serde::{Deserialize, Serialize};
type Mat = spirv::IsoConductorOr<Ferroxcube3R1MH>;
#[allow(unused)]
use coremem::geom::{Coord as _, Region as _};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum PulseType {
Square,
Sine,
/// "pulse" window represents 2 half-lifes
ExpDecay2x,
}
/// Return just the extrema of some collection
fn extrema(mut meas: Vec<f32>) -> Vec<f32> {
let mut i = 0;
while i + 2 < meas.len() {
let (prev, cur, next) = (meas[i], meas[i+1], meas[i+2]);
if (prev <= cur && cur <= next) || (prev >= cur && cur >= next) {
meas.remove(i+1);
} else {
i += 1;
}
}
meas
}
/// Return the (signed) peak magnitude and stable value
fn significa(meas: Vec<f32>) -> (f32, f32) {
let peak = meas.iter().max_by(|a, b| a.abs().partial_cmp(&b.abs()).unwrap()).copied().unwrap_or_default();
let stable = meas.last().copied().unwrap_or_default();
(peak, stable)
}
fn min_max(meas: Vec<f32>) -> (f32, f32) {
let min = meas.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).copied().unwrap_or_default();
let max = meas.iter().max_by(|a, b| a.partial_cmp(b).unwrap()).copied().unwrap_or_default();
(min, max)
}
#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd, Serialize, Deserialize)]
struct GeomParams {
feat_size: f32,
buffer_xy: f32,
buffer_z: f32,
boundary_xy: f32,
boundary_z: f32,
ferro_major: f32,
ferro_minor: f32,
ferro_buffer: f32, // horizontal space between ferros,
wire_minor: f32,
wire_wrap_minor: f32, // 2x wire_wrap_minor + feat_size must be < ferro_buffer
wire_set_major: f32,
wire_wrap_dilation: f32,
wire_wrap_iters: usize,
wraps1: f32,
wrap1_coverage: f32,
wraps2: f32,
wrap2_coverage: f32,
}
// impl GeomParams {
// fn key(&self) -> String {
// let GeomParams {
// feat_size,
// buffer_xy,
// buffer_z,
// boundary_xy,
// boundary_z,
// ferro_major,
// ferro_minor,
// ferro_buffer,
// wire_wrap_minor,
// wire_set_major,
// wire_wrap_dilation,
// wire_wrap_iters,
// wraps1,
// wrap1_coverage,
// wraps2,
// wrap2_coverage
// } = self;
// format!(
// "{feat_size}um-{buffer_xy}x{buffer_z}buf-{boundary_xy}x{boundary_z}bound-{ferro_major}ferromaj-{ferro_minor}min-{ferro_buffer}buf-{wire_wrap_major}:{wire_set_major}:{wire_wrap_dilation}:{wire_wrap_iters}wrap-{wraps1}:{wraps2}wraps-{wrap1_coverage}:{wrap2_coverage}cov",
// feat_size=feat_size*1e6,
// buffer_xy=buffer_xy,
// buffer_z=buffer_z,
// boundary_xy=boundary_xy,
// boundary_z=boundary_z,
// ferro_major=ferro_major,
// ferro_minor=ferro_minor,
// ferro_buffer=ferro_buffer,
// wire_wrap_minor=wire_wrap_minor,
// wire_set_major=wire_set_major,
// wire_wrap_dilation=wire_wrap_dilation,
// wire_wrap_iters=wire_wrap_iters,
// wraps1=wraps1,
// wrap1_coverage=wrap1_coverage,
// wraps2=wraps2,
// wrap2_coverage=wrap2_coverage,
// )
// }
// }
#[derive(Copy, Clone, Debug)]
struct Params {
dry_run: bool,
geom: GeomParams,
wire_conductivity: f32,
peak_set_current: f32,
peak_clock_current: f32,
set_duration: f32,
clock_duration: f32,
clock_type: PulseType,
pre_time: f32, // how long between set and clock
post_time: f32, // how long to wait after the clock
dump_frames: (u64, Option<u64>),
}
#[derive(Clone, Default, Serialize, Deserialize)]
struct Geometries {
dim: Meters,
ferro1_region: Torus,
ferro2_region: Torus,
set1_region: Torus,
set2_region: Torus,
coupling_region: region::Union,
coupling_wire_top: Cube,
coupling_wire_bot: Cube,
wrap1_len: f32,
wrap2_len: f32,
}
#[derive(Clone, Debug, Default)]
struct Results {
m1_peak: f32,
m2_peak: f32,
m1_stable: f32,
m2_stable: f32,
h1_peak: f32,
h2_max: f32,
h2_min: f32,
h1_stable: f32,
h2_stable: f32,
iset_min: f32,
iset_max: f32,
icoupling_peak: f32,
peak_m_ratio: f32,
stable_m_ratio: f32,
/// m2_stable divided by m1_peak. i.e. "amplification"
m2_stable_m1_peak: f32,
t: f32,
}
fn derive_geometries(p: GeomParams) -> Option<Geometries> {
use std::f32::consts::PI;
let feat_sizes = Meters::new(p.feat_size, p.feat_size, p.feat_size);
let width = 4.0*p.ferro_major + 2.0*(p.buffer_xy + p.boundary_xy + p.wire_set_major + p.wire_minor) + p.ferro_buffer;
let height = 2.0*(p.ferro_major + p.ferro_minor + 4.0*p.wire_wrap_minor + 12.0*p.feat_size + p.buffer_xy + p.boundary_xy);
let depth = 2.0*(p.wire_set_major.max(4.0*p.wire_wrap_minor + p.ferro_minor + p.feat_size) + p.wire_minor + p.buffer_z + p.boundary_z);
let dim = Meters::new(width, height, depth);
let ferro1_center = Meters::new(
p.buffer_xy + p.boundary_xy + p.wire_set_major + p.wire_minor + p.ferro_major,
p.buffer_xy + p.boundary_xy + p.ferro_major + p.ferro_minor + 4.0*p.wire_wrap_minor + 12.0*p.feat_size,
0.5*depth,
// buffer_z + boundary_z + wire_set_major + wire_minor
);
let ferro2_center = ferro1_center + Meters::new(2.0*p.ferro_major + p.ferro_buffer, 0.0, 0.0);
let ferro_center = (ferro1_center + ferro2_center)*0.5;
// reserve the left/right locations for the SET wires.
let set1_center = ferro1_center - Meters::new_x(p.ferro_major);
let set2_center = ferro2_center + Meters::new_x(p.ferro_major);
let ferro1_region = Torus::new_xy(ferro1_center, p.ferro_major, p.ferro_minor);
let ferro2_region = Torus::new_xy(ferro2_center, p.ferro_major, p.ferro_minor);
let set1_region = Torus::new_xz(set1_center, p.wire_set_major, p.wire_minor);
let set2_region = Torus::new_xz(set2_center, p.wire_set_major, p.wire_minor);
let wrap1_rate = 2.0*p.wrap1_coverage/p.wraps1;
let coupling_region1 = Memoize::new(Dilate::new(
Wrap::new_about(
Translate::new(
SwapYZ::new(region::and(
Spiral::new(p.ferro_minor + 2.0*p.wire_wrap_minor + p.feat_size, p.wire_wrap_minor, wrap1_rate),
Cube::new(Meters::new(-1.0, -1.0, -p.wrap1_coverage), Meters::new(1.0, 1.0, p.wrap1_coverage))
)),
ferro1_center + Meters::new(1.0*p.ferro_major, 0.0, 0.0),
),
1.0, // one half-rev => y=1.0
ferro1_center,
),
p.wire_wrap_dilation,
p.wire_wrap_dilation / (p.wire_wrap_iters as f32),
));
let wrap2_rate = 2.0*p.wrap2_coverage/p.wraps2;
let coupling_region2 = Memoize::new(Dilate::new(
Wrap::new_about(
Translate::new(
SwapYZ::new(region::and_not(
Spiral::new(p.ferro_minor + 2.0*p.wire_wrap_minor + p.feat_size, p.wire_wrap_minor, wrap2_rate),
Cube::new(Meters::new(-1.0, -1.0, -1.0 + p.wrap2_coverage), Meters::new(1.0, 1.0, 1.0 - p.wrap2_coverage))
)),
ferro2_center + Meters::new_x(p.ferro_major),
),
1.0, // one half-rev => y=1.0
ferro2_center,
),
p.wire_wrap_dilation,
p.wire_wrap_dilation / (p.wire_wrap_iters as f32),
));
let coupling_wire_top = Cube::new_centered(
ferro_center - Meters::new_y(p.ferro_major + 4.0*p.wire_wrap_minor + 12.0*p.feat_size),
Meters::new(p.ferro_buffer + 4.0*p.ferro_major + 4.0*p.feat_size, 2.0*p.feat_size, 2.0*p.feat_size)
);
let coupling_wire_bot = Cube::new_centered(
ferro_center + Meters::new_y(p.ferro_major + 4.0*p.wire_wrap_minor + 12.0*p.feat_size),
Meters::new(p.ferro_buffer + 4.0*p.ferro_major + 4.0*p.feat_size, 2.0*p.feat_size, 2.0*p.feat_size)
);
let wrap1_top = ferro1_center + Meters::new_x(p.ferro_major).rotate_z(-p.wrap1_coverage*PI);
let wrap1_bot = ferro1_center + Meters::new_x(p.ferro_major).rotate_z(p.wrap1_coverage*PI);
let wrap2_top = ferro2_center + Meters::new_x(p.ferro_major).rotate_z((1.0+p.wrap2_coverage)*PI);
let wrap2_bot = ferro2_center + Meters::new_x(p.ferro_major).rotate_z((1.0-p.wrap2_coverage)*PI);
let coupling_stub_top_left = Cube::new_including_negatives(
wrap1_top + feat_sizes*2.0,
wrap1_top.with_y(coupling_wire_top.bot()) - feat_sizes*2.0,
);
let coupling_stub_bot_left = Cube::new_including_negatives(
wrap1_bot - feat_sizes*2.0,
wrap1_bot.with_y(coupling_wire_bot.top()) + feat_sizes*2.0,
);
let coupling_stub_top_right = Cube::new_including_negatives(
wrap2_top + feat_sizes*2.0,
wrap2_top.with_y(coupling_wire_top.bot()) - feat_sizes*2.0,
);
let coupling_stub_bot_right = Cube::new_including_negatives(
wrap2_bot - feat_sizes*2.0,
wrap2_bot.with_y(coupling_wire_bot.top()) + feat_sizes*2.0,
);
let coupling_stubs = region::Union::new()
.with(coupling_stub_top_left.clone())
.with(coupling_stub_top_right.clone())
.with(coupling_stub_bot_left.clone())
.with(coupling_stub_bot_right.clone())
;
let coupling_wires = region::Union::new()
.with(coupling_wire_top.clone())
.with(coupling_wire_bot.clone())
.with(coupling_stubs.clone())
;
let coupling_region = region::Union::new()
.with(coupling_region1.clone())
.with(coupling_region2.clone())
.with(coupling_wires.clone())
;
let wrap1_with_coupling = region::union(
coupling_region1.clone(), coupling_wires.clone()
);
let wrap2_with_coupling = region::union(
coupling_region2.clone(), coupling_wires.clone()
);
// show that the coupling top/bot wires are connected through the wrapping
if !region::is_connected(
&wrap1_with_coupling,
coupling_wire_top.center(),
coupling_wire_bot.center(),
p.feat_size,
) {
warn!("wrap1 not connected for params: {:?}", p);
return None;
}
if !region::is_connected(
&wrap2_with_coupling,
coupling_wire_top.center(),
coupling_wire_bot.center(),
p.feat_size,
) {
warn!("wrap2 not connected for params: {:?}", p);
return None;
}
let wrap1_len = region::distance_to(
&wrap1_with_coupling,
coupling_stub_top_left.center().to_index(p.feat_size),
coupling_stub_bot_left.center().to_index(p.feat_size),
p.feat_size,
).unwrap();
let wrap2_len = region::distance_to(
&wrap2_with_coupling,
coupling_stub_top_right.center().to_index(p.feat_size),
coupling_stub_bot_right.center().to_index(p.feat_size),
p.feat_size,
).unwrap();
info!("wrap lengths: {}, {} (wraps: {}, {})", wrap1_len, wrap2_len, p.wraps1, p.wraps2);
Some(Geometries {
dim,
ferro1_region,
ferro2_region,
set1_region,
set2_region,
coupling_region,
coupling_wire_top,
coupling_wire_bot,
wrap1_len,
wrap2_len,
})
}
fn run_sim(id: u32, p: Params, g: Geometries) -> Results {
info!("run_sim {}: {:?}", id, p);
let m_to_um = |m: f32| (m * 1e6).round() as u32;
let feat_vol = p.geom.feat_size * p.geom.feat_size * p.geom.feat_size;
info!("boundary: {}um; {}um", m_to_um(p.geom.boundary_xy), m_to_um(p.geom.boundary_z));
info!("size: {:?}", g.dim);
info!("ferro1: {:?}", g.ferro1_region.center());
info!("ferro2: {:?}", g.ferro2_region.center());
let base = format!("buffer5-{}", id);
let prefix = format!("out/{}/{}-{}-{}setmA-{}setps-{}clkmA-{}clkps-{}um-{}ferromaj-{}:{}wraps-{}:{}cov-{:?}clk",
base,
base,
*g.dim.to_index(p.geom.feat_size),
(p.peak_set_current * 1e3).round() as i64,
(p.set_duration * 1e12).round() as i64,
(p.peak_clock_current * 1e3).round() as i64,
(p.clock_duration * 1e12).round() as i64,
(p.geom.feat_size * 1e6).round() as i64,
p.geom.ferro_major,
p.geom.wraps1,
p.geom.wraps2,
p.geom.wrap1_coverage,
p.geom.wrap2_coverage,
p.clock_type,
);
let mut driver: SpirvDriver<Mat> = Driver::new_spirv(g.dim, p.geom.feat_size);
driver.set_steps_per_stim(1000);
if !driver.add_state_file(&*format!("{}/state.bc", prefix), 16000) {
// mu_r=881.33, starting at H=25 to H=75.
let ferro_mat = mat::Ferroxcube3R1MH::new();
// let ferro_mat = mat::db::conductor(wire_conductivity);
let wire_mat = mat::IsomorphicConductor::new(p.wire_conductivity);
driver.fill_region(&g.ferro1_region, ferro_mat);
driver.fill_region(&g.ferro2_region, ferro_mat);
driver.fill_region(&g.set1_region, wire_mat);
driver.fill_region(&g.set2_region, wire_mat);
driver.fill_region(&g.coupling_region, wire_mat);
driver.add_classical_boundary(Meters::new(p.geom.boundary_xy, p.geom.boundary_xy, p.geom.boundary_z));
// assert!(driver.test_region_filled(&g.ferro1_region, ferro_mat));
// assert!(driver.test_region_filled(&g.ferro2_region, ferro_mat));
// assert!(driver.test_region_filled(&g.set1_region, wire_mat));
// assert!(driver.test_region_filled(&g.set2_region, wire_mat));
// assert!(driver.test_region_filled(&g.coupling_region, wire_mat));
} else {
info!("loaded state file: skipping geometry calculations");
}
let add_drive_sine_pulse = |driver: &mut SpirvDriver<Mat>, region: &Torus, start: f32, duration: f32, amp: f32| {
let wave = Sinusoid1::from_wavelength(amp, duration * 2.0)
.half_cycle()
.shifted(start);
driver.add_stimulus(CurlStimulus::new(
region.clone(),
wave.clone(),
region.center(),
region.axis()
));
};
let add_drive_square_pulse = |driver: &mut SpirvDriver<Mat>, region: &Torus, start: f32, duration: f32, amp: f32| {
let wave = Gated::new(amp, start, start+duration);
driver.add_stimulus(CurlStimulus::new(
region.clone(),
wave.clone(),
region.center(),
region.axis()
));
};
let add_drive_exp_pulse = |driver: &mut SpirvDriver<Mat>, region: &Torus, start: f32, duration: f32, amp: f32| {
let wave = Exp1::new_at(amp, start, 0.5*duration);
driver.add_stimulus(CurlStimulus::new(
region.clone(),
wave.clone(),
region.center(),
region.axis()
));
};
let add_drive_step = |driver: &mut SpirvDriver<Mat>, region: &Torus, start: f32, amp: f32| {
add_drive_square_pulse(driver, region, start, 1.0, amp);
};
let add_drive_pulse = |ty: PulseType, driver: &mut SpirvDriver<Mat>, region: &Torus, start: f32, duration: f32, amp: f32| {
match ty {
PulseType::Square => add_drive_square_pulse(driver, region, start, duration, amp),
PulseType::Sine => add_drive_sine_pulse(driver, region, start, duration, amp),
PulseType::ExpDecay2x => add_drive_exp_pulse(driver, region, start, duration, amp),
}
};
// J=\sigma E
// dJ/dt = \sigma dE/dT
// dE/dt = dJ/dt / \sigma
// dE/dt = dI/dt / (A*\sigma)
// if I = k*sin(w t) then dE/dt = k*w cos(w t) / (A*\sigma)
// i.e. dE/dt is proportional to I/(A*\sigma), multiplied by w (or, divided by wavelength)
let peak_set = p.peak_set_current / feat_vol / (g.set1_region.cross_section() * p.wire_conductivity);
let peak_clock = p.peak_clock_current / feat_vol / (g.set1_region.cross_section() * p.wire_conductivity);
// SET cores
add_drive_sine_pulse(&mut driver, &g.set1_region, 0.01*p.set_duration, p.set_duration, -peak_set);
add_drive_sine_pulse(&mut driver, &g.set2_region, 0.01*p.set_duration, p.set_duration, peak_set);
// CLEAR core1
add_drive_pulse(p.clock_type, &mut driver, &g.set1_region, p.set_duration + p.pre_time, p.clock_duration, peak_clock);
// add_drive_step(&mut driver, &set1_region, set_duration + pre_time, peak_clock);
let duration = Seconds(p.set_duration + p.pre_time + p.clock_duration + p.post_time)
.to_frame(driver.timestep())
.round_up(32000);
driver.add_measurement(meas::Volume::new("mem1", g.ferro1_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem1", g.ferro1_region.clone()));
driver.add_measurement(meas::Volume::new("mem2", g.ferro2_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem2", g.ferro2_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("set1", g.set1_region.clone()));
driver.add_measurement(meas::Power::new("set1", g.set1_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("set2", g.set2_region.clone()));
// driver.add_measurement(meas::CurrentLoop::new("coupling1", coupling_region1.clone()));
// driver.add_measurement(meas::CurrentLoop::new("coupling2", coupling_region2.clone()));
driver.add_measurement(meas::Current::new("couplingtop", g.coupling_wire_top.clone()));
driver.add_measurement(meas::Current::new("couplingbot", g.coupling_wire_bot.clone()));
if p.dry_run {
info!("bailing (dry run): {}", prefix);
return Results::default();
}
let _ = std::fs::create_dir_all(&prefix);
let (frame_freq, frame_limit) = p.dump_frames;
driver.add_serializer_renderer(&*format!("{}/frame-", prefix), frame_freq, frame_limit);
let meas_csv = format!("{}/meas.csv", prefix);
let meas_sparse_csv = format!("{}/meas-sparse.csv", prefix);
driver.add_csv_renderer(&*meas_csv, 400, None);
driver.add_csv_renderer(&*meas_sparse_csv, 8000, None);
driver.step_until(duration);
let (m1_peak, m1_stable) = significa(CsvRenderer::new(&*meas_sparse_csv).read_column_as_f32("M(mem1)"));
let (m2_peak, m2_stable) = significa(CsvRenderer::new(&*meas_sparse_csv).read_column_as_f32("M(mem2)"));
let (h1_peak, h1_stable) = significa(CsvRenderer::new(&*meas_sparse_csv).read_column_as_f32("H(mem1)"));
let (_h2_peak, h2_stable) = significa(CsvRenderer::new(&*meas_sparse_csv).read_column_as_f32("H(mem2)"));
let (h2_min, h2_max) = min_max(CsvRenderer::new(&*meas_sparse_csv).read_column_as_f32("H(mem2)"));
let (iset_min, iset_max) = min_max(CsvRenderer::new(&*meas_sparse_csv).read_column_as_f32("I(set1)"));
let (icoupling_peak, _icoupling_stable) = significa(CsvRenderer::new(&*meas_sparse_csv).read_column_as_f32("Imag/cell(couplingtop)"));
let res = Results {
m1_peak,
m2_peak,
m1_stable,
m2_stable,
h1_peak,
h2_min,
h2_max,
h1_stable,
h2_stable,
iset_min,
iset_max,
icoupling_peak,
peak_m_ratio: m2_peak / m1_peak,
stable_m_ratio: m2_stable / m1_stable,
m2_stable_m1_peak: m2_stable / m1_peak,
t: driver.time(),
};
std::fs::write(
format!("{}/results.txt", prefix),
format!("{:#?}\n", res),
).unwrap();
info!("completed sim: {}", prefix);
res
}
fn main() {
let um = 1e-6;
let ns = 1e-9;
coremem::init_logging();
let i = 63;
let dry_run = false;
let mut variants = Vec::new();
// old variants:
// for wrap1_density in [1.0, 0.5, 0.2] {
// for wrap2_density in [1.0, 0.5, 0.2, 0.0] {
// for (wrap1_cov, wrap2_cov) in [(0.8, 0.8), (0.8, 0.25), (0.25, 0.8)] {
let clock_domain = [
(25600.0, 5.0 * ns),
// (1600.0, 5.0 * ns), // low relevance for large cores
// (1600.0, 1.0 * ns), // very poor perf (0.05 m2_stable_m1_peak)
(25600.0, 1.0 * ns),
// (6400.0, 1.0 * ns), // low relevance for large cores
// (51200.0, 1.0 * ns),
(102400.0, 1.0 * ns),
(51200.0, 5.0 * ns),
(12800.0, 5.0 * ns),
// TODO: RE-ENABLE
// (6400.0, 25.0 * ns), // suspended because costly
(12800.0, 25.0 * ns), // suspended because costly
// (25600.0, 25.0 * ns), // suspended because costly
// (6400.0, 5.0 * ns), // low relevance for large cores (for >= 9mm rad, <0.5 m2_stable_m1_peak)
// (409600.0, 1.0 * ns), // I(set1) shows significant underdamping => bad m2_stable_m1_peak
// (400.0, 25.0 * ns), // poor perf (0.28 m2_stable_m1_peak)
// (1600.0, 25.0 * ns), // mediocre perf (0.65 m2_stable_m1_peak)
// (1600.0, 100.0 * ns),
// (6400.0, 100.0 * ns),
// (400.0, 100.0 * ns),
// (400.0, 25.0 * ns),
// (100.0, 100e-9),
// (25.0, 25e-9),
// (25.0, 200e-9), // if we see a good trend here, scale this further
// (100.0, 25e-9),
// (1600.0, 25e-9),
];
let ferro_majors = [
11000.0 * um,
// 1360.0 * um,
9000.0 * um,
// 13500.0 * um, // fails geom test?
6000.0 * um,
4080.0 * um,
// 2320.0 * um,
// 3200.0 * um,
// 1680.0 * um
];
let wrap2_densities = [
-0.3,
-0.5,
-0.15,
-0.05,
// 0.3,
// 0.2,
// 0.15,
// 0.05,
// 0.0,
];
let clock_types = [
// PulseType::Sine,
PulseType::ExpDecay2x,
// PulseType::Square
];
let post_times = [
// 10ns is enough to see m2 peak
// 10.0 * ns, // completed
// 20.0 * ns,
// 50ns is 4 extra half-lifes for a 25ns exp decay
50.0 * ns,
// 125ns is a midpoint. we should be able to guess m2 stabilization *trends*
125.0 * ns,
// 275ns is enough to see m2 stabilize
275.0 * ns,
];
let wrap1_density = 1.0;
let (wrap1_cov, wrap2_cov) = (0.8, 0.8);
for post_time in post_times {
for ferro_major in ferro_majors {
for wrap2_density in wrap2_densities {
for (peak_clock_current, clock_duration) in clock_domain {
for clock_type in clock_types {
variants.push((
peak_clock_current,
clock_duration,
post_time,
clock_type,
ferro_major,
wrap1_cov,
wrap2_cov,
wrap1_density,
wrap2_density
));
}
}
}
}
}
info!(
"evaluating {} variants ({} time increments of {} primitives)",
variants.len(),
post_times.len(),
variants.len() / post_times.len(),
);
let mut geom_cache = DiskCache::new_with_supplier(
&format!("out/buffer5-{}/.geom_cache", i),
|geom: &GeomParams| derive_geometries(geom.clone())
);
for (peak_clock_current, clock_duration, post_time, clock_type, ferro_major, wrap1_coverage, wrap2_coverage, wrap1_density, wrap2_density) in variants {
info!("{}A/{}s {}s {}m {}:{}cov {}:{}density", peak_clock_current, clock_duration, post_time, ferro_major, wrap1_coverage, wrap2_coverage, wrap1_density, wrap2_density);
let base_params = Params {
dry_run,
geom: GeomParams {
feat_size: 40e-6f32,
buffer_xy: 160e-6,
buffer_z: 160e-6,
boundary_xy: 320e-6,
boundary_z: 320e-6,
ferro_major,
ferro_minor: 60e-6,
ferro_buffer: 1320e-6,
wire_minor: 40e-6,
wire_wrap_minor: 50e-6,
wire_set_major: 200e-6,
wire_wrap_dilation: 50e-6,
wire_wrap_iters: 3,
wraps1: 8.0,
wraps2: 8.0,
wrap1_coverage,
wrap2_coverage,
},
wire_conductivity: 5e6f32,
peak_set_current: 60.0,
peak_clock_current,
set_duration: 0e-9,
clock_duration,
clock_type,
pre_time: 1e-9,
post_time,
dump_frames: (256000, Some(257000)),
};
let wraps1_choices: Vec<_> = (-120..120)
.into_iter()
.filter_map(|wraps1| {
let params = GeomParams {
wraps1: (wraps1 * 4) as f32,
..base_params.geom
};
let geoms = geom_cache.get_or_insert_from_supplier(params.clone())?;
Some((params, geoms))
})
.collect();
let wraps1_max = wraps1_choices
.iter()
.max_by_key(|(_p, geoms)| R32::from_primitive(geoms.wrap1_len))
.cloned()
.unwrap_or_default()
.0.wraps1;
let wraps1_target = wraps1_max * wrap1_density;
let wraps1 = wraps1_choices
.iter()
.min_by_key(|(p, _g)| R32::from_primitive((p.wraps1 - wraps1_target).abs()))
.cloned()
.unwrap_or_default()
.0.wraps1;
let wraps2_choices: Vec<_> = (-120..120)
.into_iter()
.filter_map(|wraps2| {
let params = GeomParams {
wraps2: (wraps2 * 4) as f32,
..base_params.geom
};
let geoms = geom_cache.get_or_insert_from_supplier(params.clone())?;
Some((params, geoms))
})
.collect();
let wraps2_max = wraps2_choices
.iter()
.max_by_key(|(_p, geoms)| R32::from_primitive(geoms.wrap2_len))
.cloned()
.unwrap_or_default()
.0.wraps2;
let wraps2_target = wraps2_max * wrap2_density;
let wraps2 = wraps2_choices
.iter()
.min_by_key(|(p, _g)| R32::from_primitive((p.wraps2 - wraps2_target).abs()))
.cloned()
.unwrap_or_default()
.0.wraps2;
let mut params = base_params;
params.geom.wraps1 = wraps1;
params.geom.wraps2 = wraps2;
match geom_cache.get_or_insert_from_supplier(params.geom.clone()) {
Some(geoms) => {
run_sim(i, params, geoms);
},
None => error!("skipping sim because no valid geometry: {:?}", params),
}
}
info!("done");
}

View File

@@ -0,0 +1,150 @@
/// this example creates a "set/reset" latch from a non-linear ferromagnetic device.
/// this is quite a bit like a "core memory" device.
/// the SR latch in this example is wired to a downstream latch, mostly to show that it's
/// possible to transfer the state (with some limitation) from one latch to another.
use coremem::{Driver, mat, meas, SpirvDriver};
use coremem::geom::{Meters, Torus};
use coremem::sim::spirv;
use coremem::sim::units::Seconds;
use coremem::stim::{CurlStimulus, Sinusoid1, TimeVarying as _};
fn main() {
coremem::init_logging();
// feature size: the side-length of each discrete grid cell to model (in Meters)
let feat_size = 20e-6f32;
// parameters used below to describe the components we construct below. units are (M, A or S).
let depth = 1600e-6;
// closest distance between the non-vacuum component and the dissipating boundary
// longer distances cause boundary reflections to be more dissipated (generally good).
let buffer_xy = 240e-6;
// length of our energy-dissipating boundary. longer distances decrease boundary reflections (good)
let boundary_xy = 500e-6;
let boundary_z = 300e-6;
// geometry parameters for the ferrite cores (modeled as torii)
let ferro_major = 320e-6;
let ferro_minor = 60e-6;
let ferro_buffer = 60e-6;
// geometry parameters for the coupling, drive, and sense wires (modeled as torii)
let wire_minor = 40e-6;
let wire_major = 160e-6;
let wire_coupling_major = 280e-6; // (ferro_minor*4 + ferro_buffer)/2 + wire_minor = 190
let peak_current = 7.5e6;
let current_duration = 6.0e-9; // half-wavelength of the sine wave
let drive_conductivity = 5e6f32;
let sense_conductivity = 5e3f32;
let half_depth = depth * 0.5;
// intermediate computed geometric parameters
let ferro_top_mid = boundary_xy + buffer_xy + wire_minor + wire_major;
let ferro_center_y = ferro_top_mid + ferro_major;
let ferro_bot_mid = ferro_center_y + ferro_major;
let height = ferro_bot_mid + wire_major + wire_minor + buffer_xy + boundary_xy;
let ferro1_left_edge = boundary_xy + buffer_xy;
let ferro1_center = ferro1_left_edge + ferro_minor + ferro_major;
let ferro1_right_edge = ferro1_center + ferro_major + ferro_minor;
let ferro2_left_edge = ferro1_right_edge + ferro_buffer;
let ferro2_center = ferro2_left_edge + ferro_minor + ferro_major;
let ferro2_right_edge = ferro2_center + ferro_major + ferro_minor;
let width = ferro2_right_edge + wire_major + wire_minor + buffer_xy + boundary_xy;
// create actual Regions from the computed parameters
let ferro1_region = Torus::new_xy(Meters::new(ferro1_center, ferro_center_y, half_depth), ferro_major, ferro_minor);
let ferro2_region = Torus::new_xy(Meters::new(ferro2_center, ferro_center_y, half_depth), ferro_major, ferro_minor);
let set_region = Torus::new_yz(Meters::new(ferro1_center, ferro_center_y - ferro_major, half_depth), wire_major, wire_minor);
let reset_region = Torus::new_yz(Meters::new(ferro1_center, ferro_center_y + ferro_major, half_depth), wire_major, wire_minor);
let coupling_region = Torus::new_xz(Meters::new(0.5*(ferro1_center + ferro2_center), ferro_center_y, half_depth), wire_coupling_major, wire_minor);
let sense_region = Torus::new_xz(Meters::new(ferro2_center + ferro_major, ferro_center_y, half_depth), wire_major, wire_minor);
let mut driver: SpirvDriver<spirv::FullyGenericMaterial> = Driver::new_spirv(Meters::new(width, height, depth), feat_size);
// mu_r=881.33, starting at H=25 to H=75.
driver.fill_region(&ferro1_region, mat::MHPgram::new(25.0, 881.33, 44000.0));
driver.fill_region(&ferro2_region, mat::MHPgram::new(25.0, 881.33, 44000.0));
driver.fill_region(&set_region, mat::IsomorphicConductor::new(drive_conductivity));
driver.fill_region(&reset_region, mat::IsomorphicConductor::new(drive_conductivity));
driver.fill_region(&coupling_region, mat::IsomorphicConductor::new(drive_conductivity));
driver.fill_region(&sense_region, mat::IsomorphicConductor::new(sense_conductivity));
// fill the edge of the simulation with a graded conductor:
// on the inside of the simulation it matches vacuum (conductivity of 0), and
// ramps up conductivity (to dissipate energy) as it approaches the edge of the simulation.
driver.add_classical_boundary(Meters::new(boundary_xy, boundary_xy, boundary_z));
// helper to schedule a stimulus at the provided start time/duration.
let mut add_drive_pulse = |region: &Torus, start, duration, amp| {
let wave = Sinusoid1::from_wavelength(amp, duration * 2.0)
.half_cycle()
.shifted(start);
driver.add_stimulus(CurlStimulus::new(
region.clone(),
wave.clone(),
region.center(),
region.axis()
));
};
// stimuli apply some delta_E to the simulation, so we need to map our current to E:
// J=\sigma E
// dJ/dt = \sigma dE/dT
// dE/dt = dJ/dt / \sigma
// dE/dt = dI/dt / (A*\sigma)
// if I = k*sin(w t) then dE/dt = k*w cos(w t) / (A*\sigma)
// i.e. dE/dt is proportional to I/(A*\sigma), multiplied by w (or, divided by wavelength)
let peak_stim1 = peak_current/current_duration / (set_region.cross_section() * drive_conductivity);
// pulse the SET wire near the start of the simulation
add_drive_pulse(&set_region, 0.01*current_duration, current_duration, peak_stim1);
// RESET
add_drive_pulse(&reset_region, 4.0*current_duration, current_duration, peak_stim1);
// TOGGLE
add_drive_pulse(&set_region, 8.0*current_duration, current_duration, peak_stim1);
add_drive_pulse(&reset_region, 10.0*current_duration, current_duration, peak_stim1);
// TOGETHER
add_drive_pulse(&reset_region, 14.0*current_duration, current_duration, peak_stim1);
add_drive_pulse(&set_region, 14.0*current_duration, current_duration, peak_stim1);
// SET TWICE
add_drive_pulse(&set_region, 18.0*current_duration, current_duration, peak_stim1);
add_drive_pulse(&set_region, 20.0*current_duration, current_duration, peak_stim1);
let duration = 25.0*current_duration;
// measure a bunch of items of interest throughout the whole simulation duration:
driver.add_measurement(meas::CurrentLoop::new("coupling", coupling_region.clone()));
driver.add_measurement(meas::Current::new("coupling", coupling_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("sense", sense_region.clone()));
driver.add_measurement(meas::Current::new("sense", sense_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::Magnetization::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::MagneticFlux::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem2", ferro2_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("set", set_region.clone()));
driver.add_measurement(meas::Current::new("set", set_region.clone()));
driver.add_measurement(meas::Power::new("set", set_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("reset", reset_region.clone()));
// XXX: if you change any parameters (above), then change this prefix. otherwise simulations
// will try to load state generated by an earlier run and use it to compute the current
// (differently-parameterized) run.
let prefix = "out/examples/sr-latch/";
let _ = std::fs::create_dir_all(&prefix);
// add a state file for easy resumption
driver.add_state_file(&*format!("{}state.bc", prefix), 9600);
// serialize frames for later viewing with `cargo run --release --bin viewer`
driver.add_serializer_renderer(&*format!("{}frame-", prefix), 36000, None);
// render a couple CSV files: one very detailed and the other more sparsely detailed
driver.add_csv_renderer(&*format!("{}meas.csv", prefix), 200, None);
driver.add_csv_renderer(&*format!("{}meas-sparse.csv", prefix), 1600, None);
// how frequently to re-evaluate the stimulus (Sample & Hold interpolation between evaluations)
driver.set_steps_per_stim(1000);
driver.step_until(Seconds(duration));
}

View File

@@ -0,0 +1,62 @@
//! this example runs a *2-dimensional* simulation.
//! it places a dissipative material (i.e. a conductor) on the left edge of the simulation
//! and then radiates a vertical wavefront from the center of the simulation.
//! it's about the bare-minimum simulation which still does something interesting.
//!
//! note that any practical simulation should probably terminate the simulation space
//! with something that absorbs energy. since this example doesn't, it lets you see what
//! happens when you just use the default boundary conditions.
use coremem::{mat, driver};
use coremem::geom::{Coord as _, Cube, Index, Vec3};
use coremem::units::Seconds;
use coremem::stim::{Stimulus, TimeVarying as _, UniformStimulus};
fn main() {
coremem::init_logging();
// create a 2d simulation with so many grid cells
let width = 401;
let height = 401;
let size = Index::new(width, height, 1 /* depth */);
// each cell represents 1um x 1um x 1um volume
let feature_size = 1e-6;
// Create the simulation "driver" which uses the CPU as backend.
// by default all the computations are done with R32: a f32 which panics on NaN/Inf
// you can parameterize it to use R64, or unchecked f32 -- see src/driver.rs for the definition
let mut driver: driver::CpuDriver = driver::Driver::new(size, feature_size);
// uncomment to use the Spirv/GPU driver. this one is restricted to unchecked f32.
// note: this won't have better perf unless you reduce the y4m/term renderer framerate below.
// let mut driver: driver::SpirvDriver = driver::Driver::new_spirv(size, feature_size);
// create a conductor on the left side.
let conductor = Cube::new(
Index::new(0, 0, 0).to_meters(feature_size),
Index::new(width/10, height, 1).to_meters(feature_size),
);
driver.fill_region(&conductor, mat::IsomorphicConductor::new(200f32));
// create a vertical strip in the center of the simulation which emits a wave.
let center_region = Cube::new(
Index::new(200, height/4, 0).to_meters(feature_size),
Index::new(201, height*3/4, 1).to_meters(feature_size),
);
// emit a constant E/H delta over this region for 100 femtoseconds
let stim = Stimulus::new(
center_region,
UniformStimulus::new(
Vec3::new(2e19, 0.0, 0.0), // E field (per second)
Vec3::new(0.0, 0.0, 2e19/376.730) // H field (per second)
).gated(0.0, 100e-15),
);
driver.add_stimulus(stim);
// render the output to a video and to the terminal (low-res)
let _ = std::fs::create_dir_all("out/examples/wavefront");
driver.add_y4m_renderer("out/examples/wavefront/rendered.y4m", 1, None);
driver.add_term_renderer(4, None);
// finally, run the simulation:
driver.step_until(Seconds(100e-12));
}