sim_lib_interference_core/problem.rs
1//! A complete, single-frequency coherent interference problem.
2
3use std::f64::consts::TAU;
4
5use crate::{Hertz, PositiveMetres, ScalarMedium, SourceSet, WaveNumber};
6
7/// The distance at which a point emitter's field amplitude is specified.
8///
9/// Point-source propagation therefore uses a dimensionless spreading factor
10/// `POINT_SOURCE_REFERENCE_DISTANCE_METRES / r`.
11pub const POINT_SOURCE_REFERENCE_DISTANCE_METRES: f64 = 1.0;
12
13/// A coherent scalar-wave problem in one homogeneous medium.
14///
15/// The model uses the real field
16/// `u(x, t) = Re{U(x) * exp(-i * omega * t)}` and the complex wavenumber
17/// `k_tilde = omega / c + i * alpha`. Away from point sources, the phasor
18/// satisfies `laplacian(U) + k_tilde^2 * U = 0`.
19///
20/// The positive outgoing sign is `exp(i * k_tilde * distance)`: phase advances
21/// with distance while non-negative `alpha` attenuates it. Point-source
22/// amplitudes are stated at [`POINT_SOURCE_REFERENCE_DISTANCE_METRES`] and
23/// samples at or inside `singularity_radius` are outside the model.
24#[derive(Clone, Debug, PartialEq)]
25pub struct InterferenceProblem {
26 /// The one frequency shared by every coherent source.
27 pub frequency: Hertz,
28 /// The homogeneous scalar propagation medium.
29 pub medium: ScalarMedium,
30 /// The non-empty, canonically ordered coherent sources.
31 pub sources: SourceSet,
32 /// The positive exclusion radius around every point source.
33 pub singularity_radius: PositiveMetres,
34}
35
36impl InterferenceProblem {
37 /// Constructs a problem from checked components.
38 pub fn new(
39 frequency: Hertz,
40 medium: ScalarMedium,
41 sources: SourceSet,
42 singularity_radius: PositiveMetres,
43 ) -> Self {
44 Self {
45 frequency,
46 medium,
47 sources,
48 singularity_radius,
49 }
50 }
51
52 /// Returns the angular frequency `omega = 2 * pi * frequency`.
53 pub fn angular_frequency_radians_per_second(&self) -> f64 {
54 TAU * self.frequency.get()
55 }
56
57 /// Returns this problem's complex wavenumber.
58 pub fn wavenumber(&self) -> WaveNumber {
59 self.medium.wavenumber(self.frequency)
60 }
61
62 /// Returns the unattenuated wavelength `c / frequency` in metres.
63 pub fn wavelength_metres(&self) -> f64 {
64 self.medium.speed().get() / self.frequency.get()
65 }
66}