diff --git a/Cargo.toml b/Cargo.toml index 19671da..8fde732 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,9 @@ +cargo-features = ["edition2021"] [package] name = "coremem" version = "0.1.0" authors = ["Colin "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -19,6 +20,7 @@ float_eq = "0.5" font8x8 = "0.2" image = "0.23" imageproc = "0.21" +indexmap = "1.6" itertools = "0.9" lazy_static = "1.4" log = "0.4" diff --git a/examples/wrapped_torus.rs b/examples/wrapped_torus.rs index 1f7dbeb..f590a0f 100644 --- a/examples/wrapped_torus.rs +++ b/examples/wrapped_torus.rs @@ -36,7 +36,7 @@ fn main() { let size_px = Index((width_px, height_px, depth_px).into()); let mut driver: Driver = Driver::new(size_px, feat_size); driver.set_steps_per_stim(10); - let base = "wrapped_torus-30-current-decay"; + let base = "wrapped_torus-31-current-decay"; 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); @@ -126,7 +126,9 @@ fn main() { ); let _ = std::fs::create_dir_all(&prefix); - driver.add_serializer_renderer(&*format!("{}/frame-", prefix), 5000); + driver.add_state_file(&*format!("{}/state.bc", prefix), 1000); + driver.add_csv_renderer(&*format!("{}/meas.csv", prefix), 200); + driver.add_serializer_renderer(&*format!("{}/frame-", prefix), 20000); driver.step_until(duration); } diff --git a/src/driver.rs b/src/driver.rs index 9b1e717..174cb8e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -101,6 +101,10 @@ impl + Send + Sync + 'static> Driver { pub fn add_term_renderer(&mut self, step_frequency: u64) { self.add_renderer(render::ColorTermRenderer, "terminal", step_frequency); } + + pub fn add_csv_renderer(&mut self, path: &str, step_frequency: u64) { + self.add_renderer(render::CsvRenderer::new(path), path, step_frequency); + } } impl + Serialize + Send + Sync + 'static> Driver { diff --git a/src/meas.rs b/src/meas.rs index 498bbe9..f8bd25a 100644 --- a/src/meas.rs +++ b/src/meas.rs @@ -1,25 +1,24 @@ use crate::geom::{Meters, Region, Torus, Vec3, WorldRegion}; use crate::real::{Real as _, ToFloat as _}; use crate::sim::GenericSim; -use common_macros::b_tree_map; use dyn_clone::{self, DynClone}; +use indexmap::IndexMap; use serde::{Serialize, Deserialize}; -use std::collections::BTreeMap; // TODO: remove this Clone and Send requirement? Have Measurements be shared by-reference across // threads? i.e. Sync, and no Clone #[typetag::serde(tag = "type")] pub trait AbstractMeasurement: Send + Sync + DynClone { fn eval(&self, state: &dyn GenericSim) -> String; - fn key_value(&self, state: &dyn GenericSim) -> BTreeMap; + fn key_value(&self, state: &dyn GenericSim) -> IndexMap; } dyn_clone::clone_trait_object!(AbstractMeasurement); -pub fn eval_multiple_kv(state: &dyn GenericSim, meas: &[Box]) -> BTreeMap { - let mut r = BTreeMap::new(); +pub fn eval_multiple_kv(state: &dyn GenericSim, meas: &[Box]) -> IndexMap { + let mut r = IndexMap::new(); for m in meas { - let mut other = m.key_value(state); - r.append(&mut other); + let other = m.key_value(state); + r.extend(other.into_iter()); } r } @@ -32,11 +31,11 @@ impl AbstractMeasurement for Time { fn eval(&self, state: &dyn GenericSim) -> String { format!("{:.3e}s (step {})", state.time(), state.step_no()) } - fn key_value(&self, state: &dyn GenericSim) -> BTreeMap { - b_tree_map! { - "time".to_string() => state.time().to_string(), - "step".to_string() => state.step_no().to_string(), - } + fn key_value(&self, state: &dyn GenericSim) -> IndexMap { + [ + ("step".to_string(), state.step_no().to_string()), + ("time".to_string(), state.time().to_string()), + ].into_iter().collect() } } @@ -48,13 +47,13 @@ impl AbstractMeasurement for Meta { 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 GenericSim) -> BTreeMap { - b_tree_map! { - "width".to_string() => state.width().to_string(), - "height".to_string() => state.height().to_string(), - "depth".to_string() => state.depth().to_string(), - "feature_size".to_string() => state.feature_size().to_string(), - } + fn key_value(&self, state: &dyn GenericSim) -> IndexMap { + [ + ("width".to_string(), state.width().to_string()), + ("height".to_string(), state.height().to_string()), + ("depth".to_string(), state.depth().to_string()), + ("feature_size".to_string(), state.feature_size().to_string()), + ].into_iter().collect() } } @@ -72,10 +71,10 @@ impl AbstractMeasurement for Label { fn eval(&self, _state: &dyn GenericSim) -> String { self.0.clone() } - fn key_value(&self, _state: &dyn GenericSim) -> BTreeMap { - b_tree_map! { - self.0.clone() => self.0.clone(), - } + fn key_value(&self, _state: &dyn GenericSim) -> IndexMap { + [ + (self.0.clone(), self.0.clone()), + ].into_iter().collect() } } @@ -157,12 +156,12 @@ impl AbstractMeasurement for Current { mean_current_mag, mean_current_vec) } - fn key_value(&self, state: &dyn GenericSim) -> BTreeMap { + fn key_value(&self, state: &dyn GenericSim) -> IndexMap { let (mean_current_mag, mean_current_vec) = self.data(state); - b_tree_map! { - format!("Imag/cell({})", self.name) => mean_current_mag.to_string(), - format!("I/cell({})", self.name) => mean_current_vec.to_string(), - } + [ + (format!("Imag/cell({})", self.name), mean_current_mag.to_string()), + (format!("I/cell({})", self.name), mean_current_vec.to_string()), + ].into_iter().collect() } } @@ -202,11 +201,11 @@ impl AbstractMeasurement for CurrentLoop { let cross_sectional_current = self.data(state); format!("I({}): {:.2e}", self.name, cross_sectional_current) } - fn key_value(&self, state: &dyn GenericSim) -> BTreeMap { + fn key_value(&self, state: &dyn GenericSim) -> IndexMap { let cross_sectional_current = self.data(state); - b_tree_map! { - format!("I({})", self.name) => cross_sectional_current.to_string(), - } + [ + (format!("I({})", self.name), cross_sectional_current.to_string()), + ].into_iter().collect() } } @@ -276,13 +275,13 @@ impl AbstractMeasurement for MagneticLoop { self.name, mean_directed_h, ) } - fn key_value(&self, state: &dyn GenericSim) -> BTreeMap { + fn key_value(&self, state: &dyn GenericSim) -> IndexMap { let (mean_directed_m, mean_directed_b, mean_directed_h) = self.data(state); - b_tree_map! { - format!("M({})", self.name) => mean_directed_m.to_string(), - format!("B({})", self.name) => mean_directed_b.to_string(), - format!("H({})", self.name) => mean_directed_h.to_string(), - } + [ + (format!("M({})", self.name), mean_directed_m.to_string()), + (format!("B({})", self.name), mean_directed_b.to_string()), + (format!("H({})", self.name), mean_directed_h.to_string()), + ].into_iter().collect() } } @@ -317,11 +316,11 @@ impl AbstractMeasurement for MagneticFlux { let mean_mag = self.data(state); format!("Bavg({}): {:.2e}", self.name, mean_mag) } - fn key_value(&self, state: &dyn GenericSim) -> BTreeMap { + fn key_value(&self, state: &dyn GenericSim) -> IndexMap { let mean_mag = self.data(state); - b_tree_map! { - format!("Bavg({})", self.name) => mean_mag.to_string(), - } + [ + (format!("Bavg({})", self.name), mean_mag.to_string()), + ].into_iter().collect() } } @@ -356,11 +355,11 @@ impl AbstractMeasurement for Magnetization { let mean_mag = self.data(state); format!("Mavg({}): {:.2e}", self.name, mean_mag) } - fn key_value(&self, state: &dyn GenericSim) -> BTreeMap { + fn key_value(&self, state: &dyn GenericSim) -> IndexMap { let mean_mag = self.data(state); - b_tree_map! { - format!("Mavg({})", self.name) => mean_mag.to_string(), - } + [ + (format!("Mavg({})", self.name), mean_mag.to_string()), + ].into_iter().collect() } } @@ -378,11 +377,11 @@ impl AbstractMeasurement for MagnetizationAt { let m = state.sample(self.0).m(); format!("M{}: {:.2e}", loc(self.0), m) } - fn key_value(&self, state: &dyn GenericSim) -> BTreeMap { + fn key_value(&self, state: &dyn GenericSim) -> IndexMap { let m = state.sample(self.0).m(); - b_tree_map! { - format!("M{}", loc(self.0)) => m.to_string(), - } + [ + (format!("M{}", loc(self.0)), m.to_string()), + ].into_iter().collect() } } @@ -396,11 +395,11 @@ impl AbstractMeasurement for MagneticFluxAt { let b = state.sample(self.0).b(); format!("B{}: {:.2e}", loc(self.0), b) } - fn key_value(&self, state: &dyn GenericSim) -> BTreeMap { + fn key_value(&self, state: &dyn GenericSim) -> IndexMap { let b = state.sample(self.0).b(); - b_tree_map! { - format!("B{}", loc(self.0)) => b.to_string(), - } + [ + (format!("B{}", loc(self.0)), b.to_string()), + ].into_iter().collect() } } @@ -414,11 +413,11 @@ impl AbstractMeasurement for MagneticStrengthAt { let h = state.sample(self.0).h(); format!("H{}: {:.2e}", loc(self.0), h) } - fn key_value(&self, state: &dyn GenericSim) -> BTreeMap { + fn key_value(&self, state: &dyn GenericSim) -> IndexMap { let h = state.sample(self.0).h(); - b_tree_map! { - format!("H{}", loc(self.0)) => h.to_string(), - } + [ + (format!("H{}", loc(self.0)), h.to_string()), + ].into_iter().collect() } } @@ -431,11 +430,11 @@ impl AbstractMeasurement for ElectricField { let e = state.sample(self.0).e(); format!("E{}: {:.2e}", loc(self.0), e) } - fn key_value(&self, state: &dyn GenericSim) -> BTreeMap { + fn key_value(&self, state: &dyn GenericSim) -> IndexMap { let e = state.sample(self.0).e(); - b_tree_map! { - format!("E{}", loc(self.0)) => e.to_string(), - } + [ + (format!("E{}", loc(self.0)), e.to_string()), + ].into_iter().collect() } } @@ -478,11 +477,11 @@ impl AbstractMeasurement for Energy { let e = self.data(state); format!("U({}): {:.2e}", self.name, e) } - fn key_value(&self, state: &dyn GenericSim) -> BTreeMap { + fn key_value(&self, state: &dyn GenericSim) -> IndexMap { let e = self.data(state); - b_tree_map! { - format!("U({})", self.name) => e.to_string(), - } + [ + (format!("U({})", self.name), e.to_string()), + ].into_iter().collect() } } @@ -520,10 +519,10 @@ impl AbstractMeasurement for Power { let power = self.data(state); format!("P({}): {:.2e}", self.name, power) } - fn key_value(&self, state: &dyn GenericSim) -> BTreeMap { + fn key_value(&self, state: &dyn GenericSim) -> IndexMap { let power = self.data(state); - b_tree_map! { - format!("P({})", self.name) => power.to_string(), - } + [ + (format!("P({})", self.name), power.to_string()), + ].into_iter().collect() } } diff --git a/src/render.rs b/src/render.rs index 2786d82..19984f8 100644 --- a/src/render.rs +++ b/src/render.rs @@ -784,6 +784,7 @@ impl Renderer for CsvRenderer { CsvState::Writing(writer) => writer, }; writer.write_record(row.values()).unwrap(); + writer.flush().unwrap(); *lock = Some(CsvState::Writing(writer)); } }