Conventions¶
Quick start summarizes the conventions that matter most; this page lists them all.
Scattering¶
Refractive index. \(m = n + ik\), where \(\operatorname{Im} m \ge 0\) means absorbing (the Bohren–Huffman and Mishchenko convention, time dependence \(e^{-i\omega t}\)). Negative \(\operatorname{Im} m\) is rejected with
InvalidInput; it is never silently flipped. Codes using \(e^{+i\omega t}\), such as Wiscombe’s MIEV0, write \(m = n - ik\) and report the complex conjugates of the coefficients and amplitudes. The refractive index is that of the particle relative to the surrounding medium.Size parameter. \(x = 2\pi r/\lambda\), with \(\lambda\) the wavelength in the medium. Derivatives are with respect to \(x\); multiply by \(2\pi/\lambda\) for the radius.
Lengths. The Mie functions take the size parameter and return efficiencies, cross sections divided by \(\pi r^2\). The ensemble functions take radii and wavelengths in any unit, provided both use the same one, and return cross sections per particle in that unit squared.
Coefficients. \(a_n\), \(b_n\) for \(n = 1, \ldots, N\) (Bohren & Huffman 1983, Eq. 4.53), stored at index \(n - 1\).
Amplitudes. \(S_1(\mu)\) and \(S_2(\mu)\) at \(\mu = \cos\Theta\) (Bohren & Huffman 1983, Eq. 4.74).
Phase-matrix normalization. \(\tfrac12 \int_{-1}^{1} F_{11}(\mu)\,d\mu = 1\), with \(F_{11} = (\lvert S_1\rvert^2 + \lvert S_2\rvert^2)/(x^2 Q_\text{sca}/2)\). The unnormalized phase matrix is the normalized one times \(Q_\text{sca}\) (per unit geometric cross section) for single spheres, and times \(\langle C_\text{sca}\rangle\) (per particle) for ensembles.
Phase-matrix elements. \(F_{11}\), \(F_{12}\), \(F_{33}\), \(F_{34}\) are Bohren & Huffman’s \(S_{11}\), \(S_{12}\), \(S_{33}\), \(S_{34}\) (1983, §4.4) times that normalization, with \(F_{22} = F_{11}\) and \(F_{44} = F_{33}\) for spheres. \(F_{12} < 0\) for Rayleigh scattering at 90°, so the degree of linear polarization of scattered unpolarized light is \(-F_{12}/F_{11}\).
Greek coefficients. \(\alpha_1, \ldots, \alpha_4\), \(\beta_1\), \(\beta_2\) expand the phase matrix in the Wigner d-functions of
wigner_dat \(\mu = \cos\Theta\) (Mishchenko, Travis & Lacis 2002, Eqs. 4.75–4.80), indexed by order \(l = 0, \ldots, L\). Normalized, \(\alpha_1^0 = 1\) and \(\alpha_1^1 = 3g\). sasktran2’s coefficients have the same \(\alpha\) and the opposite sign of \(\beta_1\) and \(\beta_2\). See Homogeneous spheres (Lorenz–Mie) for the expansion and the sign conventions.Derivatives with respect to m. The coefficients and amplitudes are holomorphic in \(m\): their derivative \(d/dm\) equals \(\partial/\partial(\operatorname{Re} m)\), and \(\partial/\partial(\operatorname{Im} m) = i\,d/dm\). Real outputs give \(\partial/\partial(\operatorname{Re} m)\) and \(\partial/\partial(\operatorname{Im} m)\).
Errors¶
The Rust core returns one error type, scatrs::Error, and Python raises a matching exception.
The Python exceptions are also subclasses of the standard exception for the same kind of
failure:
Rust |
Python exception |
Meaning |
|---|---|---|
|
|
an argument is outside its domain or a documented limit |
|
|
an iteration missed the accuracy it promises |
|
|
valid inputs outside the domain where accuracy has been validated |
All three derive from ScatrsError. The Mie and ensemble functions
raise OutsideValidatedDomain for valid inputs outside their validated domain, unless the
caller allows it (allow_unvalidated). Python raises the standard exceptions where Python
itself would: TypeError for a wrong argument type, for example. See scatrs.errors.
Kernels such as gauss_legendre and wigner_d check their inputs and return an error rather
than panic or return NaN. The scalar types instead follow f64: a function outside its domain
returns NaN, as f64::sqrt(-1.0) does.
Special functions¶
Wigner d-functions. scatrs uses the convention of Mishchenko, Travis & Lacis (2002), Scattering, Absorption, and Emission of Light by Small Particles, Appendix B. It is the common \(d^j_{m'm}(\beta)\) with \((m', m)\) renamed \((m, n)\). For example
The functions take \(x = \cos\theta \in [-1, 1]\) and return every degree
\(l = 0, \ldots, l_\text{max}\). Entries with \(l < \max(\lvert m\rvert, \lvert n\rvert)\) are
zero. In Python the degree is the last axis: wigner_d() returns an array
of shape np.shape(x) + (l_max + 1,). See Special functions.
Quadrature. Gauss–Legendre rules are on \([-1, 1]\). Nodes ascend and the rule is exactly
symmetric: nodes[i] == -nodes[n - 1 - i] and weights[i] == weights[n - 1 - i]. For odd
\(n\) the middle node is \(+0\).
Determinism¶
Each Mie kernel runs on one thread, and the ensembles give the same bits for any number of threads, so a given build gives the same bits for the same inputs every time. Gauss–Legendre rules are designed to be bitwise identical across platforms too, because they use no platform maths library. Some results do use the platform’s library and can differ between platforms in the last bits, within their accuracy bounds:
f64elementary functions such asexpandsin, including those insideDual<f64, N>;the double-double functions that start from an
f64result (ln,cbrtandatan2), and those computed from them, such aspowf,asinandacos;Dual<f64, N>sinandcoson Apple targets;the Mie outputs, which use
f64sinandcos. The default number of terms usescbrt, so where \(x + 6x^{1/3} + 3\) is an integer (at \(x = 8\), for example) a platform whosecbrtis not correctly rounded can use one term more or fewer.
Rust¶
Mixed arithmetic. Generic code over T: Real combines T with f64 with the f64 on
the right: x * 2.0, not 2.0 * x. For Complex<T>, write z.scale(T::from_f64(2.0)).
Derivatives. Dual<T, N> carries \(N\) first-order derivatives alongside a value. The value
is always computed bit for bit as T would compute it; sin and cos are the one
exception (see below). The conventions that affect results are:
Comparisons (
==,<, …) look only at the value.is_zeroandis_oneare strict: every derivative must be zero too.A derivative component that is exactly zero is a strong zero. It stays zero even when multiplied by an infinite or NaN factor.
At points where a function is not differentiable, scatrs makes a fixed choice. For example,
hypotat the origin has derivative 0 andatan2at the origin has NaN derivatives.On Apple targets the
f64values ofsinandcoscome from one fusedsincoscall.
Scalar types (Rust) gives the reasons and the main rules; the rustdoc of
Dual lists them all.