bubble¶
The jbubble.bubble subpackage contains all bubble physics components: state representation, the Property abstraction, and gas/shell/medium/EoM models.
State¶
BubbleState
¶
BubbleState(
R: Array,
*,
R_dot: Array = jnp.zeros(()),
R0: Array = jnp.zeros(()),
P_gas0: Array = jnp.zeros(()),
)
Bases: Module
Standard bubble state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
R
|
Array
|
Bubble wall radius [m]. |
required |
R_dot
|
Array
|
Bubble wall velocity [m/s]. Keyword-only. Default: |
jnp.zeros(())
|
R0
|
Array
|
Equilibrium bubble radius [m]. Frozen
(\(\mathrm{d}R_0/\mathrm{d}t = 0\)) in the standard case; it becomes
a slow state variable for processes such as rectified diffusion.
Keyword-only. Default: |
jnp.zeros(())
|
P_gas0
|
Array
|
Equilibrium gas pressure [Pa]. Frozen in the standard case.
Keyword-only. Default: |
jnp.zeros(())
|
Notes
A zero R0 or P_gas0 means "unset":
solve_eom fills it from the equation of
motion, so BubbleState(R=1.2 * R0) starts at rest at 1.2 times the
equilibrium radius.
EquationOfMotion.initial_state
builds the same state with R= and R_dot=. For an empty cavity, set
P_gas0 to a tiny positive value, such as 1e-12, because zero means
"unset".
Properties¶
The Property abstraction is the key extensibility mechanism in jbubble. Any physical parameter that could depend on the bubble state is represented as a Property — a callable state → scalar. Plain floats are automatically promoted via as_property.
Property
¶
Property()
Abstract base for state-dependent (or constant) bubble properties.
Any callable eqx.Module that maps a
BubbleState to a scalar array
inherits from this class. The concrete subclass
ConstantProperty handles
the common case of a fixed value; state-dependent models, such as
MarmottantSurfaceTension,
override __call__ directly.
ConstantProperty
¶
ConstantProperty(val: ArrayLike)
NeuralProperty
¶
NeuralProperty(net: Module)
Bases: Property
A Property backed by an Equinox neural network.
The network receives a normalised 1-D input [R / R0] and must
return a 1-D output of shape (1,). You are responsible for any
output transform that the physics needs. For example, wrap the final
layer output with jax.nn.softplus or jnp.exp to keep a surface
tension positive.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
net
|
Module
|
Any callable Equinox module with signature
|
required |
Examples:
>>> import equinox as eqx
>>> import jax
>>> import jax.numpy as jnp
>>> key = jax.random.PRNGKey(0)
>>> mlp = eqx.nn.MLP(in_size=1, out_size=1, width_size=32, depth=3,
... final_activation=jax.nn.softplus, key=key)
>>> sigma = NeuralProperty(net=mlp)
as_property
¶
Convert a plain scalar or JAX array to a ConstantProperty, or pass it through.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
val
|
float, jax.Array, or Property
|
A plain scalar, a JAX array (possibly a tracer), or an existing
|
required |
Returns:
| Type | Description |
|---|---|
Property
|
|
Source code in jbubble/bubble/property.py
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
Gas models¶
GasModel
¶
GasModel()
Internal gas pressure model.
Computes the outward gas pressure \(p_\text{gas}\) as a function of the
instantaneous state. The model reads the equilibrium configuration,
R0 and P_gas0, directly from state, so gas models store only
their intrinsic physical parameters, such as the polytropic exponent.
Concrete models include the polytropic law
(PolytropicGas) and the
van der Waals corrected gas
(VanDerWaalsGas).
is_admissible
¶
is_admissible(state: BubbleState) -> Array
Return whether the gas law is defined at state.
The default requires a positive radius, R > 0. A gas law with a
smaller domain, such as
VanDerWaalsGas, overrides
this method. solve_eom uses it,
through
EquationOfMotion.is_admissible,
to keep gradients finite when a rejected trial step leaves the
domain.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
BubbleState
|
State to check. Its leaves may be NaN or infinite. |
required |
Returns:
| Type | Description |
|---|---|
Array
|
Boolean scalar. |
Source code in jbubble/bubble/gas.py
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | |
VanDerWaalsGas
¶
Bases: GasModel
Hard-core corrected polytropic gas (van der Waals).
where \(h = h_\text{frac} R_0\) is the van der Waals hard-core radius: the
radius of the bubble's gas content compressed to its excluded volume. The
hard core is a property of the gas, not of the bubble, so h_frac
depends on the gas species and its equilibrium density. The pressure
diverges as \(R \to h\) and the law is undefined for \(R \le h\), which
is_admissible
reports to the solver.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gamma
|
float or Property
|
Polytropic exponent. |
required |
h_frac
|
float or Property
|
Hard-core radius as a fraction of |
required |
is_admissible
¶
is_admissible(state: BubbleState) -> Array
Return whether R lies outside the hard core, R > h_frac * R0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
BubbleState
|
State to check. Its leaves may be NaN or infinite. |
required |
Returns:
| Type | Description |
|---|---|
Array
|
Boolean scalar. |
Source code in jbubble/bubble/gas.py
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | |
Shell models¶
ShellModel
¶
ShellModel(sigma: ArrayLike | Property)
Bubble shell or coating model.
Computes the total inward stress from the shell, including:
- Laplace pressure from surface tension, \(2\sigma(R)/R\).
- Shell viscous dissipation, for example \(4\kappa_s\dot{R}/R^2\).
- Shell elastic restoring forces, for thick shells.
Every ShellModel holds a Property
as its sigma field. The field's
as_property converter turns a
plain float into a Property.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sigma
|
float or Property
|
Surface tension law [N/m]. |
required |
p_laplace
¶
p_laplace(state: BubbleState) -> Array
Return the Laplace pressure from surface tension, \(2\sigma(R)/R\) [Pa].
Source code in jbubble/bubble/shell.py
63 64 65 | |
p_elastic
¶
p_elastic(state: BubbleState) -> Array
Return the elastic contribution from the shell [Pa].
Source code in jbubble/bubble/shell.py
67 68 69 70 | |
p_viscous
¶
p_viscous(state: BubbleState) -> Array
Return the viscous contribution from the shell [Pa].
Source code in jbubble/bubble/shell.py
72 73 74 75 | |
NoShell
¶
NoShell(sigma: ArrayLike | Property)
Bases: ShellModel
No shell coating, only Laplace pressure.
Suitable for uncoated gas bubbles. Accepts a plain float for sigma,
for example 72e-3 for water.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sigma
|
float or Property
|
Surface tension law [N/m]. |
required |
LipidShell
¶
Bases: ShellModel
Thin lipid shell with surface viscosity.
This is the shell model of Marmottant et al. (2005) and of its smoothed variants.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sigma
|
float or Property
|
Surface tension law [N/m]. |
required |
kappa_s
|
float or Property
|
Shell surface-dilatational viscosity [N s/m]. |
required |
Notes
p_elastic returns zero for this model. The shell elasticity isn't
absent: the surface tension law sigma encodes all of it. When sigma
is state-dependent, as with
MarmottantSurfaceTension
or
SmoothMarmottantSurfaceTension,
the area-elasticity term \(\chi\left[(R/R_b)^2 - 1\right]\) enters
through the Laplace pressure \(2\sigma(R)/R\), not through a separate
p_elastic term. This matches how the literature writes the
Marmottant model: the elastic and ruptured regimes modify \(\sigma(R)\)
rather than adding an independent stress contribution.
ThickShell
¶
ThickShell(
sigma: ArrayLike | Property,
d_s: ArrayLike | Property,
G_s: ArrayLike | Property,
mu_s: ArrayLike | Property,
)
Bases: ShellModel
Incompressible viscoelastic shell of finite thickness (Church 1995).
The shell is a layer of incompressible Kelvin-Voigt solid with shear modulus \(G_s\), shear viscosity \(\mu_s\), and thickness \(d_s\) at the equilibrium radius \(R_0\). Integrating its radial stress across the layer gives the shell stresses of the Church (1995) model in the finite-deformation form of Qin and Ferrara (2010, Eq. 10):
where \(R\) is the outer, liquid-side radius and \(V_s\) is the shell volume divided by \(4\pi/3\). Because the shell is incompressible, \(V_s\) stays constant, and \(R^3 - V_s\) is the cube of the inner radius. The total shell pressure is
For a thin shell, \(d_s \ll R_0\), so \(V_s \approx 3 R_0^2 d_s\), and the model reduces to the thin-shell model of Hoff et al. (2000):
The viscous terms agree exactly in that limit, and the elastic terms agree to first order in the strain. For small oscillations about \(R_0\), the shell therefore adds a stiffness of \(12 G_s d_s / R_0\) per unit radial strain and a damping of \(12 \mu_s d_s / R_0^2\) per unit wall velocity, with relative corrections of order \(d_s/R_0\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sigma
|
float or Property
|
Surface tension law [N/m]. |
required |
d_s
|
float or Property
|
Shell thickness at the equilibrium radius \(R_0\) [m]. The shell volume, not the thickness, stays constant as the bubble moves. |
required |
G_s
|
float or Property
|
Shell shear modulus [Pa]. It can be state-dependent, for example to model strain stiffening or strain softening. |
required |
mu_s
|
float or Property
|
Shell shear viscosity [Pa s]. It can be state-dependent, for example to model shear thinning. |
required |
Notes
jbubble integrates a single radius, so this class simplifies the full Church and Qin-Ferrara equations as follows:
- The gas model reads the outer radius \(R\) rather than the inner radius \((R^3 - V_s)^{1/3}\), so the gas stiffness comes out low by a relative amount of about \(3 d_s / R_0\).
- The class neglects the shell's inertia, a relative error of order \(d_s / R_0\) in the inertial terms.
- A single surface tension \(\sigma\) acts at \(R\), in place of separate gas-shell and shell-liquid tensions.
- The shell is unstrained at \(R_0\), and
initial_statebalances the Laplace pressure with the gas pressure.
In Church's model, as quoted by Tu et al. (2009, Eqs. 2 and 3), \(R_1\) is the inner radius and \(R_{e1}\) is the unstrained inner radius. The viscous term is identical to Church's \(4 \mu_s V_s \dot{R}_1 / (R_1 R^3)\), written with the outer radius. The elastic term agrees to first order in the strain with Church's \(4 G_s (V_s / R^3)(1 - R_{e1}/R_1)\) if you set \(R_{e1} = R_0 - d_s\), which leaves the shell unstrained at \(R_0\), as in this class. Church's own \(R_{e1}\) is slightly larger, because Church's shell is strained at equilibrium so that it carries the Laplace pressure. The model needs \(R^3 > V_s\), that is, a positive inner radius.
References
Church, C. C. (1995). The effects of an elastic solid surface layer on the radial pulsations of gas bubbles. J. Acoust. Soc. Am. 97(3), 1510-1521. doi:10.1121/1.412091
Hoff, L., Sontum, P. C., & Hovem, J. M. (2000). Oscillations of polymeric microbubbles: Effect of the encapsulating shell. J. Acoust. Soc. Am. 107(4), 2272-2280. doi:10.1121/1.428557
Qin, S., & Ferrara, K. W. (2010). A model for the dynamics of ultrasound contrast agents in vivo. J. Acoust. Soc. Am. 128(3), 1511-1521. doi:10.1121/1.3409476
Tu, J., Guan, J., Qiu, Y., & Matula, T. J. (2009). Estimating the shell parameters of SonoVue microbubbles using light scattering. J. Acoust. Soc. Am. 126(6), 2954-2962. doi:10.1121/1.3242346
Surface tension Properties¶
These are Property subclasses that encode a state-dependent surface tension law. They are passed as the sigma argument to shell models.
MarmottantSurfaceTension
¶
MarmottantSurfaceTension(
R_buckle_ratio: ArrayLike,
chi: ArrayLike | Property,
sigma_rupture: ArrayLike | Property,
)
Bases: Property
Piecewise Marmottant surface tension law.
Three regimes, based on the radius \(R\) relative to the buckling radius \(R_b\) and the rupture radius \(R_r\):
where \(R_b\) is R_buckle_ratio * state.R0, \(\sigma_r\) is
sigma_rupture, and continuity of \(\sigma\) at the elastic-to-ruptured
transition gives \(R_r = R_b\sqrt{1 + \sigma_r/\chi}\).
The surface tension at \(R_0\) is
\(\sigma_0 = \chi\left[(1/r_b)^2 - 1\right]\), where \(r_b\) is
R_buckle_ratio, so \(r_b = (1 + \sigma_0/\chi)^{-1/2}\). A ratio of 1
or more starts the bubble buckled.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
R_buckle_ratio
|
float or Array
|
Buckling radius as a fraction of |
required |
chi
|
float or Property
|
Shell elasticity [N/m]. |
required |
sigma_rupture
|
float or Property
|
Surface tension after rupture [N/m]. |
required |
Notes
\(\sigma(R)\) has discontinuous first derivatives at the regime
boundaries. For applications that need smooth gradients, such as
gradient-based optimisation, use
SmoothMarmottantSurfaceTension
instead.
The as_property converter
stores chi and sigma_rupture as
Property instances. To replace
one in an existing model with eqx.tree_at, which bypasses the
converter, pass a Property or target its val leaf.
References
Marmottant, P., van der Meer, S., Emmer, M., Versluis, M., de Jong, N., Hilgenfeldt, S., & Lohse, D. (2005). A model for large amplitude oscillations of coated bubbles accounting for buckling and rupture. J. Acoust. Soc. Am. 118(6), 3499-3505. doi:10.1121/1.2109427
SmoothMarmottantSurfaceTension
¶
SmoothMarmottantSurfaceTension(
R_buckle_ratio: ArrayLike,
chi: ArrayLike | Property,
sigma_rupture: ArrayLike | Property,
smoothing: ArrayLike = 0.01,
)
Bases: Property
Marmottant surface tension law with smoothed corners.
The piecewise Marmottant law clips the elastic surface tension to the
interval \([0, \sigma_r]\). This law, which is jbubble's own
construction, replaces the hard clip with a logistic-smoothed clamp
whose width \(\varepsilon\), set by smoothing, is a fraction of
\(\sigma_r\):
where \(r_b\) is R_buckle_ratio, \(\chi\) is chi, \(\sigma_r\) is
sigma_rupture, and \(\operatorname{softplus}(x) = \ln(1 + e^x)\).
Equivalently, \(\sigma\) is the Marmottant law averaged over a logistic
spread of scale \(\varepsilon\sigma_r\) in the elastic surface tension,
which rounds the buckling and rupture corners.
The law has the following properties:
- It's infinitely differentiable in \(R\), \(\chi\), \(r_b\), and \(\sigma_r\).
- It's non-decreasing in \(R\) and stays within \([0, \sigma_r]\).
- It differs from the Marmottant law by at most
\(\varepsilon \ln 2 \, \sigma_r\) for every \(\chi\) and \(r_b\), and it
reaches that bound at the two corners. With the default
smoothing = 0.01and \(\sigma_r = 0.072\) N/m, the bound is 0.5 mN/m. - It converges uniformly to the Marmottant law as \(\varepsilon \to 0\).
- It's well-posed for all positive
R_buckle_ratio,chi, andsigma_rupture, including bubbles that start buckled (R_buckle_ratioof 1 or more) or ruptured.
Use this law for gradient-based fitting of a lipid shell. Use
MarmottantSurfaceTension
for the exact piecewise law in forward simulations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
R_buckle_ratio
|
float or Array
|
Buckling radius as a fraction of |
required |
chi
|
float or Property
|
Shell elasticity [N/m]. Must be positive. |
required |
sigma_rupture
|
float or Property
|
Surface tension after rupture [N/m]. Must be positive. |
required |
smoothing
|
float or Array
|
Corner width \(\varepsilon\) as a fraction of |
0.01
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Notes
The default smoothing = 0.01 keeps the law close to the piecewise
one without stiffening the ODE. Keller-Miksis benchmarks with
R_buckle_ratio = 0.98, \(\chi\) from 0.1 to 1.5 N/m, R0 from 1 to
3 µm, 1 to 3 MHz, and 25 kPa to 1 MPa show that, at the default:
- The median root-mean-square difference between the radius curves of the two laws is of order \(10^{-5} R_0\), about the size of the piecewise law's own error at the default solver tolerances. At 50 kPa and below, where the piecewise law's own error is smallest, the smoothing error dominates.
- The derivative of the peak radius with respect to \(\chi\) has the same sign as the piecewise law's in every case tested.
- Fitting \(\chi\) to radius curves from the piecewise law at 50 kPa recovers it to within 0.1%.
- The solver takes about as many steps as it does for the piecewise law, with a median ratio between 0.92 and 1.0.
The smoothing biases the result when the bubble starts near a corner,
that is, when \(\sigma_0 = \sigma(R_0)\) of the piecewise law lies within
about \(5\varepsilon\sigma_r\) of 0 or of \(\sigma_r\). For example,
R_buckle_ratio close to 1 starts the bubble near buckling. Then
\(\sigma(R_0)\) shifts by up to \(\varepsilon \ln 2 \, \sigma_r\), and the
elasticity at \(R_0\) drops to as little as half the Marmottant value.
At smoothing = 0.01, this gives a radius bias of about \(10^{-3} R_0\)
(root mean square) and a bias in fitted \(\chi\) of a few percent, up to
8% at 200 kPa. In that regime, use smoothing = 0.005, or use
MarmottantSurfaceTension
for forward-only simulations. The bubble still starts at equilibrium,
because
initial_state
reads \(\sigma(R_0)\) from the law itself.
The as_property converter
stores chi and sigma_rupture as
Property instances.
GompertzSurfaceTension
¶
GompertzSurfaceTension(
R_buckle_ratio: ArrayLike,
chi: ArrayLike | Property,
sigma_rupture: ArrayLike | Property,
)
Bases: Property
Marmottant-Gompertz surface tension law (Gümmer et al. 2021).
A Gompertz function replaces the corners of the piecewise Marmottant law with a smooth curve that takes the same inputs (Gümmer, Schenke, and Denner 2021, Eqs. 11 and 13-15):
where \(r_b\) is R_buckle_ratio, \(\chi\) is chi, and \(\sigma_r\) is
sigma_rupture (the paper's \(\sigma_c\)). The coefficient \(b\) pins
\(\sigma(R_0) = \sigma_0\), the Marmottant elastic value. The
coefficient \(c\) makes the maximum slope of the curve,
\(c\,\sigma_r/(e R_b)\) where \(\sigma = \sigma_r/e\), equal to the
Marmottant slope at \(R = R_b\sqrt{1 + \sigma_r/(2\chi)}\). The paper
takes \(\sigma_0\) as its input; R_buckle_ratio carries the same
information, with \(r_b = (1 + \sigma_0/\chi)^{-1/2}\) (Eq. 11).
The class evaluates the algebraically identical form
and caps the inner exponent at 50, so a deep compression gives \(\sigma = 0\) with a finite gradient instead of an overflow.
Use this law to reproduce Gümmer et al. (2021). For gradient-based
fitting, use
SmoothMarmottantSurfaceTension,
which converges to the Marmottant law. The Notes explain why.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
R_buckle_ratio
|
float or Array
|
Buckling radius as a fraction of |
required |
chi
|
float or Property
|
Shell elasticity [N/m]. |
required |
sigma_rupture
|
float or Property
|
Asymptotic (ruptured) surface tension [N/m]. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the law is ill-posed: it needs \(\chi > 0\) and
\(0 < \sigma_0 < \sigma_r\), so |
Notes
The curve is anchored at \(R_0\), not at the Marmottant transitions, so
no value of \(c\) makes it converge to the Marmottant law: as \(c\) grows,
the curve tends to a step at \(R_0\). Its elasticity at \(R_0\) matches
the Marmottant value only approximately, when \(\sigma_0\) is near
\(\sigma_r/e\), and it falls towards zero as \(\sigma_0\) approaches 0 or
\(\sigma_r\). With R_buckle_ratio fixed, \(\sigma_0\) grows in
proportion to \(\chi\), so the mismatch changes as you vary \(\chi\).
Fitted to radius curves from the Marmottant law, this law's \(\chi\)
comes out biased by about 6% at R_buckle_ratio = 0.98 and by up to
about 47% at R_buckle_ratio = 0.995.
The as_property converter
stores chi and sigma_rupture as
Property instances.
References
Gümmer, J., Schenke, S., & Denner, F. (2021). Modelling lipid-coated microbubbles in focused ultrasound applications at subresonance frequencies. Ultrasound Med. Biol. 47(10), 2958-2979. doi:10.1016/j.ultrasmedbio.2021.06.012
Medium models¶
MediumModel
¶
MediumModel(
mu: ArrayLike | Property,
*,
rho_L: ArrayLike = 998.0,
c_L: ArrayLike = 1500.0,
)
Surrounding medium (fluid or tissue) model.
Computes the total inward viscous and elastic stresses that the surrounding medium exerts on the bubble wall. For a Newtonian liquid this is \(4\mu\dot{R}/R\). Viscoelastic media, such as Kelvin-Voigt and neo-Hookean media, add elastic restoring forces.
Subclasses implement separate methods for the viscous and elastic
contributions, and the default __call__ sums them to get the total
medium pressure \(p_\text{medium}(\text{state})\).
The mu field's as_property
converter turns a plain float into a
Property.
The medium also holds the liquid's density rho_L and speed of sound
c_L, which the equations of motion and the emission models read. They
are plain constants, not Property fields, because the equations of
motion assume a liquid of constant density and sound speed. Both are
keyword-only and default to water at 20 °C, so a subclass can add
required fields after them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mu
|
float or Property
|
Viscous scaling parameter. For Newtonian, Kelvin-Voigt, and
neo-Hookean media this is the dynamic viscosity [Pa s]. For
|
required |
rho_L
|
float or Array
|
Liquid density [kg/m³]. Keyword-only. Default: |
998.0
|
c_L
|
float or Array
|
Speed of sound in the liquid [m/s]. Keyword-only. Default:
|
1500.0
|
References
NIST Chemistry WebBook, NIST Standard Reference Database 69, Thermophysical Properties of Fluid Systems: water at 0.101325 MPa. doi:10.18434/T4D303
p_viscous
¶
p_viscous(state: BubbleState) -> Array
Return the viscous contribution to the medium pressure [Pa].
Source code in jbubble/bubble/medium.py
78 79 80 81 | |
p_elastic
¶
p_elastic(state: BubbleState) -> Array
Return the elastic contribution to the medium pressure [Pa].
Source code in jbubble/bubble/medium.py
83 84 85 86 | |
NewtonianMedium
¶
NewtonianMedium(
mu: ArrayLike | Property,
*,
rho_L: ArrayLike = 998.0,
c_L: ArrayLike = 1500.0,
)
Bases: MediumModel
Newtonian liquid medium.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mu
|
float or Property
|
Dynamic viscosity [Pa s]. |
required |
rho_L
|
float or Array
|
Liquid density [kg/m³]. Keyword-only. Default: |
998.0
|
c_L
|
float or Array
|
Speed of sound in the liquid [m/s]. Keyword-only. Default:
|
1500.0
|
KelvinVoigtMedium
¶
KelvinVoigtMedium(
mu: ArrayLike | Property,
G: ArrayLike | Property,
*,
rho_L: ArrayLike = 998.0,
c_L: ArrayLike = 1500.0,
)
Bases: MediumModel
Kelvin-Voigt viscoelastic medium (Yang & Church 2005).
The elastic term integrates a linear (Hookean) elastic stress through the incompressible surrounding solid. Yang and Church take the displacement field \(u(r) = (R^3 - R_0^3)/(3r^2)\) of the incompressible radial motion, so the deviatoric stress difference is \(\tau_{rr} - \tau_{\theta\theta} = -2G(R^3 - R_0^3)/r^3\), and
The model reads \(R_0\) from state.R0. The elastic term is zero at
\(R = R_0\), linearises to \(4G(R - R_0)/R_0\), which is the same small-strain
limit as NeoHookeanMedium,
and stays bounded by \(4G/3\) under expansion. For large strains, prefer
the neo-Hookean model, whose constitutive law holds at finite strain.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mu
|
float or Property
|
Dynamic viscosity [Pa s]. |
required |
G
|
float or Property
|
Shear modulus [Pa]. It can be state-dependent, for example to model strain stiffening. |
required |
rho_L
|
float or Array
|
Liquid density [kg/m³]. Keyword-only. Default: |
998.0
|
c_L
|
float or Array
|
Speed of sound in the liquid [m/s]. Keyword-only. Default:
|
1500.0
|
References
Yang, X., & Church, C. C. (2005). A model for the dynamics of gas bubbles in soft tissue. The Journal of the Acoustical Society of America, 118(6), 3595-3606. https://doi.org/10.1121/1.2118307
Warnez, M. T., & Johnsen, E. (2015). Numerical modeling of bubble dynamics in viscoelastic media with relaxation. Physics of Fluids, 27(6), 063103, Eq. 42. https://doi.org/10.1063/1.4922598
NeoHookeanMedium
¶
NeoHookeanMedium(
mu: ArrayLike | Property,
G: ArrayLike | Property,
*,
rho_L: ArrayLike = 998.0,
c_L: ArrayLike = 1500.0,
)
Bases: MediumModel
Neo-Hookean finite-strain viscoelastic medium.
Captures finite-strain behaviour at large oscillation amplitudes, where the Kelvin-Voigt linear approximation breaks down.
The elastic pressure comes from integrating the deviatoric stress difference \(\sigma_{\theta\theta} - \sigma_{rr}\) through the surrounding incompressible neo-Hookean solid. With the substitution \(v = r_0/r\), the integral collapses exactly to \(2G\int_{R_0/R}^{1} (1 + v^3)\,\mathrm{d}v\), which gives
Key behaviour:
- Zero at \(R = R_0\): no elastic stress at equilibrium.
- Reduces to \(4G(R - R_0)/R_0\) for small strains, identical to Kelvin-Voigt.
- Saturates to \(5G/2\) as \(R \to \infty\). Physically, the material near the bubble thins to zero, so the elastic pressure plateaus.
- Diverges to \(-\infty\) as \(R \to 0\): strong restoring force under compression.
The total medium pressure is
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mu
|
float or Property
|
Dynamic viscosity [Pa s]. Accepts a plain float. |
required |
G
|
float or Property
|
Shear modulus [Pa]. Accepts a plain float. It can be state-dependent, for example to model strain stiffening. |
required |
rho_L
|
float or Array
|
Liquid density [kg/m³]. Keyword-only. Default: |
998.0
|
c_L
|
float or Array
|
Speed of sound in the liquid [m/s]. Keyword-only. Default:
|
1500.0
|
PowerLawMedium
¶
PowerLawMedium(
mu: ArrayLike | Property,
n_exp: ArrayLike | Property,
eps: float = 100.0,
*,
rho_L: ArrayLike = 998.0,
c_L: ArrayLike = 1500.0,
)
Bases: MediumModel
Power-law (generalised Newtonian) surrounding medium.
The viscosity depends on the local shear rate, \(\eta = K\dot{\gamma}^{\,n-1}\), where \(\dot{\gamma} = \sqrt{2\mathbf{D}:\mathbf{D}}\) is the rheometric shear rate, the convention under which rheometers report \(K\) and \(n\). For the incompressible radial flow \(u = R^2\dot{R}/r^2\) the shear rate is \(\dot{\gamma}(r) = 2\sqrt{3}\,|R^2\dot{R}|/r^3\), which is \(\dot{\gamma}_w = 2\sqrt{3}\,|\dot{R}|/R\) at the bubble wall. Integrating the viscous stress through the liquid,
The factor \(1/n\) comes from the radial variation of \(\eta\). For \(n = 1\) this is exactly the Newtonian result \(4K\dot{R}/R\).
The pure power law has an infinite viscosity at zero shear rate when \(n < 1\), which makes the right-hand side non-smooth whenever \(\dot{R}\) changes sign. The model regularises the wall shear rate smoothly:
Special cases:
- \(n < 1\): shear-thinning (biological tissue, blood, polymer gels).
- \(n = 1\): Newtonian; recovers
NewtonianMediumwith viscosity \(K\). - \(n > 1\): shear-thickening.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mu
|
float or Property
|
Consistency index \(K\) [Pa sⁿ], in the rheometric convention
\(\eta = K\dot{\gamma}^{\,n-1}\) with
\(\dot{\gamma} = \sqrt{2\mathbf{D}:\mathbf{D}}\). The inherited field
name is |
required |
n_exp
|
float or Property
|
Power-law exponent (dimensionless, positive). |
required |
eps
|
float
|
Shear-rate regularisation \(\varepsilon\) [s⁻¹]. Default: |
100.0
|
rho_L
|
float or Array
|
Liquid density [kg/m³]. Keyword-only. Default: |
998.0
|
c_L
|
float or Array
|
Speed of sound in the liquid [m/s]. Keyword-only. Default:
|
1500.0
|
Notes
The default \(\varepsilon = 10^2\) s⁻¹ is three or more orders of magnitude below the wall shear rates of a driven microbubble (a 1 % oscillation at 1 MHz gives \(\dot{\gamma}_w \approx 2\sqrt{3}\,(2\pi \cdot 10^6)(0.01) \approx 2 \times 10^5\) s⁻¹), so it changes the viscous pressure only near the instants where \(\dot{R}\) reverses. For a 2 µm bubble in a shear-thinning liquid (\(n = 0.7\)) at 100 kPa, the radius differs from the \(\varepsilon \to 0\) solution by less than \(10^{-7} R_0\).
\(\varepsilon\) also sets the viscosity at zero shear rate,
\(K\varepsilon^{\,n-1}\), which plays the part of the finite zero-shear
viscosity of a real shear-thinning liquid. A smaller \(\varepsilon\)
raises it when \(n < 1\), so the right-hand side changes faster where
\(\dot{R}\) reverses and the solver takes more steps there. The smooth
floor keeps the right-hand side differentiable for any
\(\varepsilon > 0\). jbubble 0.1 instead floored the shear rate with a
max, whose kink at the floor made tight-tolerance solves at 300 kPa
fail.
The earlier jbubble convention used the wall velocity gradient \(2|\dot{R}|/R\) as the shear rate, which overestimates the viscous pressure by \(\sqrt{3}^{\,1-n}\) for a \(K\) measured on a rheometer.
References
Kaykanat, S. I., & Uguz, K. (2024). Shape stability of a microbubble in a power-law liquid. The European Physical Journal Special Topics, Eq. 17, with \(M = 2m(2\sqrt{3})^{k-1}\). https://doi.org/10.1140/epjs/s11734-024-01174-7
Equations of motion¶
EquationOfMotion
¶
EquationOfMotion(
gas: GasModel,
shell: ShellModel,
medium: MediumModel,
R0: ArrayLike,
P_amb: ArrayLike,
)
Macroscopic equation of motion for bubble dynamics.
Assembles a GasModel, a
ShellModel, and a
MediumModel into a complete ODE
right-hand side. Concrete subclasses encode different formulations
(Rayleigh-Plesset, Keller-Miksis, and others), which differ in how they
relate the liquid-side boundary pressure \(p_L\) to the radial
acceleration \(\ddot{R}\).
The type parameter StateType binds each equation of motion (EoM) to
the state it operates on. The built-in EoMs use
BubbleState; an EoM that carries
extra degrees of freedom binds a BubbleState subclass, so the type
checker catches a wrong state type before it fails at runtime deep
inside a JAX trace.
The concrete helper p_L
computes the liquid-side boundary pressure:
Because p_L is a regular method on an Equinox module, JAX
differentiates through it automatically. EoMs that need
\(\mathrm{d}p_L/\mathrm{d}t\), such as Keller-Miksis, use
jax.grad(self.p_L) instead of hand-coding analytical derivatives for
every combination of components.
The EoM receives the driving acoustic pressure as a callable p_ac_fn,
so EoMs that need \(\mathrm{d}p_\text{ac}/\mathrm{d}t\) compute it with
jax.grad.
initial_state
seeds the equilibrium configuration, R0 and P_gas0, into the state.
The ODE carries them as frozen constants, with zero time derivatives in
the standard case.
Two hooks tell solve_eom about the state:
is_admissible
marks the states where the right-hand side is defined, and
state_scale gives
the magnitude of each state field, which sets the meaning of the
solver tolerances. Override them in a subclass that adds state fields.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gas
|
GasModel
|
Internal gas pressure model. |
required |
shell
|
ShellModel
|
Shell or coating model. |
required |
medium
|
MediumModel
|
Surrounding medium model. It also holds the liquid density
\(\rho_L\) ( |
required |
R0
|
float or Array
|
Equilibrium bubble radius [m]. |
required |
P_amb
|
float or Array
|
Ambient (far-field) pressure [Pa]. |
required |
p_L
¶
p_L(state: BubbleState) -> Array
Liquid-side boundary pressure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
BubbleState
|
Current bubble state. |
required |
Returns:
| Type | Description |
|---|---|
Array
|
Boundary pressure [Pa]. |
Source code in jbubble/bubble/eom.py
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |
initial_state
¶
initial_state(
*, R: ArrayLike | None = None, R_dot: ArrayLike | None = None
) -> BubbleState
Return an initial state seeded with the equilibrium configuration.
By default the bubble starts at rest at its equilibrium radius. Seeds
R0 and the Laplace-equilibrium gas pressure into the state:
Pass R or R_dot to start away from equilibrium, for example
eom.initial_state(R=1.2 * eom.R0) for a bubble released from rest
at 1.2 times its equilibrium radius. R0 and P_gas0 stay at their
equilibrium values, so the gas pressure at R follows the gas law.
Override this method for coupled systems with a larger state vector. An override must accept the same keyword arguments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
R
|
float or Array
|
Initial radius [m]. |
None
|
R_dot
|
float or Array
|
Initial wall velocity [m/s]. |
None
|
Returns:
| Type | Description |
|---|---|
BubbleState
|
State with |
Source code in jbubble/bubble/eom.py
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | |
is_admissible
¶
is_admissible(state: StateType) -> Array
Return whether the right-hand side is defined at state.
solve_eom never evaluates the equation
of motion at an inadmissible state, so its gradients stay finite
when an adaptive solver tries, and then rejects, a trial step that
leaves the physical domain. The default requires R > 0 and defers
to GasModel.is_admissible,
which also excludes, for example, the van der Waals hard core.
Override this method in a subclass whose state has other fields with
a restricted domain, and combine the result with
super().is_admissible(state).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
StateType
|
State to check. Its leaves may be NaN or infinite. |
required |
Returns:
| Type | Description |
|---|---|
Array
|
Boolean scalar. |
Source code in jbubble/bubble/eom.py
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | |
state_scale
¶
state_scale(state: StateType) -> StateType
Return the characteristic magnitude of each state field.
solve_eom integrates the dimensionless
state state / state_scale(state), so the step-size controller's
rtol and atol mean the same thing for a 50 nm bubble as for a
50 µm one. The default scales are:
RandR0: the equilibrium radiusstate.R0[m].R_dot: the velocity scale \(\sqrt{P_\text{amb}/\rho_L}\) [m/s], with \(\rho_L\) frommedium.rho_L, about 10 m/s in water at atmospheric pressure. It's the natural velocity of bubble dynamics: \(R_0\omega_0 \approx \sqrt{3\kappa P_\text{amb}/\rho_L}\) for the Minnaert frequency \(\omega_0\).P_gas0: its own valuestate.P_gas0[Pa], orP_ambif that value isn't positive.- Any other field of a
BubbleStatesubclass:1, that is, SI units.
Because R0 and P_gas0 are scaled by their own values, the solver
sees them exactly: (x / x) * x == x in floating point.
Override this method to give extra state fields a scale. The scale must be positive and finite; the solver treats it as a constant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
StateType
|
Initial state, with a positive |
required |
Returns:
| Type | Description |
|---|---|
StateType
|
Scale for each field, with the same structure as |
Source code in jbubble/bubble/eom.py
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | |
RayleighPlesset
¶
RayleighPlesset(
gas: GasModel,
shell: ShellModel,
medium: MediumModel,
R0: ArrayLike,
P_amb: ArrayLike,
)
Bases: EquationOfMotion
Rayleigh-Plesset equation of motion (incompressible liquid).
The simplest bubble dynamics EoM, which assumes an incompressible
surrounding liquid. It takes only the parameters of
EquationOfMotion, and reads
\(\rho_L\) from medium.rho_L.
ModifiedRayleighPlesset
¶
ModifiedRayleighPlesset(
gas: GasModel,
shell: ShellModel,
medium: MediumModel,
R0: ArrayLike,
P_amb: ArrayLike,
)
Bases: EquationOfMotion
Modified Rayleigh-Plesset equation with gas radiation damping.
Adds a first-order compressibility correction to the gas pressure term only, as used by Marmottant et al. (2005):
where autodiff computes \(\mathrm{d}p_\text{gas}/\mathrm{d}t = (\partial p_\text{gas}/\partial R)\,\dot{R}\).
It takes only the parameters of
EquationOfMotion, and reads
\(\rho_L\) and \(c_L\) from medium.rho_L and medium.c_L.
KellerMiksis
¶
KellerMiksis(
gas: GasModel,
shell: ShellModel,
medium: MediumModel,
R0: ArrayLike,
P_amb: ArrayLike,
)
Bases: EquationOfMotion
Keller-Miksis equation of motion (first-order compressibility).
Accounts for liquid compressibility up to first order in the Mach number \(M = \dot{R}/c_L\):
JAX autodiff computes the time derivative
\(\mathrm{d}p_L/\mathrm{d}t\) automatically, through the chain rule
on p_L:
The \(\ddot{R}\) term moves to the left-hand side, so this EoM works with any combination of gas, shell, and medium models without hand-coded derivatives.
It takes only the parameters of
EquationOfMotion, and reads
\(\rho_L\) and \(c_L\) from medium.rho_L and medium.c_L.