mod amplitudes;
mod domain;
mod efficiencies;
mod greek;
mod kernel;
mod phase;
use num_complex::Complex;
use crate::scalar::Real;
use crate::{Error, Result};
pub(crate) use amplitudes::for_each_order;
pub use amplitudes::{AmplitudeBuffers, AngularTable};
pub use domain::{MieOutput, VALIDATED_DOMAIN, ValidatedDomain};
pub use efficiencies::{Efficiencies, EfficiencyJacobian};
pub use greek::{GreekCoefficients, GreekProjector, MAX_GREEK_ORDER, PhaseTables};
pub use phase::{Normalization, PhaseBuffers};
pub(crate) use phase::{element_derivatives, elements};
pub const MAX_MIE_ORDER: usize = 1 << 20;
pub const MIN_SIZE_PARAMETER: f64 = 1e-30;
pub const MAX_SIZE_PARAMETER: f64 = 1e6;
pub const MIN_REFRACTIVE_INDEX: f64 = 1e-6;
pub const MAX_REFRACTIVE_INDEX: f64 = 1e3;
pub fn default_n_max(x: f64) -> usize {
terms(x, 6.0, 3.0)
}
pub fn n_terms(x: f64, m: Complex<f64>, opts: &MieOptions) -> Result<usize> {
check_inputs::<f64>(x, m.re, m.im, opts)
}
fn terms(x: f64, c: f64, d: f64) -> usize {
let n = (x + c * x.cbrt() + d).ceil();
if n.is_finite() && n < 2f64.powi(53) {
n as usize
} else {
usize::MAX
}
}
fn default_terms<T: Real>(x: f64) -> usize {
if T::epsilon().approx_f64() < 2f64.powi(-60) {
terms(x, 12.0, 6.0)
} else {
default_n_max(x)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[non_exhaustive]
pub struct MieOptions {
pub n_max: Option<usize>,
pub allow_unvalidated: bool,
}
impl MieOptions {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_n_max(mut self, n_max: usize) -> Self {
self.n_max = Some(n_max);
self
}
#[must_use]
pub fn with_allow_unvalidated(mut self, allow: bool) -> Self {
self.allow_unvalidated = allow;
self
}
}
#[derive(Clone, Debug)]
pub struct Mie<T: Real> {
state: kernel::State<T>,
}
impl<T: Real> Default for Mie<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Real> Mie<T> {
pub fn new() -> Self {
Self {
state: kernel::State::new(),
}
}
pub fn compute(
&mut self,
x: T,
m: Complex<T>,
opts: &MieOptions,
) -> Result<MieSolution<'_, T>> {
self.solve(x, m, opts, false)
}
pub fn compute_with_derivatives(
&mut self,
x: T,
m: Complex<T>,
opts: &MieOptions,
) -> Result<MieSolution<'_, T>> {
self.solve(x, m, opts, true)
}
fn solve(
&mut self,
x: T,
m: Complex<T>,
opts: &MieOptions,
derivatives: bool,
) -> Result<MieSolution<'_, T>> {
let n = check_inputs::<T>(x.approx_f64(), m.re.approx_f64(), m.im.approx_f64(), opts)?;
if !(x.is_finite() && m.re.is_finite() && m.im.is_finite()) {
return Err(Error::InvalidInput(
"the derivative parts of x and m must be finite".into(),
));
}
self.state.solve(x, m, n, derivatives);
Ok(MieSolution { state: &self.state })
}
}
fn check_inputs<T: Real>(x: f64, m_re: f64, m_im: f64, opts: &MieOptions) -> Result<usize> {
if !(MIN_SIZE_PARAMETER..=MAX_SIZE_PARAMETER).contains(&x) {
return Err(Error::InvalidInput(format!(
"the size parameter x must be in [{MIN_SIZE_PARAMETER:e}, {MAX_SIZE_PARAMETER:e}], \
got {x:e}"
)));
}
if !(m_re.is_finite() && m_im.is_finite()) {
return Err(Error::InvalidInput(format!(
"the refractive index must be finite, got {m_re:e} + {m_im:e}i"
)));
}
if m_re < 0.0 || m_im < 0.0 {
return Err(Error::InvalidInput(format!(
"the refractive index must have Re m >= 0 and Im m >= 0 (Im m < 0 is a gain medium), \
got {m_re:e} + {m_im:e}i"
)));
}
let abs_m = m_re.hypot(m_im);
if !(MIN_REFRACTIVE_INDEX..=MAX_REFRACTIVE_INDEX).contains(&abs_m) {
return Err(Error::InvalidInput(format!(
"|m| must be in [{MIN_REFRACTIVE_INDEX:e}, {MAX_REFRACTIVE_INDEX:e}], got {abs_m:e}"
)));
}
let default = default_terms::<T>(x);
let n = opts.n_max.unwrap_or(default);
if n == 0 || n > MAX_MIE_ORDER {
return Err(Error::InvalidInput(format!(
"the number of terms must be in [1, {MAX_MIE_ORDER}], got {n}"
)));
}
if !opts.allow_unvalidated {
let m = Complex::new(m_re, m_im);
if !VALIDATED_DOMAIN.contains(x, m) {
return Err(Error::OutsideValidatedDomain(format!(
"x = {x:e}, m = {m_re:e} + {m_im:e}i is outside the validated domain {}",
VALIDATED_DOMAIN
)));
}
if n < default {
return Err(Error::OutsideValidatedDomain(format!(
"n_max = {n} is below the default truncation {default} for x = {x:e}"
)));
}
}
Ok(n)
}
#[derive(Clone, Copy, Debug)]
pub struct MieSolution<'a, T: Real> {
state: &'a kernel::State<T>,
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub struct CoefficientDerivatives<'a, T> {
pub da_dx: &'a [Complex<T>],
pub da_dm: &'a [Complex<T>],
pub db_dx: &'a [Complex<T>],
pub db_dm: &'a [Complex<T>],
}
impl<'a, T: Real> MieSolution<'a, T> {
pub fn x(&self) -> T {
self.state.x
}
pub fn m(&self) -> Complex<T> {
self.state.m
}
pub fn n_terms(&self) -> usize {
self.state.a.len()
}
pub fn a(&self) -> &'a [Complex<T>] {
&self.state.a
}
pub fn b(&self) -> &'a [Complex<T>] {
&self.state.b
}
pub fn derivatives(&self) -> Option<CoefficientDerivatives<'a, T>> {
self.state.derivatives.then(|| CoefficientDerivatives {
da_dx: &self.state.da_dx,
da_dm: &self.state.da_dm,
db_dx: &self.state.db_dx,
db_dm: &self.state.db_dm,
})
}
}