Fold SampleableSim and MaterialSim into one

This commit is contained in:
colin 2022-07-28 16:41:32 -07:00
parent a49d9cd7a4
commit 6206569f4a
8 changed files with 137 additions and 149 deletions

View File

@ -1,4 +1,4 @@
use coremem::{self, Driver, GenericSim};
use coremem::{self, Driver, MaterialSim};
use coremem::sim::legacy::SimState;
use coremem::sim::spirv::{SpirvSim, WgpuBackend};
use coremem::cross::mat::FullyGenericMaterial;
@ -18,7 +18,7 @@ fn measure<F: FnMut()>(name: &str, n_times: u32, mut f: F) -> f32 {
avg
}
fn measure_steps<S: GenericSim + Clone + Default + Send + Sync + 'static>(name: &str, steps_per_call: u32, mut d: Driver<S>) {
fn measure_steps<S: MaterialSim + Clone + Default + Send + Sync + 'static>(name: &str, steps_per_call: u32, mut d: Driver<S>) {
measure(name, 100/steps_per_call, || d.step_multiple(steps_per_call));
}

View File

@ -3,7 +3,7 @@ use crate::mat;
use crate::meas::{self, AbstractMeasurement};
use crate::real::Real;
use crate::render::{self, MultiRenderer, Renderer};
use crate::sim::{GenericSim, MaterialSim, SampleableSim};
use crate::sim::{GenericSim, MaterialSim};
use crate::sim::units::{Frame, Time};
use crate::stim::AbstractStimulus;
use crate::cross::vec::Vec3;
@ -83,7 +83,7 @@ impl<S: MaterialSim> Driver<S> {
}
}
impl<S: SampleableSim> Driver<S> {
impl<S: GenericSim> Driver<S> {
pub fn size(&self) -> Index {
self.state.size()
}
@ -95,8 +95,8 @@ impl<S: SampleableSim> Driver<S> {
}
}
impl<S: SampleableSim + Send + Sync + 'static> Driver<S> {
pub fn dyn_state(&mut self) -> &mut dyn SampleableSim {
impl<S: GenericSim + Send + Sync + 'static> Driver<S> {
pub fn dyn_state(&mut self) -> &mut dyn GenericSim {
&mut self.state
}
@ -122,14 +122,14 @@ impl<S: SampleableSim + Send + Sync + 'static> Driver<S> {
}
}
impl<S: SampleableSim + Send + Sync + Serialize + 'static> Driver<S> {
impl<S: GenericSim + Send + Sync + Serialize + 'static> Driver<S> {
pub fn add_serializer_renderer(&mut self, out_base: &str, step_frequency: u64, frame_limit: Option<u64>) {
let fmt_str = format!("{out_base}{{step_no}}.bc", out_base=out_base);
self.add_renderer(render::SerializerRenderer::new_static(&*fmt_str), &*fmt_str, step_frequency, frame_limit);
}
}
impl<S: SampleableSim + Send + Sync + Serialize + for<'a> Deserialize<'a> + 'static> Driver<S> {
impl<S: GenericSim + Send + Sync + Serialize + for<'a> Deserialize<'a> + 'static> Driver<S> {
/// instruct the driver to periodically save the simulation state to the provided path.
/// also attempts to load an existing state file, returning `true` on success.
pub fn add_state_file(&mut self, state_file: &str, snapshot_frequency: u64) -> bool {
@ -142,7 +142,7 @@ impl<S: SampleableSim + Send + Sync + Serialize + for<'a> Deserialize<'a> + 'sta
}
}
impl<S: GenericSim + Clone + Default + Send + Sync + 'static> Driver<S> {
impl<S: MaterialSim + Clone + Default + Send + Sync + 'static> Driver<S> {
fn render(&mut self) {
let prep_start = Instant::now();
let their_state = self.state.clone();

View File

@ -1,7 +1,7 @@
use crate::geom::{Meters, Region, Torus, WorldRegion};
use crate::real::{Real as _, ToFloat as _};
use crate::cross::vec::Vec3;
use crate::sim::SampleableSim;
use crate::sim::GenericSim;
use dyn_clone::{self, DynClone};
use indexmap::IndexMap;
use serde::{Serialize, Deserialize};
@ -10,12 +10,12 @@ use serde::{Serialize, Deserialize};
// threads? i.e. Sync, and no Clone
#[typetag::serde(tag = "type")]
pub trait AbstractMeasurement: Send + Sync + DynClone {
fn eval(&self, state: &dyn SampleableSim) -> String;
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String>;
fn eval(&self, state: &dyn GenericSim) -> String;
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String>;
}
dyn_clone::clone_trait_object!(AbstractMeasurement);
pub fn eval_multiple_kv(state: &dyn SampleableSim, meas: &[Box<dyn AbstractMeasurement>]) -> IndexMap<String, String> {
pub fn eval_multiple_kv(state: &dyn GenericSim, meas: &[Box<dyn AbstractMeasurement>]) -> IndexMap<String, String> {
let mut r = IndexMap::new();
for m in meas {
let other = m.key_value(state);
@ -29,10 +29,10 @@ pub struct Time;
#[typetag::serde]
impl AbstractMeasurement for Time {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
format!("{:.3e}s (step {})", state.time(), state.step_no())
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
[
("step".to_string(), state.step_no().to_string()),
("time".to_string(), state.time().to_string()),
@ -45,10 +45,10 @@ pub struct Meta;
#[typetag::serde]
impl AbstractMeasurement for Meta {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
format!("{}x{}x{} feat: {:.1e}m", state.width(), state.height(), state.depth(), state.feature_size())
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
[
("width".to_string(), state.width().to_string()),
("height".to_string(), state.height().to_string()),
@ -69,10 +69,10 @@ impl Label {
#[typetag::serde]
impl AbstractMeasurement for Label {
fn eval(&self, _state: &dyn SampleableSim) -> String {
fn eval(&self, _state: &dyn GenericSim) -> String {
self.0.clone()
}
fn key_value(&self, _state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, _state: &dyn GenericSim) -> IndexMap<String, String> {
[
(self.0.clone(), self.0.clone()),
].into_iter().collect()
@ -93,7 +93,7 @@ impl Volume {
}
}
/// Returns the volume of the region, in units of um^3
fn data(&self, state: &dyn SampleableSim) -> f32 {
fn data(&self, state: &dyn GenericSim) -> f32 {
let feat_um = state.feature_size() as f64 * 1e6;
(state.volume_of_region(&*self.region) as f64 * feat_um * feat_um * feat_um) as f32
@ -102,13 +102,13 @@ impl Volume {
#[typetag::serde]
impl AbstractMeasurement for Volume {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
format!("Vol({}): {:.2e} um^3",
self.name,
self.data(state),
)
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
[
(format!("Vol({})", self.name), self.data(state).to_string()),
].into_iter().collect()
@ -128,7 +128,7 @@ impl Current {
region: Box::new(r)
}
}
fn data(&self, state: &dyn SampleableSim) -> (f32, Vec3<f32>) {
fn data(&self, state: &dyn GenericSim) -> (f32, Vec3<f32>) {
let FieldSample(volume, current_mag, current_vec) = state.map_sum_over_enumerated(&*self.region, |coord: Meters, _cell| {
let current = state.current(coord);
FieldSample(1, current.mag().cast(), current.cast())
@ -186,14 +186,14 @@ impl std::iter::Sum for FieldSamples<[FieldSample; 3]> {
#[typetag::serde]
impl AbstractMeasurement for Current {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
let (mean_current_mag, mean_current_vec) = self.data(state);
format!("I/cell({}): {:.2e} {:.2e}",
self.name,
mean_current_mag,
mean_current_vec)
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
let (mean_current_mag, mean_current_vec) = self.data(state);
[
(format!("Imag/cell({})", self.name), mean_current_mag.to_string()),
@ -216,7 +216,7 @@ impl CurrentLoop {
region: r,
}
}
fn data(&self, state: &dyn SampleableSim) -> f32 {
fn data(&self, state: &dyn GenericSim) -> f32 {
let FieldSample(volume, directed_current, _current_vec) = state.map_sum_over_enumerated(&self.region, |coord: Meters, _cell| {
let normal = self.region.axis();
let to_coord = *coord - *self.region.center();
@ -234,11 +234,11 @@ impl CurrentLoop {
#[typetag::serde]
impl AbstractMeasurement for CurrentLoop {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
let cross_sectional_current = self.data(state);
format!("I({}): {:.2e}", self.name, cross_sectional_current)
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
let cross_sectional_current = self.data(state);
[
(format!("I({})", self.name), cross_sectional_current.to_string()),
@ -261,7 +261,7 @@ impl MagneticLoop {
region: r,
}
}
fn data(&self, state: &dyn SampleableSim) -> (f32, f32, f32) {
fn data(&self, state: &dyn GenericSim) -> (f32, f32, f32) {
let FieldSamples([
FieldSample(volume, directed_m, _m_vec),
FieldSample(_, directed_b, _b_vec),
@ -303,7 +303,7 @@ impl MagneticLoop {
#[typetag::serde]
impl AbstractMeasurement for MagneticLoop {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
let (mean_directed_m, mean_directed_b, mean_directed_h) = self.data(state);
format!(
"M({}): {:.2e}; B({}): {:.2e}; H({}): {:.2e}",
@ -312,7 +312,7 @@ impl AbstractMeasurement for MagneticLoop {
self.name, mean_directed_h,
)
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
let (mean_directed_m, mean_directed_b, mean_directed_h) = self.data(state);
[
(format!("M({})", self.name), mean_directed_m.to_string()),
@ -336,7 +336,7 @@ impl MagneticFlux {
region: Box::new(r)
}
}
fn data(&self, state: &dyn SampleableSim) -> Vec3<f32> {
fn data(&self, state: &dyn GenericSim) -> Vec3<f32> {
let FieldSample(volume, _directed_mag, mag_vec) = state.map_sum_over(&*self.region, |cell| {
let b = cell.b();
let mag = b.mag();
@ -349,11 +349,11 @@ impl MagneticFlux {
#[typetag::serde]
impl AbstractMeasurement for MagneticFlux {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
let mean_mag = self.data(state);
format!("Bavg({}): {:.2e}", self.name, mean_mag)
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
let mean_mag = self.data(state);
[
(format!("Bavg({})", self.name), mean_mag.to_string()),
@ -375,7 +375,7 @@ impl Magnetization {
region: Box::new(r)
}
}
fn data(&self, state: &dyn SampleableSim) -> Vec3<f32> {
fn data(&self, state: &dyn GenericSim) -> Vec3<f32> {
let FieldSample(volume, _directed_mag, mag_vec) = state.map_sum_over(&*self.region, |cell| {
let m = cell.m();
let mag = m.mag();
@ -388,11 +388,11 @@ impl Magnetization {
#[typetag::serde]
impl AbstractMeasurement for Magnetization {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
let mean_mag = self.data(state);
format!("Mavg({}): {:.2e}", self.name, mean_mag)
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
let mean_mag = self.data(state);
[
(format!("Mavg({})", self.name), mean_mag.to_string()),
@ -410,11 +410,11 @@ pub struct MagnetizationAt(pub Meters);
#[typetag::serde]
impl AbstractMeasurement for MagnetizationAt {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
let m = state.sample(self.0).m();
format!("M{}: {:.2e}", loc(self.0), m)
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
let m = state.sample(self.0).m();
[
(format!("M{}", loc(self.0)), m.to_string()),
@ -428,11 +428,11 @@ pub struct MagneticFluxAt(pub Meters);
#[typetag::serde]
impl AbstractMeasurement for MagneticFluxAt {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
let b = state.sample(self.0).b();
format!("B{}: {:.2e}", loc(self.0), b)
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
let b = state.sample(self.0).b();
[
(format!("B{}", loc(self.0)), b.to_string()),
@ -446,11 +446,11 @@ pub struct MagneticStrengthAt(pub Meters);
#[typetag::serde]
impl AbstractMeasurement for MagneticStrengthAt {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
let h = state.sample(self.0).h();
format!("H{}: {:.2e}", loc(self.0), h)
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
let h = state.sample(self.0).h();
[
(format!("H{}", loc(self.0)), h.to_string()),
@ -463,11 +463,11 @@ pub struct ElectricField(pub Meters);
#[typetag::serde]
impl AbstractMeasurement for ElectricField {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
let e = state.sample(self.0).e();
format!("E{}: {:.2e}", loc(self.0), e)
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
let e = state.sample(self.0).e();
[
(format!("E{}", loc(self.0)), e.to_string()),
@ -491,7 +491,7 @@ impl Energy {
region: Box::new(region),
}
}
fn data(&self, state: &dyn SampleableSim) -> f32 {
fn data(&self, state: &dyn GenericSim) -> f32 {
// Potential energy stored in a E/M field:
// https://en.wikipedia.org/wiki/Magnetic_energy
// https://en.wikipedia.org/wiki/Electric_potential_energy#Energy_stored_in_an_electrostatic_field_distribution
@ -510,11 +510,11 @@ impl Energy {
#[typetag::serde]
impl AbstractMeasurement for Energy {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
let e = self.data(state);
format!("U({}): {:.2e}", self.name, e)
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
let e = self.data(state);
[
(format!("U({})", self.name), e.to_string()),
@ -538,7 +538,7 @@ impl Power {
region: Box::new(region),
}
}
fn data(&self, state: &dyn SampleableSim) -> f32 {
fn data(&self, state: &dyn GenericSim) -> f32 {
// Power is P = IV = A*J*V = L^2*J.(LE) = L^3 J.E
// where L is feature size.
#[allow(non_snake_case)]
@ -552,11 +552,11 @@ impl Power {
#[typetag::serde]
impl AbstractMeasurement for Power {
fn eval(&self, state: &dyn SampleableSim) -> String {
fn eval(&self, state: &dyn GenericSim) -> String {
let power = self.data(state);
format!("P({}): {:.2e}", self.name, power)
}
fn key_value(&self, state: &dyn SampleableSim) -> IndexMap<String, String> {
fn key_value(&self, state: &dyn GenericSim) -> IndexMap<String, String> {
let power = self.data(state);
[
(format!("P({})", self.name), power.to_string()),

View File

@ -1,7 +1,7 @@
use crate::geom::Meters;
use crate::real::ToFloat as _;
use crate::cross::vec::{Vec2, Vec3};
use crate::sim::{SampleableSim, Sample, StaticSim};
use crate::sim::{GenericSim, Sample, StaticSim};
use crate::meas::{self, AbstractMeasurement};
use crossterm::{cursor, QueueableCommand as _};
use crossterm::style::{style, Color, PrintStyledContent, Stylize as _};
@ -55,7 +55,7 @@ fn scale_vector(x: Vec2<f32>, typical_mag: f32) -> Vec2<f32> {
x.with_mag(new_mag)
}
fn im_size<S: SampleableSim>(state: &S, max_w: u32, max_h: u32) -> (u32, u32) {
fn im_size<S: GenericSim>(state: &S, max_w: u32, max_h: u32) -> (u32, u32) {
let mut width = max_w;
let mut height = width * state.height() / state.width();
if height > max_h {
@ -134,7 +134,7 @@ struct RenderSteps<'a, S> {
z: u32,
}
impl<'a, S: SampleableSim> RenderSteps<'a, S> {
impl<'a, S: GenericSim> RenderSteps<'a, S> {
/// Render using default configuration constants
fn render(state: &'a S, measurements: &'a [Box<dyn AbstractMeasurement>], z: u32) -> RgbImage {
Self::render_configured(state, measurements, z, (640, 480), RenderConfig::default())
@ -352,12 +352,12 @@ pub trait Renderer<S>: Send + Sync {
}
}
fn default_render_z_slice<S: SampleableSim, R: Renderer<S>>(
fn default_render_z_slice<S: GenericSim, R: Renderer<S>>(
me: &R, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig,
) {
me.render_with_image(state, &RenderSteps::render(state, measurements, z), measurements, config);
}
fn default_render<S: SampleableSim, R: Renderer<S>>(
fn default_render<S: GenericSim, R: Renderer<S>>(
me: &R, state: &S, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig
) {
me.render_z_slice(state, state.depth() / 2, measurements, config);
@ -386,7 +386,7 @@ fn default_render<S: SampleableSim, R: Renderer<S>>(
#[derive(Default)]
pub struct ColorTermRenderer;
impl<S: SampleableSim> Renderer<S> for ColorTermRenderer {
impl<S: GenericSim> Renderer<S> for ColorTermRenderer {
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
default_render(self, state, measurements, config)
}
@ -448,7 +448,7 @@ impl Y4MRenderer {
}
}
impl<S: SampleableSim> Renderer<S> for Y4MRenderer {
impl<S: GenericSim> Renderer<S> for Y4MRenderer {
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
default_render_z_slice(self, state, z, measurements, config)
}
@ -540,7 +540,7 @@ impl<S> MultiRenderer<S> {
}
}
impl<S: SampleableSim> Renderer<S> for MultiRenderer<S> {
impl<S: GenericSim> Renderer<S> for MultiRenderer<S> {
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
default_render_z_slice(self, state, z, measurements, config)
}
@ -567,10 +567,10 @@ pub struct SerializedFrame<S=StaticSim> {
pub measurements: Vec<Box<dyn AbstractMeasurement>>,
}
impl<S: SampleableSim> SerializedFrame<S> {
impl<S: GenericSim> SerializedFrame<S> {
pub fn to_static(self) -> SerializedFrame<StaticSim> {
SerializedFrame {
state: SampleableSim::to_static(&self.state),
state: GenericSim::to_static(&self.state),
measurements: self.measurements,
}
}
@ -605,7 +605,7 @@ impl SerializerRenderer {
}
impl SerializerRenderer {
fn serialize<S: SampleableSim + Serialize>(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>]) {
fn serialize<S: GenericSim + Serialize>(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>]) {
let frame = SerializedFrame {
state,
measurements: measurements.iter().cloned().collect(),
@ -616,13 +616,13 @@ impl SerializerRenderer {
bincode::serialize_into(out, &frame).unwrap();
}
pub fn try_load<S: SampleableSim + for <'a> Deserialize<'a>>(&self) -> Option<SerializedFrame<S>> {
pub fn try_load<S: GenericSim + for <'a> Deserialize<'a>>(&self) -> Option<SerializedFrame<S>> {
let mut reader = BufReader::new(File::open(&*self.fmt_str).ok()?);
bincode::deserialize_from(&mut reader).ok()
}
}
impl<S: SampleableSim + Serialize> Renderer<S> for SerializerRenderer {
impl<S: GenericSim + Serialize> Renderer<S> for SerializerRenderer {
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
default_render_z_slice(self, state, z, measurements, config)
}
@ -665,7 +665,7 @@ impl CsvRenderer {
}
}
impl<S: SampleableSim> Renderer<S> for CsvRenderer {
impl<S: GenericSim> Renderer<S> for CsvRenderer {
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
default_render_z_slice(self, state, z, measurements, config)
}

View File

@ -4,7 +4,7 @@ use crate::geom::{Coord, Index, Meters};
use crate::cross::real::{R32, Real};
use crate::cross::step::SimMeta;
use crate::cross::vec::{Vec3, Vec3u};
use crate::sim::{CellStateWithM, GenericSim, MaterialSim, Sample, SampleableSim, StaticSim};
use crate::sim::{CellStateWithM, GenericSim, MaterialSim, Sample, StaticSim};
use crate::stim::AbstractStimulus;
use mat::{GenericMaterial, Material, MaterialExt as _};
@ -324,26 +324,16 @@ impl<R: Real, M: Material<R> + Clone + Send + Sync + PartialEq> MaterialSim for
fn get_material<C: Coord>(&self, pos: C) -> &M {
self.get_material(pos)
}
}
impl<R: Real, M: Material<R> + Send + Sync> SampleableSim for SimState<R, M> {
fn sample(&self, pos: Meters) -> Sample {
// TODO: smarter sampling than nearest neighbor?
let pos_sim = pos.to_index(self.feature_size());
let idx = [pos_sim.z() as usize, pos_sim.y() as _, pos_sim.x() as _];
match self.cells.get(idx) {
Some(mat) => Sample {
state: CellStateWithM {
e: self.e[idx].cast(),
h: self.h[idx].cast(),
m: mat.m().cast(),
},
conductivity: mat.conductivity().cast(),
},
None => Default::default(),
fn step_multiple<S: AbstractStimulus>(&mut self, num_steps: u32, s: &S) {
for _ in 0..num_steps {
self.apply_stimulus(s);
SimState::step(self);
}
}
}
impl<R: Real, M: Material<R> + Send + Sync> GenericSim for SimState<R, M> {
fn meta(&self) -> SimMeta<f32> {
let dim = Vec3u::new(
self.cells.shape()[2] as _,
@ -361,21 +351,28 @@ impl<R: Real, M: Material<R> + Send + Sync> SampleableSim for SimState<R, M> {
self.step_no
}
fn sample(&self, pos: Meters) -> Sample {
// TODO: smarter sampling than nearest neighbor?
let pos_sim = pos.to_index(self.feature_size());
let idx = [pos_sim.z() as usize, pos_sim.y() as _, pos_sim.x() as _];
match self.cells.get(idx) {
Some(mat) => Sample {
state: CellStateWithM {
e: self.e[idx].cast(),
h: self.h[idx].cast(),
m: mat.m().cast(),
},
conductivity: mat.conductivity().cast(),
},
None => Default::default(),
}
}
fn to_static(&self) -> StaticSim {
unimplemented!()
}
}
impl<R: Real, M: Material<R> + Send + Sync> GenericSim for SimState<R, M> {
fn step_multiple<S: AbstractStimulus>(&mut self, num_steps: u32, s: &S) {
for _ in 0..num_steps {
self.apply_stimulus(s);
SimState::step(self);
}
}
}
#[allow(unused)]
impl<R: Real, M> SimState<R, M> {
fn get_material<C: Coord>(&self, c: C) -> &M {
@ -1204,14 +1201,14 @@ mod test {
assert_vec3_eq(actual_df_dt, df_dt(tm));
}
fn energy(s: &dyn SampleableSim) -> f32 {
fn energy(s: &dyn GenericSim) -> f32 {
0.5 * s.feature_volume() * s.map_sum_enumerated(|_pos: Index, cell| {
// println!("{}: {}", _pos, cell.e());
cell.e().mag_sq().to_f64()
}).to_f32()
}
fn energy_now_and_then<S: GenericSim>(state: &mut S, frames: u32) -> (f32, f32) {
fn energy_now_and_then<S: MaterialSim>(state: &mut S, frames: u32) -> (f32, f32) {
let energy_0 = energy(state);
for _f in 0..frames {
// println!("e({}) = {}", _f, energy(state));
@ -1394,7 +1391,7 @@ mod test {
}
/// Returns the energy ratio (Eend / Estart)
fn pml_test_full_interior<F, S: GenericSim>(state: &mut S, f: F) -> f32
fn pml_test_full_interior<F, S: MaterialSim>(state: &mut S, f: F) -> f32
where F: Fn(Index) -> (Vec3<f32>, f32) + Sync // returns (E vector, cycles (like Hz))
{
let stim = PmlStim {
@ -1418,7 +1415,7 @@ mod test {
where
R: Region,
F: Fn(Index) -> (Vec3<f32>, f32) + Sync,
S: GenericSim
S: MaterialSim
{
let feat = state.feature_size();
pml_test_full_interior(state, |idx| {
@ -1430,7 +1427,7 @@ mod test {
})
}
fn pml_test_at<S: GenericSim>(state: &mut S, e: Vec3<f32>, center: Index) -> f32 {
fn pml_test_at<S: MaterialSim>(state: &mut S, e: Vec3<f32>, center: Index) -> f32 {
pml_test_full_interior(state, |idx| {
if idx == center {
(e, 1.0)
@ -1440,14 +1437,14 @@ mod test {
})
}
fn pml_test<S: GenericSim, R: RangeBounds<f32> + Debug>(state: &mut S, e: Vec3<f32>, en_range: R) {
fn pml_test<S: MaterialSim, R: RangeBounds<f32> + Debug>(state: &mut S, e: Vec3<f32>, en_range: R) {
let center = Index(state.size().0 / 2);
let pml = pml_test_at(state, e, center);
// PML should absorb all energy
assert!(en_range.contains(&pml), "{} not in {:?}", pml, en_range);
}
fn pml_test_against_baseline<P: GenericSim, B: GenericSim>(pml_state: &mut P, baseline_state: &mut B, e: Vec3<f32>) {
fn pml_test_against_baseline<P: MaterialSim, B: MaterialSim>(pml_state: &mut P, baseline_state: &mut B, e: Vec3<f32>) {
assert_eq!(pml_state.size(), baseline_state.size());
let center = Index(pml_state.size().0 / 2);
let en_pml = pml_test_at(pml_state, e, center);

View File

@ -23,6 +23,12 @@ pub trait MaterialSim: GenericSim {
fn put_material<C: Coord, M: Into<Self::Material>>(&mut self, pos: C, mat: M);
fn get_material<C: Coord>(&self, pos: C) -> &Self::Material;
fn step_multiple<S: AbstractStimulus>(&mut self, num_steps: u32, s: &S);
fn step(&mut self) {
// XXX: try not to exercise this path! NoopStimulus is probably a lot of waste.
self.step_multiple(1, &NoopStimulus);
}
fn fill_region_using<C, Reg, F, M>(&mut self, region: &Reg, f: F)
where
Reg: Region,
@ -121,14 +127,6 @@ pub trait MaterialSim: GenericSim {
}
}
pub trait GenericSim: SampleableSim {
fn step(&mut self) {
// XXX: try not to exercise this path! NoopStimulus is probably a lot of waste.
self.step_multiple(1, &NoopStimulus);
}
fn step_multiple<S: AbstractStimulus>(&mut self, num_steps: u32, s: &S);
}
/// Conceptually, one cell looks like this (in 2d):
///
/// +-------.-------+
@ -219,7 +217,7 @@ impl<R: Real> CellStateWithM<R> {
}
}
impl<'a> dyn SampleableSim + 'a {
impl<'a> dyn GenericSim + 'a {
pub fn get<C: Coord>(&self, at: C) -> Sample {
self.sample(at.to_meters(self.feature_size()))
}
@ -291,10 +289,10 @@ impl<'a> dyn SampleableSim + 'a {
}
// TODO: the Send/Sync bounds here could be removed with some refactoring
pub trait SampleableSim: Send + Sync {
fn sample(&self, pos: Meters) -> Sample;
pub trait GenericSim: Send + Sync {
fn meta(&self) -> SimMeta<f32>;
fn step_no(&self) -> u64;
fn sample(&self, pos: Meters) -> Sample;
/// Take a "snapshot" of the simulation, dropping all material-specific information.
fn to_static(&self) -> StaticSim;

View File

@ -4,7 +4,7 @@ use log::{info, trace, warn};
use crate::geom::{Coord, Index, Meters};
use crate::real::Real;
use crate::sim::{CellStateWithM, GenericSim, MaterialSim, Sample, SampleableSim, StaticSim};
use crate::sim::{CellStateWithM, GenericSim, MaterialSim, Sample, StaticSim};
use crate::stim::AbstractStimulus;
use crate::cross::vec::Vec3;
use coremem_cross::mat::{FullyGenericMaterial, Material};
@ -88,14 +88,36 @@ where
let flat_idx = self.flat_index(pos_sim).unwrap();
&self.mat[flat_idx]
}
fn step_multiple<S: AbstractStimulus>(&mut self, num_steps: u32, stim: &S) {
let (stim_e, stim_h) = self.eval_stimulus(stim);
self.backend.step_n(
self.meta,
self.mat.as_slice(),
stim_e.as_slice().unwrap(),
stim_h.as_slice().unwrap(),
self.e.as_mut_slice(),
self.h.as_mut_slice(),
self.m.as_mut_slice(),
num_steps,
);
self.step_no += num_steps as u64;
}
}
impl<R, M, B> SampleableSim for SpirvSim<R, M, B>
impl<R, M, B> GenericSim for SpirvSim<R, M, B>
where
R: Real,
M: Send + Sync + Material<R>,
B: Send + Sync,
B: Send + Sync + SimBackend<R, M>,
{
fn meta(&self) -> SimMeta<f32> {
self.meta.cast()
}
fn step_no(&self) -> u64 {
self.step_no
}
fn sample(&self, pos: Meters) -> Sample {
// TODO: smarter sampling than nearest neighbor?
let pos_sim = pos.to_index(self.feature_size());
@ -113,13 +135,6 @@ where
}
}
fn meta(&self) -> SimMeta<f32> {
self.meta.cast()
}
fn step_no(&self) -> u64 {
self.step_no
}
fn to_static(&self) -> StaticSim {
let mut mat = Vec::new();
mat.resize(self.e.len(), Default::default());
@ -135,28 +150,6 @@ where
}
}
impl<R, M, B> GenericSim for SpirvSim<R, M, B>
where
R: Real,
M: Send + Sync + Material<R>,
B: Send + Sync + SimBackend<R, M>
{
fn step_multiple<S: AbstractStimulus>(&mut self, num_steps: u32, stim: &S) {
let (stim_e, stim_h) = self.eval_stimulus(stim);
self.backend.step_n(
self.meta,
self.mat.as_slice(),
stim_e.as_slice().unwrap(),
stim_h.as_slice().unwrap(),
self.e.as_mut_slice(),
self.h.as_mut_slice(),
self.m.as_mut_slice(),
num_steps,
);
self.step_no += num_steps as u64;
}
}
impl<R: Real, M: Default, B> SpirvSim<R, M, B>
{
pub fn new_with_backend(size: Index, feature_size: f32, backend: B) -> Self {
@ -279,20 +272,20 @@ where
mod test {
use super::*;
use crate::real::ToFloat as _;
use crate::sim::SampleableSim;
use crate::sim::GenericSim;
use crate::sim::legacy::{self, SimState};
use crate::mat::{self, AnisomorphicConductor};
fn mean_magnitude_e(sim: &dyn SampleableSim) -> f32 {
fn mean_magnitude_e(sim: &dyn GenericSim) -> f32 {
(sim.map_sum_enumerated(|_pos: Index, cell| {
cell.e().mag().to_f64()
})/((sim.width() * sim.height() * sim.depth()) as f64)).to_f32()
}
fn mean_magnitude_h(sim: &dyn SampleableSim) -> f32 {
fn mean_magnitude_h(sim: &dyn GenericSim) -> f32 {
(sim.map_sum_enumerated(|_pos: Index, cell| {
cell.h().mag().to_f64()
})/((sim.width() * sim.height() * sim.depth()) as f64)).to_f32()
}
fn mean_magnitude_m(sim: &dyn SampleableSim) -> f32 {
fn mean_magnitude_m(sim: &dyn GenericSim) -> f32 {
(sim.map_sum_enumerated(|_pos: Index, cell| {
cell.m().mag().to_f64()
})/((sim.width() * sim.height() * sim.depth()) as f64)).to_f32()
@ -309,8 +302,8 @@ mod test {
for y in 0..dim.y() {
for x in 0..dim.x() {
let idx = Index::new(x, y, z);
let real_cell = (real_state as &dyn SampleableSim).get(idx);
let ref_cell = (ref_state as &dyn SampleableSim).get(idx);
let real_cell = (real_state as &dyn GenericSim).get(idx);
let ref_cell = (ref_state as &dyn GenericSim).get(idx);
let diff_e = real_cell.e() - ref_cell.e();
let diff_h = real_cell.h() - ref_cell.h();

View File

@ -1,7 +1,7 @@
//! Post-processing tools
use coremem::meas::AbstractMeasurement;
use coremem::render::{ColorTermRenderer, Renderer as _, RenderConfig, SerializedFrame};
use coremem::sim::{SampleableSim, StaticSim};
use coremem::sim::{GenericSim, StaticSim};
use itertools::Itertools as _;
use lru::LruCache;