convert HasTangent -> HasCrossSection
i believe the current loop algorithm (which i'm just preserving here) is actually not correct. i'll work through it more.
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
use crate::geom::{HasTangent as _, Meters, Region, Torus, WorldRegion};
|
||||
use crate::geom::{HasCrossSection, Meters, Region, Torus, WorldRegion};
|
||||
use crate::real::{Real as _, ToFloat as _};
|
||||
use crate::cross::vec::{Vec3, Vec3u};
|
||||
use crate::sim::AbstractSim;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
// TODO: do we really need both Send and Sync?
|
||||
pub trait AbstractMeasurement<S>: Send + Sync {
|
||||
fn key_value(&self, state: &S) -> Vec<Measurement>;
|
||||
}
|
||||
@@ -307,33 +308,53 @@ impl<S: AbstractSim> AbstractMeasurement<S> for Current {
|
||||
|
||||
/// Measures the current directed around a closed loop
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct CurrentLoop {
|
||||
pub struct CurrentLoop<R> {
|
||||
name: String,
|
||||
region: Torus,
|
||||
region: R,
|
||||
}
|
||||
|
||||
impl CurrentLoop {
|
||||
pub fn new(name: &str, r: Torus) -> Self {
|
||||
impl<R> CurrentLoop<R> {
|
||||
pub fn new(name: &str, r: R) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
region: r,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<R: Region + HasCrossSection> CurrentLoop<R> {
|
||||
fn data<S: AbstractSim>(&self, state: &S) -> f32 {
|
||||
let FieldSample(volume, directed_current, _current_vec) = state.map_sum_over_enumerated(&self.region, |coord: Meters, _cell| {
|
||||
let tangent = self.region.tangent(coord);
|
||||
let current = state.current(coord);
|
||||
let directed_current = current.dot(tangent.cast());
|
||||
FieldSample(1, directed_current.cast(), current.cast())
|
||||
// i use a statistical lens for this:
|
||||
// 1. current is the rate of flow of charge into a surface.
|
||||
// 2. in any context where it makes sense to think of current, the current through each
|
||||
// cross-sectional **is the same**.
|
||||
// 3. each point in our 3d region belongs to exactly one cross-sectional surface.
|
||||
// 4. so, given a point: what's the expected current through the cross section it belongs to?
|
||||
// - answer: that point's current density times the cross section's area.
|
||||
// 5. average the above over the whole volume, and you get an "average current".
|
||||
//
|
||||
// we're sampling uniformly over the cell space -- not the set of cross sections.
|
||||
// - however, if all cross sections have equal area, this is equivalent.
|
||||
// sampling all points (instead of just a single point):
|
||||
// 1) removes bias from step #4: current *within* a cross section is not uniform, but if
|
||||
// we sample every point within the cross section and weight them equally, then the
|
||||
// average is the truth.
|
||||
// 2) probably combats grid quantization / artifacting.
|
||||
let FieldSample(num_samples, sum_cross_sectional_current, _current_vec) = state.map_sum_over_enumerated(&self.region, |coord: Meters, _cell| {
|
||||
// `normal` represents both the size of the cross section (m^2) this cell belongs to,
|
||||
// and the normal direction of the cross section.
|
||||
let normal = self.region.cross_section_normal(coord); // [m^2]
|
||||
let current_density = state.current_density(coord); // [A/m^2]
|
||||
// now we have an estimation of the entire current flowing through the cross section
|
||||
// this cell belongs to.
|
||||
let cross_sectional_current = current_density.dot(normal.cast()); // [A]
|
||||
FieldSample(1, cross_sectional_current.cast(), current_density.cast())
|
||||
});
|
||||
let mean_directed_current = directed_current.cast::<f32>() / f32::from_primitive(volume);
|
||||
let cross_section = self.region.cross_section() / (state.feature_size() * state.feature_size());
|
||||
let cross_sectional_current = mean_directed_current * cross_section;
|
||||
cross_sectional_current
|
||||
let mean_cross_sectional_current = sum_cross_sectional_current.cast::<f32>() / f32::from_primitive(num_samples);
|
||||
mean_cross_sectional_current
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AbstractSim> AbstractMeasurement<S> for CurrentLoop {
|
||||
impl<R: Region + HasCrossSection, S: AbstractSim> AbstractMeasurement<S> for CurrentLoop<R> {
|
||||
fn key_value(&self, state: &S) -> Vec<Measurement> {
|
||||
let cross_sectional_current = self.data(state);
|
||||
vec![
|
||||
|
Reference in New Issue
Block a user