sim-lib-interference-solve 0.1.0

Deterministic CPU f64 reference solving for coherent scalar wave fields.
Documentation
use sim_lib_interference_core::{
    FieldAmplitude, Hertz, MetresPerSecond, NepersPerMetre, Point3M, PositiveMetres, Radians,
    SamplingPlane, ScalarMedium, UnitVector3,
};
use sim_lib_interference_solve::{
    AperturePolicy, Observable, ReductionRule, ReferencePhasorSolver, ScenarioBuilder,
    ScenarioKind, ScenarioLimits, analyze_fringes, project,
};

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

fn x_axis() -> UnitVector3 {
    UnitVector3::new(1.0, 0.0, 0.0).unwrap()
}

fn y_axis() -> UnitVector3 {
    UnitVector3::new(0.0, 1.0, 0.0).unwrap()
}

fn builder() -> ScenarioBuilder {
    ScenarioBuilder::new(
        Hertz::new(1.0).unwrap(),
        ScalarMedium::new(
            MetresPerSecond::new(8.0).unwrap(),
            NepersPerMetre::new(0.0).unwrap(),
        ),
        PositiveMetres::new(0.01).unwrap(),
        ScenarioLimits::default(),
    )
}

#[test]
fn public_standing_wave_analysis_keeps_sampling_and_projection_identity() {
    let scenario = builder()
        .counter_propagating_planes(
            point(0.0, 0.0, 0.0),
            x_axis(),
            PositiveMetres::new(20.0).unwrap(),
            FieldAmplitude::new(1.0).unwrap(),
            Radians::new(0.0).unwrap(),
        )
        .unwrap();
    let plane = SamplingPlane::new(
        point(-4.0, -0.05, 0.0),
        x_axis(),
        y_axis(),
        PositiveMetres::new(8.0).unwrap(),
        PositiveMetres::new(0.1).unwrap(),
        1,
        64,
    )
    .unwrap();
    let (field, evidence) = ReferencePhasorSolver::default()
        .solve(scenario.problem(), &plane)
        .unwrap();
    let sampling = evidence.preflight().sampling_certificate;
    let projection = project(&field, sampling, Observable::Amplitude, 0.0).unwrap();
    let report = analyze_fringes(&projection, 1.0e-12).unwrap();

    assert_eq!(report.sampling, sampling);
    assert_eq!(report.projection.observable(), Observable::Amplitude);
    assert_eq!(report.projection.rule(), ReductionRule::Detail);
    assert_eq!(report.projection.target_dimensions().rows(), 1);
    assert_eq!(report.projection.target_dimensions().columns(), 64);
    assert_eq!(report.stats.count, 64);
    assert_eq!(report.extrema.len(), 4);
    assert!(report.michelson_contrast.is_some());
}

#[test]
fn public_aperture_certificate_quantifies_normalization_and_spacing() {
    let aperture = builder()
        .discrete_aperture(
            point(0.0, 0.0, 0.0),
            x_axis(),
            y_axis(),
            2,
            3,
            PositiveMetres::new(2.0).unwrap(),
            PositiveMetres::new(4.0).unwrap(),
            FieldAmplitude::new(12.0).unwrap(),
            Radians::new(0.0).unwrap(),
            AperturePolicy::Strict,
        )
        .unwrap();
    let certificate = aperture.certificate();

    assert_eq!(certificate.kind, ScenarioKind::DiscreteAperture);
    assert_eq!(certificate.source_count, 6);
    assert_eq!(certificate.total_source_amplitude, 12.0);
    assert_eq!(certificate.amplitude_per_source, 2.0);
    assert_eq!(certificate.element_spacing_wavelengths.u, Some(0.25));
    assert_eq!(certificate.element_spacing_wavelengths.v, Some(0.5));
    assert_eq!(certificate.element_spacing_wavelengths.maximum(), Some(0.5));
}