Bellhop Acoustic Toolbox

Jay Patel

May 14, 2025

Bellhop Acoustic Toolbox

Bellhop - Ocean simulation modeling

What is BELLHOP ?

Bellhop_Structure

Figure 1: BELLHOP structure

WHY BELLHOP?

Why do you need to analyze the uw-comms performance?

๐Ÿ”ง Installation

๐Ÿ“ฅ Download

๐Ÿ’ก Note: Always check compile dates. Windows binaries are slower; building from source on your native architecture is recommended.

cd at/at
make all
sudo make install

If something fails, run make clean and retry.

โš ๏ธ Edit Makefile for proper architecture, Example for Linux-based machines:

export FFLAGS= -march=native -Bstatic -Waliasing -Wampersand -Wintrinsics-std -Wno-tabs -Wintrinsic-shadow -Wline-truncation -std=gnu -O1 -ffast-math -funroll-all-loops -fomit-frame-pointer -mtune=native

More help: Issue #3 on GitHub

Detailed Installation Instructions based on OS

๐Ÿ“ Bellhop 2D (Classic)

Bellhop computes underwater acoustic propagation using Gaussian beam tracing in a 2D vertical slice (range-depth). It is ideal for scenarios assuming cylindrical symmetry.

Sample Input Structure Explanation

Below is an annotated breakdown of a typical *.ENV input file for 2D Bellhop:

'arlpy'                             / Graph Title - just for Fortran/MATLAB
25000.000000                        / FREQUENCY
1                                   / [UNUSED]
'SVWT*'                             / (SSP INTERP METHOD)(TOP LAYER)(BOTTOM ATTENUATION UNITS)(VOLUME ATTENUATION)
1 0.0 100.000000                    / [UNUSED] [UNUSED] [MAX DEPTH]

0.000000   1540.400000              / [DEPTH] [SOUND SPEED]
10.000000  1540.500000              / [DEPTH] [SOUND SPEED]
20.000000  1540.700000              / [DEPTH] [SOUND SPEED]
30.000000  1534.400000              / [DEPTH] [SOUND SPEED]
50.000000  1523.300000              / [DEPTH] [SOUND SPEED]
75.000000  1519.600000              / [DEPTH] [SOUND SPEED]
100.000000 1518.500000              / [DEPTH] [SOUND SPEED]

'A' 0.000000                        / (BOTTOM LAYER)(EXTERNAL BATHYMETRY) [SIGMA BOTTOM ROUGHNESS]
100.000000 1600.000000 0.0 1.200000 1.000000 / [MAX DEPTH] [BOTTOM SOUND SPEED] [UNUSED] [DENSITY BOTTOM] [ATTENUATION BOTTOM]

1                                   / [NUMBER SOURCES]
50.000000                            / [SOURCE DEPTHS 1:N (m)]

1                                   / [NUMBER RECEIVER DEPTHS]
8.000000                             / [RECEIVER DEPTHS 1:N (m)]

1                                   / [NUMBER RECEIVER RANGES]
2.000000                             / [RECEIVER RANGES 1:N (km)]

'R *'                               / (RUN TYPE)(BEAM TYPE)(EXTERNAL SOURCE BEAM PATTERN) ; typically 'R', 'C', 'I', 'S'

0                                   / [NUMBER OF BEAMS]; 0 means bellhop auto-calculates
-80.000000 80.000000                / [MIN ANGLE] [MAX ANGLE]

0.0 101.000000 2.020000             / [STEP SIZE] [DEPTH BOX] [RANGE BOX]; 0 = default step size

Explanation of Bellhop 2D Parameters

Bellhop 2D is particularly suitable for quick channel performance estimation and visualizing ray paths and arrival structures in a vertically stratified ocean.

For visual tools like plotray, plotshd, and plotarr, use output files .RAY, .SHD, and .ARR respectively.

๐Ÿ“Š Visualizing with ARLPY

Sound Speed Profile

Sound Speed Profile

Figure 2: Sound Speed Profile of Bedford Basin (taken on 13-10-17)

How to Plot SSP’s using ARLPY ?

# SSP Plotting using ARLPY

import arlpy.uwapm as pm
import arlpy.plot as plt
import numpy as np

