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 distribution p.

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).