diff --git a/crates/coremem/src/meas.rs b/crates/coremem/src/meas.rs index f097545..2690af6 100644 --- a/crates/coremem/src/meas.rs +++ b/crates/coremem/src/meas.rs @@ -2,12 +2,11 @@ use crate::geom::{Meters, Region, Torus, WorldRegion}; use crate::real::{Real as _, ToFloat as _}; use crate::cross::vec::Vec3; use crate::sim::AbstractSim; -use indexmap::IndexMap; use serde::{Serialize, Deserialize}; pub trait AbstractMeasurement: Send + Sync { fn eval(&self, state: &S) -> String; - fn key_value(&self, state: &S) -> IndexMap; + fn key_value(&self, state: &S) -> Vec; } pub fn as_dyn_measurements>(meas: &[M]) -> Vec<&dyn AbstractMeasurement> { @@ -15,20 +14,59 @@ pub fn as_dyn_measurements>(meas: &[M]) -> Vec<&dyn } -/// create one IndexMap out of several measurements -pub fn eval_multiple_kv(state: &S, meas: &[&dyn AbstractMeasurement]) -> IndexMap { - let mut r = IndexMap::new(); - for m in meas { - let other = m.key_value(state); - r.extend(other.into_iter()); - } - r +/// combine several measurements +pub fn eval_multiple_kv(state: &S, meas: &[&dyn AbstractMeasurement]) -> Vec + meas.into_iter().flat_map(|m| m.key_value(state).into_iter()).collect() } -pub fn eval_to_vec(state: &S, meas: &[&dyn AbstractMeasurement]) -> Vec { - eval_multiple_kv(state, meas).into_iter().map(|(k, v)| { - Evaluated::new(k, v) - }).collect() +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +pub enum MeasurementValue { + Field(Vec3), + Float(f32), + Int(u64), + Dim(Vec3u), +} + +impl From> for MeasurementValue { + fn from(v: Vec3) -> Self { + Self::Field(v) + } +} +impl From for MeasurementValue { + fn from(v: f32) -> Self { + Self::Float(v) + } +} +impl From for MeasurementValue { + fn from(v: u64) -> Self { + Self::Int(v) + } +} +impl From for MeasurementValue { + fn from(v: Vec3u) -> Self { + Self::Dim(v) + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct Measurement { + name: String, + value: MeasurementValue, + /// e.g. "A" for Amps + unit: String, +} + +impl Measurement { + fn new>(name: &str, value: T, unit: &str) -> Self { + Self { + name.to_owned(), + value: value.into(), + unit: unit.to_owned(), + } + } + fn new_unitless>(name: &str, value: T) -> Self { + Self::new(name, value, "") + } } enum SiScale { @@ -111,11 +149,11 @@ impl AbstractMeasurement for Time { fn eval(&self, state: &S) -> String { format!("{} (step {})", SiScale::format_short(state.time(), "s"), state.step_no()) } - fn key_value(&self, state: &S) -> IndexMap { - [ - ("step".to_string(), state.step_no().to_string()), - ("time".to_string(), state.time().to_string()), - ].into_iter().collect() + fn key_value(&self, state: &S) -> Vec + vec![ + Measurement::new_unitless("step", state.step_no()), + Measurement::new("time", state.time(), "s"), + ] } } @@ -126,42 +164,11 @@ impl AbstractMeasurement for Meta { fn eval(&self, state: &S) -> String { format!("{}x{}x{} feat: {:.1e}m", state.width(), state.height(), state.depth(), state.feature_size()) } - fn key_value(&self, state: &S) -> 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() - } -} - -/// some measurement which has already been evaluated. -/// this is used particularly if we need to monomorphize a measurement (e.g. for serialization) -/// and know it won't be applied to a new/different state. -#[derive(Clone, Serialize, Deserialize)] -pub struct Evaluated(String, String); - -impl Evaluated { - pub fn new, S2: Into>(key: S1, value: S2) -> Self { - Self(key.into(), value.into()) - } - pub fn key(&self) -> &str { - &*self.0 - } - pub fn value(&self) -> &str { - &*self.1 - } -} - -impl AbstractMeasurement for Evaluated { - fn eval(&self, _state: &S) -> String { - format!("{}: {}", self.0, self.1) - } - fn key_value(&self, _state: &S) -> IndexMap { - [ - (self.0.clone(), self.1.clone()), - ].into_iter().collect() + fn key_value(&self, state: &S) -> Vec { + vec![ + Measurement::new_unitless("dim", state.dim()), + Measurement::new("feature_size", state.feature_size(), "m"), + ] } } @@ -193,10 +200,10 @@ impl AbstractMeasurement for Volume { self.data(state), ) } - fn key_value(&self, state: &S) -> IndexMap { - [ - (format!("Vol({})", self.name), self.data(state).to_string()), - ].into_iter().collect() + fn key_value(&self, state: &S) -> Vec { + vec![ + Measurement::new(&format!("Vol({})", self.name), self.data(), "um^3"), + ] } } @@ -277,12 +284,20 @@ impl AbstractMeasurement for Current { mean_current_mag, mean_current_vec) } - fn key_value(&self, state: &S) -> IndexMap { + fn key_value(&self, state: &S) -> Vec { let (mean_current_mag, mean_current_vec) = self.data(state); - [ - (format!("Imag/cell({})", self.name), mean_current_mag.to_string()), - (format!("I/cell({})", self.name), mean_current_vec.to_string()), - ].into_iter().collect() + vec![ + Measurement::new( + &format!("Imag/cell({})", self.name), + mean_current_mag, + "A", + ), + Measurement::new( + &format!("/cell({})", self.name), + mean_current_vec, + "A", + ), + ] } } @@ -321,11 +336,15 @@ impl AbstractMeasurement for CurrentLoop { let cross_sectional_current = self.data(state); format!("I({}): {:.2e}", self.name, cross_sectional_current) } - fn key_value(&self, state: &S) -> IndexMap { + fn key_value(&self, state: &S) -> Vec { let cross_sectional_current = self.data(state); - [ - (format!("I({})", self.name), cross_sectional_current.to_string()), - ].into_iter().collect() + vec![ + Measurement::new( + &format!("I({})", self.name), + cross_sectional_current, + "A" + ), + ] } } @@ -394,13 +413,13 @@ impl AbstractMeasurement for MagneticLoop { self.name, mean_directed_h, ) } - fn key_value(&self, state: &S) -> IndexMap { + fn key_value(&self, state: &S) -> Vec { let (mean_directed_m, mean_directed_b, mean_directed_h) = self.data(state); - [ - (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() + vec![ + Measurement::new_unitless(&format!("M({})", self.name), mean_directed_current_m), + Beasurement::new_unitless(&format!("B({})", self.name), mean_directed_current_b), + Beasurement::new_unitless(&format!("H({})", self.name), mean_directed_current_h), + ] } } @@ -434,11 +453,14 @@ impl AbstractMeasurement for MagneticFlux { let mean_mag = self.data(state); format!("Bavg({}): {:.2e}", self.name, mean_mag) } - fn key_value(&self, state: &S) -> IndexMap { + fn key_value(&self, state: &S) -> Vec { let mean_mag = self.data(state); - [ - (format!("Bavg({})", self.name), mean_mag.to_string()), - ].into_iter().collect() + vec![ + Measurement::new_unitless( + &format!("Bavg({})", self.name), + mean_mag, + ) + ] } } @@ -472,11 +494,13 @@ impl AbstractMeasurement for Magnetization { let mean_mag = self.data(state); format!("Mavg({}): {:.2e}", self.name, mean_mag) } - fn key_value(&self, state: &S) -> IndexMap { + fn key_value(&self, state: &S) -> Vec { let mean_mag = self.data(state); - [ - (format!("Mavg({})", self.name), mean_mag.to_string()), - ].into_iter().collect() + vec![ + Measurement::new_unitless( + &format!("Mavg({})", self.name), mean_mag + ), + ] } } @@ -493,11 +517,11 @@ impl AbstractMeasurement for MagnetizationAt { let m = state.sample(self.0).m(); format!("M{}: {:.2e}", loc(self.0), m) } - fn key_value(&self, state: &S) -> IndexMap { + fn key_value(&self, state: &S) -> Vec { let m = state.sample(self.0).m(); - [ - (format!("M{}", loc(self.0)), m.to_string()), - ].into_iter().collect() + vec![ + Measurement::new_unitless(&format!("M{}", loc(self.0)), m) + ] } } @@ -510,11 +534,13 @@ impl AbstractMeasurement for MagneticFluxAt { let b = state.sample(self.0).b(); format!("B{}: {:.2e}", loc(self.0), b) } - fn key_value(&self, state: &S) -> IndexMap { + fn key_value(&self, state: &S) -> Vec { let b = state.sample(self.0).b(); - [ - (format!("B{}", loc(self.0)), b.to_string()), - ].into_iter().collect() + vec![ + Measurement::new_unitless( + &format!("B{}", loc(self.0)), b + ) + ] } } @@ -527,11 +553,13 @@ impl AbstractMeasurement for MagneticStrengthAt { let h = state.sample(self.0).h(); format!("H{}: {:.2e}", loc(self.0), h) } - fn key_value(&self, state: &S) -> IndexMap { + fn key_value(&self, state: &S) -> Vec { let h = state.sample(self.0).h(); - [ - (format!("H{}", loc(self.0)), h.to_string()), - ].into_iter().collect() + vec![ + Measurement::new_unitless( + &format!("H{}", loc(self.0)), h + ) + ] } } @@ -543,11 +571,13 @@ impl AbstractMeasurement for ElectricField { let e = state.sample(self.0).e(); format!("E{}: {}", loc(self.0), e) } - fn key_value(&self, state: &S) -> IndexMap { + fn key_value(&self, state: &S) -> Vec { let e = state.sample(self.0).e(); - [ - (format!("E{}", loc(self.0)), e.to_string()), - ].into_iter().collect() + vec![ + Measurement::new_unitless( + &format!("E{}", loc(self.0)), e + ) + ] } } @@ -589,11 +619,13 @@ impl AbstractMeasurement for Energy { let e = self.data(state); format!("U({}): {}", self.name, SiScale::format_short(e, "J")) } - fn key_value(&self, state: &S) -> IndexMap { + fn key_value(&self, state: &S) -> Vec { let e = self.data(state); - [ - (format!("U({})", self.name), e.to_string()), - ].into_iter().collect() + vec![ + Measurement::new_unitless( + &format!("U({})", self.name), e + ) + ] } } @@ -630,10 +662,12 @@ impl AbstractMeasurement for Power { let power = self.data(state); format!("P({}): {}", self.name, SiScale::format_short(power, "W")) } - fn key_value(&self, state: &S) -> IndexMap { + fn key_value(&self, state: &S) -> Vec { let power = self.data(state); - [ - (format!("P({})", self.name), power.to_string()), - ].into_iter().collect() + vec![ + Measurement::new( + &format!("P({})", self.name), power, "W" + ) + ] } }