Files
fdtd-coremem/crates/coremem/examples/buffer_proto5.rs
colin 5b99d30cda restructure this multi-crate project to use Cargo's "workspace" feature
this solves an issue in the Nix build, where managing multiple
Cargo.lock files is otherwise tricky. it causes (or fails to fix?) an adjacent issue where
the spirv builder doesn't seem to have everything it needs vendored.
2022-07-05 17:34:21 -07:00

740 lines
27 KiB
Rust

//! 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");
}