acoustics¶
The jbubble.acoustics module provides models for computing the acoustic pressure radiated by a bubble, given a solved trajectory.
Emission models are not Property subclasses (they depend on the full trajectory, not just instantaneous state), and not part of the EoM (they are pure post-processing). They take a SimulationResult and a field-point distance r and return the pressure time series. They read the liquid density and speed of sound from a MediumModel, so pass the medium of the simulated equation of motion:
from jbubble.acoustics import IncompressibleMonopole
emission = IncompressibleMonopole(medium=eom.medium)
p_rad = emission(result, r=1e-2) # pressure [Pa] at 1 cm, shape (num_samples,)
For multiple field-point distances, use jax.vmap:
import jax
import jax.numpy as jnp
distances = jnp.array([1e-3, 5e-3, 1e-2, 5e-2])
p_all = jax.vmap(lambda r: emission(result, r))(distances)
# shape: (4, num_samples)
EmissionModel
¶
EmissionModel()
Acoustic emission model: bubble trajectory → radiated pressure.
Subclasses implement __call__, which takes a solved
SimulationResult and a
field-point distance r, and returns the radiated pressure time
series. Sample i of that series reaches the field point at
observer_time(result, r)[i].
To evaluate several field-point distances, use jax.vmap:
distances = jnp.array([0.001, 0.005, 0.01])
p_all = jax.vmap(lambda r: model(result, r))(distances)
# shape (3, N)
Notes
The radiated pressure of an inertial collapse is a very short spike.
e.g., For a 2 µm air bubble driven at 300 kPa and 1 MHz, the collapse peak
has a full width at half maximum of about 0.4 ns, and the default
SaveSpec, 1024 samples over the 10 µs of
a 5-cycle pulse, captures only about a tenth of its height. To resolve
such peaks, sample the trajectory every 0.1 ns or more finely, for
example with SaveSpec(num_samples=100_001) over 10 µs.
observer_time
¶
observer_time(result: SimulationResult, r: ArrayLike) -> Array
Return the time at which each emitted sample reaches distance r.
The base implementation assumes instantaneous propagation, so it
returns result.ts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
SimulationResult
|
Solved bubble trajectory. |
required |
r
|
float or Array
|
Distance from the bubble centre to the field point [m]. |
required |
Returns:
| Type | Description |
|---|---|
(Array, shape(N))
|
Observer times [s]. |
Source code in jbubble/acoustics/emission.py
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | |
IncompressibleMonopole
¶
IncompressibleMonopole(medium: MediumModel)
Bases: EmissionModel
Incompressible monopole radiation.
Assumes an incompressible surrounding liquid, so the volume acceleration \(\ddot{V}\) of the bubble, \(V = 4\pi R^3/3\), gives the radiated pressure at distance \(r\):
It's the far-field term of the incompressible pressure field \(p - p_\infty = \rho_L\left[(R^2\ddot{R} + 2R\dot{R}^2)/r - R^4\dot{R}^2/(2r^4)\right]\), so it's accurate when:
- the field point is far from the wall, \(r \gg R\), where the dropped \(R^4\dot{R}^2/(2r^4)\) term is negligible;
- the field point is within a small fraction of a wavelength, \(r \ll c_L/f\), so that the propagation delay doesn't matter;
- the bubble-wall Mach number is small, \(M = \dot{R}/c_L \ll 1\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
medium
|
MediumModel
|
Surrounding medium. The model reads the liquid density \(\rho_L\)
from |
required |
QuasiAcoustic
¶
QuasiAcoustic(medium: MediumModel)
Bases: EmissionModel
Monopole radiation delayed by the acoustic travel time.
Accounts for the finite speed of sound: the field point at distance \(r\) receives the monopole pressure that the bubble emitted at the retarded time \(t - r/c_L\):
The model returns the monopole series of
IncompressibleMonopole
on the shifted time axis
observer_time
= result.ts + r / c_L. The pressure values are therefore identical to
the incompressible monopole's; only the arrival times differ. Because
the model doesn't resample the trajectory, it keeps every collapse peak
that the saved samples resolve. If you need the pressure on another time
grid, interpolate the returned series, for example
jnp.interp(result.ts, t_obs, p_rad, left=p_rad[0]), rather than the
separate \(R\), \(\dot{R}\), and \(\ddot{R}\) series: interpolating those and
recombining them overshoots the true peak.
The validity conditions of the monopole, \(r \gg R\) and \(M \ll 1\), still apply; the delay removes the \(r \ll c_L/f\) condition.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
medium
|
MediumModel
|
Surrounding medium. The model reads the liquid density \(\rho_L\)
from |
required |
observer_time
¶
observer_time(result: SimulationResult, r: ArrayLike) -> Array
Return result.ts + r / medium.c_L, the arrival time of each sample [s].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
SimulationResult
|
Solved bubble trajectory. |
required |
r
|
float or Array
|
Distance from the bubble centre to the field point [m]. |
required |
Returns:
| Type | Description |
|---|---|
(Array, shape(N))
|
Observer times [s]. |
Source code in jbubble/acoustics/emission.py
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | |
Choosing an emission model¶
| Model | Arrival times (observer_time) |
Validity |
|---|---|---|
IncompressibleMonopole |
result.ts |
\(r \gg R\), \(r \ll c_L/f\), \(\text{Ma} \ll 1\) |
QuasiAcoustic |
result.ts + r / c_L |
\(r \gg R\), \(\text{Ma} \ll 1\) |
Both models return the same pressure values, computed from the saved samples without resampling, so both cost the same. QuasiAcoustic adds the propagation delay: plot each model's pressure against its own observer_time(result, r).
The fully compressible acoustic emission model (analogous to APECSS's wave equation integration) is planned but not yet implemented. For most research purposes, QuasiAcoustic at a field point distance \(r \gg R_0\) is adequate.