make all the examples be their own binary crates
this achieves a few things: - trivial way to get these shipped as the default nix package - better dependency management - ability to split large applications into multiple files the README probably needs some updating.
This commit is contained in:
4
crates/applications/archive/README.md
Normal file
4
crates/applications/archive/README.md
Normal 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.
|
152
crates/applications/archive/buffer_proto1.rs
Normal file
152
crates/applications/archive/buffer_proto1.rs
Normal 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));
|
||||
}
|
149
crates/applications/archive/buffer_proto2.rs
Normal file
149
crates/applications/archive/buffer_proto2.rs
Normal 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));
|
||||
}
|
171
crates/applications/archive/buffer_proto3.rs
Normal file
171
crates/applications/archive/buffer_proto3.rs
Normal 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));
|
||||
}
|
197
crates/applications/archive/buffer_proto4.rs
Normal file
197
crates/applications/archive/buffer_proto4.rs
Normal 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));
|
||||
}
|
124
crates/applications/archive/minimal_torus.rs
Normal file
124
crates/applications/archive/minimal_torus.rs
Normal 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));
|
||||
}
|
75
crates/applications/archive/pml.rs
Normal file
75
crates/applications/archive/pml.rs
Normal 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));
|
||||
}
|
124
crates/applications/archive/toroid25d.rs
Normal file
124
crates/applications/archive/toroid25d.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
135
crates/applications/archive/wrapped_torus.rs
Normal file
135
crates/applications/archive/wrapped_torus.rs
Normal 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));
|
||||
}
|
10
crates/applications/buffer_proto5/Cargo.toml
Normal file
10
crates/applications/buffer_proto5/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "buffer_proto5"
|
||||
version = "0.1.0"
|
||||
authors = ["Colin <colin@uninsane.org>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
coremem = { path = "../../coremem" }
|
||||
log = "0.4"
|
||||
serde = "1.0"
|
739
crates/applications/buffer_proto5/src/main.rs
Normal file
739
crates/applications/buffer_proto5/src/main.rs
Normal 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");
|
||||
}
|
8
crates/applications/sr_latch/Cargo.toml
Normal file
8
crates/applications/sr_latch/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "sr_latch"
|
||||
version = "0.1.0"
|
||||
authors = ["Colin <colin@uninsane.org>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
coremem = { path = "../../coremem" }
|
150
crates/applications/sr_latch/src/main.rs
Normal file
150
crates/applications/sr_latch/src/main.rs
Normal 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));
|
||||
}
|
8
crates/applications/wavefront/Cargo.toml
Normal file
8
crates/applications/wavefront/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "wavefront"
|
||||
version = "0.1.0"
|
||||
authors = ["Colin <colin@uninsane.org>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
coremem = { path = "../../coremem" }
|
62
crates/applications/wavefront/src/main.rs
Normal file
62
crates/applications/wavefront/src/main.rs
Normal 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));
|
||||
}
|
Reference in New Issue
Block a user