env = pm.create_env2d()
ssp = [
    [ 0, 1540],  # 1540 m/s at the surface
    [10, 1530],  # 1530 m/s at 10 m depth
    [20, 1532],  # 1532 m/s at 20 m depth
    [25, 1533],  # 1533 m/s at 25 m depth
    [30, 1535]   # 1535 m/s at the seabed
]
env = pm.create_env2d(soundspeed=ssp)
pm.plot_ssp(env, width=500)

Plotting an Environment

# Plotting an Environment using ARLPY

pm.plot_env(env, width=900)

Eigenrays

# Eigenrays using ARLPY
import arlpy.uwapm as pm
import arlpy.plot as plt
import numpy as np

env = pm.create_env2d()
rays = pm.compute_eigenrays(env)
pm.plot_rays(rays, env=env, width=900)

Eigenrays

Figure 3: Eigenrays using ARLPY

arrivals = pm.compute_arrivals(env)
pm.plot_arrivals(arrivals, width=900)
arrivals[arrivals.arrival_number < 10][['time_of_arrival', 'angle_of_arrival', 'surface_bounces', 'bottom_bounces']]

time_of_arrival angle_of_arrival surface_bounces bottom_bounces
1 0.721796 22.538254 9 8
2 0.716791 -21.553932 8 8
3 0.709687 20.052078 8 7
4 0.705226 -19.034414 7 7
5 0.698960 17.484421 7 6
6 0.695070 -16.436060 6 6
7 0.689678 14.842224 6 5
8 0.686383 -13.766296 5 5
9 0.681901 12.133879 5 4
10 0.679223 -11.034208 4 4
# convert to a impulse response time series

ir = pm.arrivals_to_impulse_response(arrivals, fs=96000)
plt.plot(np.abs(ir), fs=96000, width=900)

Bathymetry

Let’s first start off by defining our bathymetry, a steep up-slope for the first 300 m, and then a gentle downslope:

# add/change bathy to env
bathy = [
    [0, 30],    # 30 m water depth at the transmitter
    [300, 20],  # 20 m water depth 300 m away
    [1000, 25]  # 25 m water depth at 1 km
]

# add/change SSP to env
ssp = [
    [ 0, 1540],  # 1540 m/s at the surface
    [10, 1530],  # 1530 m/s at 10 m depth
    [20, 1532],  # 1532 m/s at 20 m depth
    [25, 1533],  # 1533 m/s at 25 m depth
    [30, 1535]   # 1535 m/s at the seabed
]

# Appending ssp and bathy to existing env file
env = pm.create_env2d(
    depth=bathy,
    soundspeed=ssp,
    bottom_soundspeed=1450,
    bottom_density=1200,
    bottom_absorption=1.0,
    tx_depth=15
)
pm.print_env(env)


                    name : arlpy
       bottom_absorption : 1.0
          bottom_density : 1200
        bottom_roughness : 0
       bottom_soundspeed : 1450
                   depth : [[   0.   30.]
                            [ 300.   20.]
                            [1000.   25.]]
            depth_interp : linear
               frequency : 25000
               max_angle : 80
               min_angle : -80
                  nbeams : 0
                rx_depth : 10
                rx_range : 1000
              soundspeed : [[   0. 1540.]
                            [  10. 1530.]
                            [  20. 1532.]
                            [  25. 1533.]
                            [  30. 1535.]]
       soundspeed_interp : spline
                 surface : None
          surface_interp : linear
                tx_depth : 15
       tx_directionality : None
                    type : 2D


pm.plot_env(env, width=900)

Looks more interesting! Let’s see what the eigenrays look like, and also the arrival structure:

rays = pm.compute_eigenrays(env)
pm.plot_rays(rays, env=env, width=900)

We could also ignore the receiver, and plot rays launched at various angles:

rays = pm.compute_rays(env)
pm.plot_rays(rays, env=env, width=900)
import numpy as np
from scipy.interpolate import griddata
import scipy.ndimage as ndimage
from scipy.ndimage import gaussian_filter
import scipy
# from scipy.misc import imsave
from matplotlib import cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from stl import mesh, Mode
import matplotlib.tri as mtri
from mpl_toolkits.mplot3d.axes3d import get_test_data
from pandas import read_csv



