sim-lib-interference-core 0.1.0

Validated physical boundary types for coherent scalar wave-field studies.
Documentation
use std::f64::consts::{FRAC_PI_2, LN_2};

use sim_lib_interference_core::{
    Emitter, FieldAmplitude, Hertz, InterferenceError, InterferenceProblem, MetresPerSecond,
    NepersPerMetre, Point3M, PositiveMetres, Radians, ScalarMedium, SourceSet, UnitVector3,
    contribution_at, forward_plane_contribution_at, point_contribution_at,
};

fn point(x: f64, y: f64, z: f64) -> Point3M {
    Point3M::from_metres(x, y, z).unwrap()
}

fn problem(attenuation_nepers_per_metre: f64) -> InterferenceProblem {
    let source = Emitter::Point {
        id: "origin".to_owned(),
        position: point(0.0, 0.0, 0.0),
        amplitude_at_reference: FieldAmplitude::new(4.0).unwrap(),
        phase: Radians::new(0.0).unwrap(),
    };
    InterferenceProblem::new(
        Hertz::new(1.0).unwrap(),
        ScalarMedium::new(
            MetresPerSecond::new(4.0).unwrap(),
            NepersPerMetre::new(attenuation_nepers_per_metre).unwrap(),
        ),
        SourceSet::new(vec![source]).unwrap(),
        PositiveMetres::new(0.01).unwrap(),
    )
}

fn assert_close(actual: f64, expected: f64, tolerance: f64) {
    assert!(
        (actual - expected).abs() <= tolerance,
        "actual {actual:?}, expected {expected:?}, tolerance {tolerance:?}"
    );
}

#[test]
fn point_contribution_has_outgoing_phase_and_inverse_distance_spreading() {
    let problem = problem(0.0);
    let (real, imaginary) = point_contribution_at(
        &problem,
        "point",
        point(0.0, 0.0, 0.0),
        FieldAmplitude::new(4.0).unwrap(),
        Radians::new(0.0).unwrap(),
        point(1.0, 0.0, 0.0),
    )
    .unwrap();

    assert_close(real, 0.0, 8.0 * f64::EPSILON);
    assert_close(imaginary, 4.0, 8.0 * f64::EPSILON);

    let (real, imaginary) = point_contribution_at(
        &problem,
        "point",
        point(0.0, 0.0, 0.0),
        FieldAmplitude::new(4.0).unwrap(),
        Radians::new(0.0).unwrap(),
        point(2.0, 0.0, 0.0),
    )
    .unwrap();
    assert_close(real, -2.0, 8.0 * f64::EPSILON);
    assert_close(imaginary, 0.0, 8.0 * f64::EPSILON);
}

#[test]
fn attenuation_is_the_positive_imaginary_wavenumber_component() {
    let problem = problem(LN_2);
    let (real, imaginary) = forward_plane_contribution_at(
        &problem,
        "plane",
        point(0.0, 0.0, 0.0),
        UnitVector3::new(1.0, 0.0, 0.0).unwrap(),
        FieldAmplitude::new(4.0).unwrap(),
        Radians::new(0.0).unwrap(),
        point(1.0, 0.0, 0.0),
    )
    .unwrap();

    assert_close(real, 0.0, 8.0 * f64::EPSILON);
    assert_close(imaginary, 2.0, 8.0 * f64::EPSILON);
}

#[test]
fn singular_and_behind_plane_samples_fail_closed() {
    let problem = problem(0.0);

    assert!(matches!(
        point_contribution_at(
            &problem,
            "point",
            point(0.0, 0.0, 0.0),
            FieldAmplitude::new(1.0).unwrap(),
            Radians::new(0.0).unwrap(),
            point(0.01, 0.0, 0.0),
        ),
        Err(InterferenceError::SingularPointSample { .. })
    ));

    assert_eq!(
        forward_plane_contribution_at(
            &problem,
            "plane",
            point(0.0, 0.0, 0.0),
            UnitVector3::new(1.0, 0.0, 0.0).unwrap(),
            FieldAmplitude::new(1.0).unwrap(),
            Radians::new(0.0).unwrap(),
            point(-0.25, 0.0, 0.0),
        )
        .unwrap_err(),
        InterferenceError::BehindForwardPlane {
            source_id: "plane".to_owned(),
            signed_distance_metres: -0.25,
        }
    );
}

#[test]
fn enum_dispatch_matches_the_focused_plane_function() {
    let problem = problem(0.0);
    let plane = Emitter::ForwardPlane {
        id: "plane".to_owned(),
        through: point(0.0, 0.0, 0.0),
        direction: UnitVector3::new(1.0, 0.0, 0.0).unwrap(),
        amplitude: FieldAmplitude::new(3.0).unwrap(),
        phase: Radians::new(FRAC_PI_2).unwrap(),
    };
    let at = point(0.0, 0.0, 0.0);

    assert_eq!(
        contribution_at(&problem, &plane, at).unwrap(),
        forward_plane_contribution_at(
            &problem,
            "plane",
            point(0.0, 0.0, 0.0),
            UnitVector3::new(1.0, 0.0, 0.0).unwrap(),
            FieldAmplitude::new(3.0).unwrap(),
            Radians::new(FRAC_PI_2).unwrap(),
            at,
        )
        .unwrap()
    );
}

#[test]
fn non_finite_derived_geometry_is_rejected() {
    let problem = problem(0.0);
    let result = point_contribution_at(
        &problem,
        "far",
        point(-f64::MAX, 0.0, 0.0),
        FieldAmplitude::new(1.0).unwrap(),
        Radians::new(0.0).unwrap(),
        point(f64::MAX, 0.0, 0.0),
    );

    assert!(matches!(
        result,
        Err(InterferenceError::NonFinitePropagation {
            name: "point-distance-metres",
            ..
        })
    ));
}