use core::fmt;
use num_complex::Complex;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum MieOutput {
Coefficients,
CoefficientDerivatives,
Extinction,
Scattering,
Absorption,
Backscattering,
Asymmetry,
Amplitudes,
EfficiencyJacobian,
AmplitudeDerivatives,
PhaseMatrix,
PhaseMatrixDerivatives,
GreekCoefficients {
order: usize,
},
GreekDerivatives {
order: usize,
},
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub struct ValidatedDomain {
pub x_min: f64,
pub x_max: f64,
pub m_re_max: f64,
pub m_im_max: f64,
pub abs_m_min: f64,
}
pub const VALIDATED_DOMAIN: ValidatedDomain = ValidatedDomain {
x_min: 1e-6,
x_max: 2e4,
m_re_max: 10.0,
m_im_max: 10.0,
abs_m_min: 0.1,
};
impl ValidatedDomain {
pub fn contains(&self, x: f64, m: Complex<f64>) -> bool {
(self.x_min..=self.x_max).contains(&x)
&& m.re > 0.0
&& m.re <= self.m_re_max
&& (0.0..=self.m_im_max).contains(&m.im)
&& m.norm() >= self.abs_m_min
}
pub fn condition_factor(x: f64, m: Complex<f64>) -> f64 {
(1.0 + m.norm().max(1.0) * x) * near_one(m)
}
pub fn tolerance(&self, output: MieOutput, x: f64, m: Complex<f64>, condition: f64) -> f64 {
let kappa = Self::condition_factor(x, m);
let quadrature = |order: usize| ((order as f64 + 1.0).powi(2) + x * x) / 8.0;
let prior = match output {
MieOutput::CoefficientDerivatives | MieOutput::AmplitudeDerivatives => {
1.0 + m.norm().max(1.0) * x
}
MieOutput::EfficiencyJacobian | MieOutput::PhaseMatrixDerivatives => {
kappa * near_one(m)
}
MieOutput::GreekCoefficients { order } => kappa + quadrature(order),
MieOutput::GreekDerivatives { order } => kappa * near_one(m) + quadrature(order),
_ => kappa,
};
Self::constant(output) * (prior + condition) * f64::EPSILON
}
pub fn constant(output: MieOutput) -> f64 {
match output {
MieOutput::Coefficients
| MieOutput::Extinction
| MieOutput::Scattering
| MieOutput::Absorption
| MieOutput::Backscattering
| MieOutput::Asymmetry => 16.0,
MieOutput::Amplitudes => 128.0,
MieOutput::AmplitudeDerivatives => 64.0,
MieOutput::EfficiencyJacobian => 256.0,
MieOutput::CoefficientDerivatives => 512.0,
MieOutput::PhaseMatrix => 32.0,
MieOutput::PhaseMatrixDerivatives => 128.0,
MieOutput::GreekCoefficients { .. } => 8.0,
MieOutput::GreekDerivatives { .. } => 16.0,
}
}
}
fn near_one(m: Complex<f64>) -> f64 {
(m.norm() / (m - Complex::new(1.0, 0.0)).norm()).max(1.0)
}
impl fmt::Display for ValidatedDomain {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"x in [{:e}, {:e}], 0 < Re m <= {}, 0 <= Im m <= {}, |m| >= {}",
self.x_min, self.x_max, self.m_re_max, self.m_im_max, self.abs_m_min
)
}
}