using UnderwaterAcoustics
using AcousticRayTracers
using Plots
env = UnderwaterEnvironment(
bathymetry = SampledField([200, 150]; x=[0, 1000], interp=Linear()),
soundspeed = SampledField([1500, 1480, 1495, 1510, 1520]; z=0:-50:-200, interp=CubicSpline()),
seabed = SandyClay
)
pm = RaySolver(env)
tx = AcousticSource(0, -50, 300)
rx = AcousticReceiver(1000, -100)
rays = arrivals(pm, tx, rx)
plot(env; xlims=(-10, 1010))
plot!(tx)
plot!(rx)
plot!(rays)RaySolver
| Model | AcousticRayTracers.RaySolver |
| Description | 2½D acoustic Gaussian beam tracer |
| Language | Julia |
| Advantages | Differentiable (forward mode) |
| Limitations | Tell us and we will fix them! |
| Differentiability | ForwardDiff |
RaySolver is a differentiable 2½D Gaussian beam tracer similar to Bellhop, but fully written in Julia to be compatible with automatic differentiation (AD) tools such as ForwardDiff. Its implementation is largely based on the description in:
- Jensen, Kuperman, Porter & Schmidt, “Computational Ocean Acoustics,” Springer New York, NY, 2011.
Example
rxs = AcousticReceiverGrid2D(1:1000, -200:0)
x = transmission_loss(pm, tx, rxs; mode=:coherent)
plot(rxs, x; crange=70)
plot!(env; xlims=(0,1000), linewidth=3)Backscatter
By default, RaySolver terminates rays that turn around and head back toward the source. Enabling the backscatter option continues tracing such rays until they exit the modeling domain (\(\texttt{rmin} ≤ x ≤ \texttt{rmax}\)) or become too weak to matter (min_amplitude). This allows the model to predict echoes from steep bathymetric features. Backscattered eigenrays are reported with arrival angles \(|θᵣ| > 90°\), and contribute to arrivals, acoustic fields and impulse responses.
If rmax is not specified, the modeling domain extends to the query range (the receiver range, or the far edge of a receiver grid) — the same domain as without backscatter. Anything beyond the domain is invisible to the model, so echoes from features behind the receivers require an explicit larger rmax (as in the example below, where the slope lies beyond the receiver). Setting rmin < 0 extends the domain to the left of the transmitter; a second fan of rays with the same elevation angles is then automatically launched in the \(-x\) direction, so that features to the left of the transmitter can also produce echoes (such arrivals have launch angles \(|θₛ| > 90°\)).
To illustrate, we place a transmitter and a receiver in a 200 m deep channel that ends in a steep rocky slope rising to 10 m depth. Rays travel past the receiver, reverse off the slope (via slope and surface bounce combinations), and return to the receiver from behind. We plot the strongest 3 forward and strongest 3 backscattered eigenrays:
env = UnderwaterEnvironment(
bathymetry = SampledField([200, 200, 10]; x=[0, 800, 1000], interp=Linear()),
soundspeed = 1500,
seabed = Rock
)
pm = RaySolver(env; backscatter=true, rmax=1000)
tx = AcousticSource(0, -50, 300)
rx = AcousticReceiver(400, -100)
rays = arrivals(pm, tx, rx)
fwd = filter(a -> abs(a.θᵣ) ≤ π/2, rays) # forward eigenrays
echoes = filter(a -> abs(a.θᵣ) > π/2, rays) # backscattered eigenrays
strongest(a, n) = sort(a; by = x -> abs(x.ϕ), rev=true)[1:min(n, length(a))]
plot(env; xlims=(-10, 1010))
plot!(tx)
plot!(rx)
plot!(strongest(fwd, 3))
plot!(strongest(echoes, 3))Scatterers (experimental)
RaySolver has experimental support for discrete scatterers in the water column, described in the environment with the scatterers field (see Scatterer). Rays reflect specularly off the scatterer boundary: the amplitude is given by the scatterer’s acoustic boundary condition via the standard reflection coefficient at the local tangent plane, and the Gaussian beam spreading is corrected for the curvature of the surface. Scatterer geometry may be differentiated through, i.e., gradients of acoustic quantities with respect to scatterer position and size are supported.
Scatterers are usually used together with backscatter=true, so that target echoes propagate back toward the source. Here, a small rigid ellipse on the transmitter–receiver line blocks the direct path, while a larger rigid ellipse, shallower and beyond the receiver, is rotated so that its face reflects the transmitter’s rays down to the receiver:
env = UnderwaterEnvironment(
bathymetry = 200,
soundspeed = 1500,
seabed = SandyClay,
scatterers = (
Scatterer(Ellipse(125, -100, 8, 3; θ=90°), RigidBoundary), # blocks the direct path
Scatterer(Ellipse(450, -60, 10, 4; θ=-82°), RigidBoundary) # reflects toward the receiver
)
)
pm = RaySolver(env; backscatter=true, rmax=600, nbeams=2500)
tx = AcousticSource(0, -100, 1000)
rx = AcousticReceiver(250, -100)
rays = arrivals(pm, tx, rx)
fwd = filter(a -> abs(a.θᵣ) ≤ π/2, rays)
echoes = filter(a -> abs(a.θᵣ) > π/2, rays)
plot(env; xlims=(-10, 610))
plot!(tx)
plot!(rx)
plot!(strongest(fwd, 3))
plot!(strongest(echoes, 3))There is no direct eigenray — every forward path detours around the blocking scatterer — and the strongest echoes arrive from the tilted reflector behind and above the receiver. The transmission loss field shows the geometric shadows cast by both targets, and the interference of the backscattered field with the outgoing field:
rxs = AcousticReceiverGrid2D(1:600, -200:0)
x = transmission_loss(pm, tx, rxs; mode=:coherent)
plot(rxs, x; crange=70)
plot!(env; xlims=(0,600), linewidth=3)Limitations
The scatterer implementation is experimental, and has the following limitations:
- Tangent-plane (Kirchhoff) coupling: the reflection is computed with the plane-wave reflection coefficient at the local tangent plane. This is valid when the scatterer is large compared to a wavelength (\(ka ≫ 1\)) and away from grazing incidence. There is no diffraction or creeping-wave contribution, so shadow edges are geometric (softened only by the Gaussian beam width). Small or low-frequency targets (\(ka ≲ 1\)) are not modeled correctly and need a target-strength model instead.
- Grazing incidence: the beam-spreading curvature correction diverges at grazing incidence, and is skipped for grazing angles below \(\sin θ_g = 10^{-3}\). The correction is verified against an analytic (mirror equation) benchmark from normal incidence down to \(\sin θ_g ≈ 0.93\); between these it follows the standard analytic form but is not independently benchmarked.
- 2½D semantics: a 2D scatterer with cylindrical out-of-plane spreading models a target extended in the cross-range (\(y\)) direction, not a compact 3D body — a real sphere’s target strength is not reproduced.
- Eigenray search resolution: echoes off small scatterers subtend narrow launch-angle windows, and the default
nbeamsheuristic may miss them. Increasenbeams(a few thousand) when scatterer echoes matter. Transmission loss fields computed over a receiver grid do not suffer from this. - Modeling domain: rays exiting the domain (\(x < \texttt{rmin}\) or \(x > \texttt{rmax}\)) are assumed to never return, so anything reflective outside the domain is invisible to the model. Since
rmaxdefaults to the query range andrminto 0, features behind the receivers or to the left of the transmitter are only seen if the domain is explicitly extended to include them.
Notes
The RaySolver propagation model requires that the transmitter is located at \((x=0, y=0)\) and all receivers are located in the right half-plane (i.e., \(x>0\) and \(y=0\)). This limitation can be worked around by wrapping the model with Reframe2D, which automatically transforms 2D scenarios specified in world coordinates into the coordinate system required by the model (see the quickstart guide for an example).