data = read_csv('bathy.txt', sep='\s+', header=None, names=['x', 'y', 'depth'])

x = np.arange(data.x.min(), data.x.max()+1)
y = np.arange(data.y.min(), data.y.max()+1)

X, Y = np.meshgrid(x, y)

Z = griddata(data[['x','y']].values, -data['depth'].values, (X, Y), method='linear')

# make the grid square
Z[np.isnan(Z)] = 0

fig = plt.figure(figsize=(14, 8))
ax = fig.add_subplot(111)
plt.imshow(Z)
plt.show()

png

Figure 4: Bedford Basin Bathy 2D

Bedford Basin Bathy 3D

Figure 5: Bedford Basin Bathy 3D

or place lots of receivers in a grid to visualize the acoustic pressure field (or equivalently transmission loss). We can modify the environment (env) without having to recreate it, as it is simply a Python dictionary object:

env['rx_range'] = np.linspace(0, 1000, 1001)
env['rx_depth'] = np.linspace(0, 30, 301)

Transmission Loss

- RUN TYPE BELLHOP

OPTION(1:1):           'R' generates a ray file
                       'E' generates an eigenray file
                       'A' generates an amplitude-delay file (ascii)
                       'a' generate  an amplitude-delay file (binary)
                       'C' Coherent     TL calculation
                       'I' Incoherent   TL calculation
                       'S' Semicoherent TL calculation
                            (Lloyd mirror source pattern)                        
tloss = pm.compute_transmission_loss(env)
pm.plot_transmission_loss(tloss, env=env, clim=[-60,-30], width=900)

We see a complicated interference pattern, but an interesting focusing at 800 m at a 15 m depth. The detailed interference pattern is of course sensitive to small changes in the environment. A less sensitive, but more averaged out, transmission loss estimate can be obtained using the incoherent mode:

tloss = pm.compute_transmission_loss(env, mode='incoherent')
pm.plot_transmission_loss(tloss, env=env, clim=[-60,-30], width=900)

Source directionality

beampattern = np.array([
    [-180,  10], [-170, -10], [-160,   0], [-150, -20], [-140, -10], [-130, -30],
    [-120, -20], [-110, -40], [-100, -30], [-90 , -50], [-80 , -30], [-70 , -40],
    [-60 , -20], [-50 , -30], [-40 , -10], [-30 , -20], [-20 ,   0], [-10 , -10],
    [  0 ,  10], [ 10 , -10], [ 20 ,   0], [ 30 , -20], [ 40 , -10], [ 50 , -30],
    [ 60 , -20], [ 70 , -40], [ 80 , -30], [ 90 , -50], [100 , -30], [110 , -40],
    [120 , -20], [130 , -30], [140 , -10], [150 , -20], [160 ,   0], [170 , -10],
    [180 ,  10]
])
env['tx_directionality'] = beampattern
tloss = pm.compute_transmission_loss(env)
pm.plot_transmission_loss(tloss, env=env, clim=[-60,-30], width=900)
tloss = pm.compute_transmission_loss(env, mode='incoherent')
pm.plot_transmission_loss(tloss, env=env, clim=[-60,-30], width=900)

Undulating water surface

surface = np.array([[r, 0.5+0.5*np.sin(2*np.pi*0.005*r)] for r in np.linspace(0,1000,1001)])
env['surface'] = surface
tloss = pm.compute_transmission_loss(env)
pm.plot_transmission_loss(tloss, env=env, clim=[-60,-30], width=900)
tloss = pm.compute_transmission_loss(env, mode='incoherent')
pm.plot_transmission_loss(tloss, env=env, clim=[-60,-30], width=900)
env['rx_range'] = 800
env['rx_depth'] = 15

rays = pm.compute_eigenrays(env)
pm.plot_rays(rays, env=env, width=900)
arrivals = pm.compute_arrivals(env)
pm.plot_arrivals(arrivals, dB=True, width=900)

Note: We plotted the amplitudes in dB, as the later arrivals are much weaker than the first one, and better visualized in a logarithmic scale.

Bellhop3D โ€“ 3D Beam Tracing for Complex Ocean Environments

