Near-field maps from a T-matrix
TransitionMatrices.jl reconstructs the electromagnetic field around a particle — not just the far-field cross sections — from any computed T-matrix. Given an incident plane wave, the scattered field is expanded in radiating vector spherical wave functions (VSWFs) with coefficients \((p, q) = \mathbf{T}\,(a, b)\), where \((a, b)\) are the plane-wave expansion coefficients. The total external field is
$$\mathbf{E}_\text{tot}(\mathbf r) = \mathbf{E}_\text{inc}(\mathbf r) + \mathbf{E}_\text{sca}(\mathbf r), \qquad \mathbf{E}_\text{sca}(\mathbf r) = \sum_{n,m} p_{mn}\mathbf{M}_{mn}(k\mathbf r) + q_{mn}\mathbf{N}_{mn}(k\mathbf r).$$
This works for any T-matrix (Mie, EBCM, IITM, Sh-matrix). Here we map the field enhancement \(|\mathbf{E}_\text{tot}|/|\mathbf{E}_\text{inc}|\) around a dielectric sphere.
The radiating expansion converges only outside the sphere circumscribing the particle (the Rayleigh hypothesis). For a sphere that boundary is the surface, so the map is valid right down to it; for a non-spherical particle, evaluate only outside the circumscribing sphere.
begin
import Pkg
Pkg.activate(@__DIR__) # the examples/ environment
Pkg.develop(; path = dirname(@__DIR__)) # point TransitionMatrices at this repo
Pkg.instantiate()
using TransitionMatrices, Plots
end
1. A scatterer and its incidence
A dielectric sphere of size parameter \(x = k a = 2.5\) and refractive index \(m_r = 1.5\), illuminated by an \(x\)-polarized plane wave propagating along \(+z\). With \(\lambda = 2\pi\) the size parameter equals the radius, so \(a = 2.5\).
The incidence direction is \((\vartheta_\text{inc}, \varphi_\text{inc}) = (0, 0)\); the polarization \((E_\vartheta, E_\varphi) = (1, 0)\) is \(\hat{\mathbf x}\) because \(\hat{\boldsymbol\vartheta}(0,0) = \hat{\mathbf x}\).
begin
λ = 2π
k = 2π / λ
a = 2.5
mᵣ = 1.5 + 0.0im
x = k * a
N = ceil(Int, x + 4 * cbrt(x) + 2)
𝐓 = TransitionMatrices.MieTransitionMatrix{ComplexF64, N}(x, mᵣ)
# Incident plane wave: +z propagation, x-polarized.
ϑ_inc, φ_inc = 0.0, 0.0
Eθ, Eφ = 1.0 + 0im, 0.0im
# The scattered coefficients (p, q) = 𝐓 (a, b) depend only on the incidence,
# so compute them once and reuse them at every field point.
p, q = scattering_coefficients(𝐓, ϑ_inc, φ_inc, Eθ, Eφ)
nothing
end
2. Sample the total field on a grid
We evaluate \(|\mathbf{E}_\text{tot}|\) in the \(x\)–\(z\) plane (the plane of incidence and polarization). Points inside the sphere are masked — the external expansion is not meant to represent the interior field (that is the internal field, a separate reconstruction).
begin
xs = range(-3a, 3a; length = 161)
zs = range(-3a, 3a; length = 161)
enhancement = fill(NaN, length(zs), length(xs))
for (i, zz) in enumerate(zs), (j, xx) in enumerate(xs)
r = hypot(xx, zz)
r ≤ a && continue # inside the sphere: masked
pos = [xx, 0.0, zz]
E = incident_field(λ, ϑ_inc, φ_inc, Eθ, Eφ, pos) +
scattered_field(p, q, λ, pos)
enhancement[i, j] = sqrt(abs2(E[1]) + abs2(E[2]) + abs2(E[3]))
end
nothing
end
3. The field-enhancement map
The incident wave travels upward (\(+z\)). The map shows the standing-wave interference of the incident and scattered fields, the forward-scattering concentration on the far (\(+z\)) side, and the shadow on the near side.
let
hm = heatmap(xs, zs, enhancement;
aspect_ratio = 1, c = :inferno, clims = (0, maximum(filter(isfinite, enhancement))),
xlabel = "x", ylabel = "z", colorbar_title = "|E| / |E₀|",
title = "dielectric sphere, x = $(round(x; digits = 2)), mᵣ = 1.5",
size = (640, 560))
# outline the sphere
θ = range(0, 2π; length = 200)
plot!(hm, a .* cos.(θ), a .* sin.(θ); label = "", lw = 1.5, lc = :cyan)
end
4. A line cut along the optical axis
Along \(x = 0\) the field shows the shadow/illumination asymmetry and the forward enhancement directly.
let
zline = range(-3a, 3a; length = 400)
amp = map(zline) do zz
abs(zz) ≤ a && return NaN
pos = [0.0, 0.0, zz]
E = incident_field(λ, ϑ_inc, φ_inc, Eθ, Eφ, pos) + scattered_field(p, q, λ, pos)
sqrt(abs2(E[1]) + abs2(E[2]) + abs2(E[3]))
end
plot(zline, amp; lw = 2, xlabel = "z (optical axis)", ylabel = "|E| / |E₀|",
label = "", title = "axial cut (x = y = 0)", size = (720, 320))
vspan!([-a, a]; alpha = 0.15, c = :gray, label = "sphere")
end
Takeaway
scattering_coefficients once, then scattered_field / total_field at any point gives the full external field for any T-matrix — sphere, spheroid, cylinder, Chebyshev, or a general particle. Reusing the precomputed (p, q) keeps a dense field grid cheap.
For the field inside the particle, see the internal-field reconstruction (TODO, Tier 2).