mod domain;
mod kernel;
use num_complex::Complex;
pub use domain::{VALIDATED_DOMAIN, ValidatedDomain};
use crate::mie::{
AmplitudeBuffers, AngularTable, CoefficientDerivatives, Coefficients, Efficiencies,
EfficiencyJacobian, GreekCoefficients, Group, MAX_MIE_ORDER, MAX_REFRACTIVE_INDEX,
MAX_SIZE_PARAMETER, MIN_REFRACTIVE_INDEX, MIN_SIZE_PARAMETER, MieOptions, Normalization,
PhaseBuffers, default_terms,
};
use crate::scalar::Real;
use crate::{Error, Result};
pub const MAX_LAYERS: usize = 1 << 10;
#[derive(Clone, Debug)]
struct LayerDerivatives<T> {
da_dx: Vec<Complex<T>>,
da_dm: Vec<Complex<T>>,
db_dx: Vec<Complex<T>>,
db_dm: Vec<Complex<T>>,
abs_sum: [T; 3],
}
impl<T: Real> LayerDerivatives<T> {
fn new() -> Self {
Self {
da_dx: Vec::new(),
da_dm: Vec::new(),
db_dx: Vec::new(),
db_dm: Vec::new(),
abs_sum: [T::zero(); 3],
}
}
}
#[derive(Clone, Debug)]
pub struct Layered<T: Real> {
kernel: kernel::Kernel<T>,
x: T,
a: Vec<Complex<T>>,
b: Vec<Complex<T>>,
abs_sum: T,
derivatives: bool,
layers: Vec<LayerDerivatives<T>>,
}
impl<T: Real> Default for Layered<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Real> Layered<T> {
pub fn new() -> Self {
Self {
kernel: kernel::Kernel::new(),
x: T::zero(),
a: Vec::new(),
b: Vec::new(),
abs_sum: T::zero(),
derivatives: false,
layers: Vec::new(),
}
}
pub fn compute(
&mut self,
x: &[T],
m: &[Complex<T>],
opts: &MieOptions,
) -> Result<LayeredSolution<'_, T>> {
let n = check_inputs(x, m, opts)?;
let last = x.len() - 1;
if last == 0 {
self.kernel.mie.solve(x[0], m[0], n, false);
} else {
self.kernel.solve(x, m, n, false);
}
let s = &self.kernel.mie;
self.x = x[last];
self.a.clone_from(&s.a);
self.b.clone_from(&s.b);
self.abs_sum = s.abs_sum;
self.derivatives = false;
Ok(LayeredSolution { s: self })
}
pub fn compute_with_derivatives(
&mut self,
x: &[T],
m: &[Complex<T>],
opts: &MieOptions,
) -> Result<LayeredSolution<'_, T>> {
let n = check_inputs(x, m, opts)?;
let outer = x[x.len() - 1].approx_f64();
if !opts.allow_unvalidated && x.len() > 1 && outer > VALIDATED_DOMAIN.derivative_x_max {
return Err(Error::OutsideValidatedDomain(format!(
"the derivatives are validated up to x_L = {:e}, got {outer:e}",
VALIDATED_DOMAIN.derivative_x_max
)));
}
let layers = x.len();
let last = layers - 1;
self.x = x[last];
self.layers.resize_with(layers, LayerDerivatives::new);
self.layers.truncate(layers);
if last == 0 {
self.kernel.mie.solve(x[0], m[0], n, true);
} else {
self.kernel.solve(x, m, n, true);
for (d, g) in self.layers.iter_mut().zip(&self.kernel.grads) {
d.da_dx.clone_from(&g.da_dx);
d.da_dm.clone_from(&g.da_dm);
d.db_dx.clone_from(&g.db_dx);
d.db_dm.clone_from(&g.db_dm);
d.abs_sum = g.abs_sum;
}
}
let s = &self.kernel.mie;
self.a.clone_from(&s.a);
self.b.clone_from(&s.b);
self.abs_sum = s.abs_sum;
let d = &mut self.layers[last];
d.da_dx.clone_from(&s.da_dx);
d.da_dm.clone_from(&s.da_dm);
d.db_dx.clone_from(&s.db_dx);
d.db_dm.clone_from(&s.db_dm);
d.abs_sum = s.abs_sum_jacobian;
self.derivatives = true;
Ok(LayeredSolution { s: self })
}
}
pub fn n_terms(x: &[f64], m: &[Complex<f64>], opts: &MieOptions) -> Result<usize> {
check_inputs(x, m, opts)
}
fn check_inputs<T: Real>(x: &[T], m: &[Complex<T>], opts: &MieOptions) -> Result<usize> {
let invalid = |s: String| Err(Error::InvalidInput(s));
let layers = x.len();
if layers == 0 || m.len() != layers {
return invalid(format!(
"a layered sphere needs one size parameter and one refractive index per layer, got \
{} and {}",
layers,
m.len()
));
}
if layers > MAX_LAYERS {
return invalid(format!(
"at most {MAX_LAYERS} layers are accepted, got {layers}"
));
}
let xs: Vec<f64> = x.iter().map(|v| v.approx_f64()).collect();
let ms: Vec<Complex<f64>> = m
.iter()
.map(|v| Complex::new(v.re.approx_f64(), v.im.approx_f64()))
.collect();
if let Some((l, v)) = xs.iter().enumerate().find(|(_, v)| !v.is_finite()) {
return invalid(format!(
"the size parameter of layer {l} must be finite, got {v:e}"
));
}
if let Some(l) = (1..layers).find(|&l| xs[l] < xs[l - 1]) {
return invalid(format!(
"the size parameters must be nondecreasing from the core out, but layer {l} has \
{:e} < {:e}",
xs[l],
xs[l - 1]
));
}
let outer = xs[layers - 1];
if xs[0] < MIN_SIZE_PARAMETER || outer > MAX_SIZE_PARAMETER {
return invalid(format!(
"the size parameters must be in [{MIN_SIZE_PARAMETER:e}, {MAX_SIZE_PARAMETER:e}], \
got {:e} to {outer:e}",
xs[0]
));
}
for (l, v) in ms.iter().enumerate() {
if !(v.re.is_finite() && v.im.is_finite()) {
return invalid(format!(
"the refractive index of layer {l} must be finite, got {:e} + {:e}i",
v.re, v.im
));
}
if v.re < 0.0 || v.im < 0.0 {
return invalid(format!(
"the refractive index of layer {l} must have Re m >= 0 and Im m >= 0 (Im m < 0 \
is a gain medium), got {:e} + {:e}i",
v.re, v.im
));
}
if !(MIN_REFRACTIVE_INDEX..=MAX_REFRACTIVE_INDEX).contains(&v.norm()) {
return invalid(format!(
"|m| of layer {l} must be in [{MIN_REFRACTIVE_INDEX:e}, \
{MAX_REFRACTIVE_INDEX:e}], got {:e}",
v.norm()
));
}
}
let default = default_terms::<T>(outer);
let n = opts.n_max.unwrap_or(default);
if n == 0 || n > MAX_MIE_ORDER {
return invalid(format!(
"the number of terms must be in [1, {MAX_MIE_ORDER}], got {n}"
));
}
if !opts.allow_unvalidated {
if !VALIDATED_DOMAIN.contains(&xs, &ms) {
return Err(Error::OutsideValidatedDomain(format!(
"a layered sphere of {layers} layers with x_L = {outer:e} and m = {ms:?} 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_L = {outer:e}"
)));
}
}
if !(x.iter().all(|v| v.is_finite()) && m.iter().all(|v| v.re.is_finite() && v.im.is_finite()))
{
return invalid("the derivative parts of x and m must be finite".into());
}
Ok(n)
}
#[derive(Clone, Copy, Debug)]
pub struct LayeredSolution<'a, T: Real> {
s: &'a Layered<T>,
}
fn no_derivatives(what: &str) -> Error {
Error::InvalidInput(format!(
"{what} derivatives need a solution from Layered::compute_with_derivatives"
))
}
impl<'a, T: Real> LayeredSolution<'a, T> {
pub fn x(&self) -> T {
self.s.x
}
pub fn n_terms(&self) -> usize {
self.s.a.len()
}
pub fn a(&self) -> &'a [Complex<T>] {
&self.s.a
}
pub fn b(&self) -> &'a [Complex<T>] {
&self.s.b
}
fn layers(&self) -> usize {
if self.s.derivatives {
self.s.layers.len()
} else {
0
}
}
pub fn derivatives(&self, layer: usize) -> Option<CoefficientDerivatives<'a, T>> {
(layer < self.layers()).then(|| {
let d = &self.s.layers[layer];
CoefficientDerivatives {
da_dx: &d.da_dx,
da_dm: &d.da_dm,
db_dx: &d.db_dx,
db_dm: &d.db_dm,
}
})
}
pub(crate) fn coefficients(&self) -> Coefficients<'a, T> {
Coefficients {
x: self.s.x,
a: &self.s.a,
b: &self.s.b,
abs_sum: self.s.abs_sum,
}
}
pub(crate) fn group(&self, layer: usize) -> Option<Group<'a, T>> {
self.derivatives(layer).map(|d| Group {
d,
abs_sum: self.s.layers[layer].abs_sum,
outer: layer + 1 == self.s.layers.len(),
})
}
fn group_or(&self, layer: usize, what: &str) -> Result<Group<'a, T>> {
if !self.s.derivatives {
return Err(no_derivatives(what));
}
self.group(layer).ok_or_else(|| {
Error::InvalidInput(format!(
"no layer {layer}: the particle has {} layers",
self.s.layers.len()
))
})
}
pub fn efficiencies(&self) -> Efficiencies<T> {
self.coefficients().efficiencies()
}
pub fn efficiencies_jacobian(&self) -> Option<Vec<EfficiencyJacobian<T>>> {
if !self.s.derivatives {
return None;
}
let c = self.coefficients();
Some(
(0..self.layers())
.map(|l| c.efficiencies_jacobian(&self.group(l).expect("a layer")))
.collect(),
)
}
pub fn amplitudes(&self, mu: &[T], out: AmplitudeBuffers<'_, T>) -> Result<()> {
self.coefficients().amplitudes(mu, out)
}
pub fn amplitudes_from_table(
&self,
table: &AngularTable<T>,
out: AmplitudeBuffers<'_, T>,
) -> Result<()> {
self.coefficients().amplitudes_from_table(table, out)
}
pub fn amplitudes_with_derivatives(
&self,
mu: &[T],
layer: usize,
values: AmplitudeBuffers<'_, T>,
d_dx: AmplitudeBuffers<'_, T>,
d_dm: AmplitudeBuffers<'_, T>,
) -> Result<()> {
let g = self.group_or(layer, "amplitude")?;
self.coefficients()
.amplitudes_with_derivatives(mu, &g, values, d_dx, d_dm)
}
pub fn amplitudes_with_derivatives_from_table(
&self,
table: &AngularTable<T>,
layer: usize,
values: AmplitudeBuffers<'_, T>,
d_dx: AmplitudeBuffers<'_, T>,
d_dm: AmplitudeBuffers<'_, T>,
) -> Result<()> {
let g = self.group_or(layer, "amplitude")?;
self.coefficients()
.amplitudes_with_derivatives_from_table(table, &g, values, d_dx, d_dm)
}
pub fn phase_matrix(
&self,
mu: &[T],
normalization: Normalization,
out: PhaseBuffers<'_, T>,
) -> Result<()> {
self.coefficients().phase_matrix(mu, normalization, out)
}
pub fn phase_matrix_from_table(
&self,
table: &AngularTable<T>,
normalization: Normalization,
out: PhaseBuffers<'_, T>,
) -> Result<()> {
self.coefficients()
.phase_matrix_from_table(table, normalization, out)
}
pub fn phase_matrix_with_derivatives(
&self,
mu: &[T],
layer: usize,
normalization: Normalization,
values: PhaseBuffers<'_, T>,
d: [PhaseBuffers<'_, T>; 3],
) -> Result<()> {
let g = self.group_or(layer, "phase-matrix")?;
self.coefficients()
.phase_matrix_with_derivatives(mu, &g, normalization, values, d)
}
pub fn phase_matrix_with_derivatives_from_table(
&self,
table: &AngularTable<T>,
layer: usize,
normalization: Normalization,
values: PhaseBuffers<'_, T>,
d: [PhaseBuffers<'_, T>; 3],
) -> Result<()> {
let g = self.group_or(layer, "phase-matrix")?;
self.coefficients()
.phase_matrix_with_derivatives_from_table(table, &g, normalization, values, d)
}
pub fn greek(
&self,
order: Option<usize>,
normalization: Normalization,
) -> Result<GreekCoefficients<T>> {
self.coefficients().greek(order, normalization)
}
pub fn greek_with_derivatives(
&self,
order: Option<usize>,
layer: usize,
normalization: Normalization,
) -> Result<(GreekCoefficients<T>, [GreekCoefficients<T>; 3])> {
let g = self.group_or(layer, "Greek")?;
self.coefficients()
.greek_with_derivatives(order, &g, normalization)
}
}