Bellhop3D is an advanced extension of the classic Bellhop model, enabling simulation of 3D acoustic propagation in ocean environments. While standard Bellhop operates in a 2D vertical slice, Bellhop3D incorporates azimuthal variation, allowing researchers and engineers to analyze how sound behaves over both range and bearing โ€” critical for non-homogeneous or anisotropic ocean environments.

๐Ÿ”„ Nx2D Strategy (a.k.a. 2.5D Modeling)

Rather than solving full 3D ray equations (which is computationally intensive), Bellhop3D uses an efficient approximation called Nx2D:

This approach enables practical yet accurate modeling of complex underwater sound propagation in shallow waters, canyons, or areas with bathymetric variation across bearings.


๐ŸŽฏ Beam Types Supported

Bellhop3D allows users to choose different ray-tracing approximations, depending on the accuracy and speed required:

Beam Type Description
Cerveny Beams High-precision beams based on dynamic ray tracing and Gaussian beam theory
Geometric Hat Beams Simple beams with a “hat”-shaped energy profile
Geometric Gaussian Beams Smoother, energy-preserving Gaussian profile rays
Cartesian Hat Beams Hat-beams mapped to Cartesian coordinates for ease of use

โš™๏ธ Each type balances accuracy, computational cost, and ease of interpretation, depending on the simulation objective.


๐Ÿงช Typical Use Cases


๐Ÿ› ๏ธ Implementation Steps

  1. Define environment (.env) and transmission parameters.
  2. Choose radial resolution (e.g., every 5ยฐ or 10ยฐ around the source).
  3. Run Bellhop on each bearing (automated script).
  4. Interpolate results into a 3D field.

This can be scripted in Python, MATLAB, or automated using custom wrappers in arlpy, depending on your workflow.

Sample Input Structure Explanation

Below is an annotated breakdown of a typical *.ENV input file for Bellhop3D:

'Munk profile'        ! TITLE: Descriptive label for the simulation
50.0                  ! FREQ: Frequency in Hz
1                     ! NMEDIA: Number of media layers (usually 1 for ocean)
'CVW'                 ! SSPOPT: Sound speed interpolation type (e.g., C-linear, CVN, CVW)
51  0.0  20000.0      ! Number of SSP points, min and max depth
...                  ! Depth/sound speed profile follows
'A' 0.0               ! Altimetry model (usually flat surface)
20000.0  1600.00 0.0 1.8 0.8 /  ! Bathymetry: depth, speed, density, etc.

# Source coordinates
1                     ! NSx: number of source x positions
0.0 /                 ! x coordinate(s) in km
1                     ! NSy
0.0 /                 ! y coordinate(s) in km
1                     ! NSz
1000.0 /              ! Source depth in m

# Receiver grid
501                   ! NRz
0 5000 /              ! Receiver depths (0 to 5000 m)
1001                  ! NR
0.0  100.0 /          ! Receiver ranges (0 to 100 km)
2                     ! Ntheta
0.0 1.0 /             ! Receiver bearings (degrees)

# Run type (e.g., coherent, Gaussian beam, Nx2D)
'CG   3'              ! Option string: C=Coherent, G=Geometric beams, 3=3D mode

# Beam fans
51 6                  ! Nalpha, ISINGLE: Number of elevation beams and single-beam index
-14.66 14.66 /        ! Alpha (declination angles)
11 46                 ! Nbeta, ISINGLE: Number of azimuth beams and index
-2  2 /               ! Beta (bearing angles)

# Numerical integration
100.0  100.0 100.0 20500.0 ! STEP, Box%x, Box%y, Box%z (m, km, km, m)

Explanation of Bellhop3D Parameters


References

  1. Technical documentation: Ocean Acoustics Toolbox
  2. ARLPY Technical documentation: ARLPY Docs
  3. ๐Ÿ“ GitHub Repository (Code + Examples): Bellhop ARLPY ECED6575
Underwater Acoustic Communication Bellhop Channel Modelling Heterogeneous marine robots
Jay Patel

Jay Patel

OFI Postdoctoral Fellow in Underwater Communication Systems

My research interests include electronics & communications, distributed underwater robotics, mobile computing and programmable matter.