Orientation averaging
For randomly oriented particles you need the orientation-averaged scattering quantities. TransitionMatrices.jl offers two routes:
RandomOrientationTransitionMatrix(T)— Mishchenko's analytic closed form for the orientation average (fast, exact);orientation_average(T, p; Nα, Nβ, Nγ)— numerical average over the Euler angles against an orientation distributionp.
This notebook checks they agree and shows the numerical average converging to the analytic value as the angular grid is refined.
begin
import Pkg
Pkg.activate(@__DIR__)
Pkg.develop(; path = dirname(@__DIR__))
Pkg.instantiate()
using TransitionMatrices, Plots
end
A non-spherical particle
begin
λ = 2π
spheroid = TransitionMatrices.Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + 0.02im)
T = calc_T(spheroid, λ)
uniform = (α, β, γ) -> 1 / (8π^2) # isotropic orientation distribution
end
#5 (generic function with 1 method)
Analytic vs numerical
Both should give the same orientation-averaged efficiencies.
begin
Tanalytic = RandomOrientationTransitionMatrix(T)
Tnumeric = orientation_average(T, uniform; Nα = 20, Nβ = 20, Nγ = 20)
compare = [
(; method = "analytic (Mishchenko)", Qsca = calc_Csca(Tanalytic, λ),
Qext = calc_Cext(Tanalytic, λ), g = asymmetry_parameter(Tanalytic, λ)),
(; method = "numerical (20³ grid)", Qsca = calc_Csca(Tnumeric, λ),
Qext = calc_Cext(Tnumeric, λ), g = asymmetry_parameter(Tnumeric, λ)),
]
end
2-element Vector{@NamedTuple{method::String, Qsca::Float64, Qext::Float64, g::Float64}}:
(method = "analytic (Mishchenko)", Qsca = 6.161024698021793, Qext = 7.59930108537653, g = 0.5833300223810896)
(method = "numerical (20³ grid)", Qsca = 6.1610246980217225, Qext = 7.599301085376555, g = 0.5833300223810899)
Convergence of the numerical average
The numerical average approaches the analytic value as the β-grid is refined; the analytic closed form gives it directly, at a fraction of the cost.
begin
Qref = calc_Csca(Tanalytic, λ)
Ns = 4:2:18
errs = [abs(calc_Csca(orientation_average(T, uniform; Nα = 2, Nβ = N, Nγ = 2), λ) - Qref) / Qref
for N in Ns]
nothing
end
let
plot(collect(Ns), errs; yscale = :log10, lw = 2, marker = :circle,
xlabel = "β-grid points Nβ", ylabel = "|Qsca_numeric − Qsca_analytic| / Qsca",
label = "numerical average", legend = :topright,
title = "convergence to the analytic orientation average", size = (680, 400))
end
Takeaway
The analytic RandomOrientationTransitionMatrix is both exact and much cheaper than refining a 3-D Euler-angle quadrature — prefer it for random-orientation cross sections. The numerical orientation_average remains useful for non-uniform orientation distributions (just pass a different p).